vendredi 5 juin 2015

Setting property values on EF object (using reflection)

I used Automapper but it was not giving me the full functionality I wanted so I decided to make my custom "ApplyValuesByExcluding" between 2 objects (they even may be from different Classes).

Basically my code is almost done... near but not exactly at 100%.

So I decided to ask you guys some help.

Example:

  • Using EF I have Class Father and Class Son, each one with several properties (string, int, enum, etc..)
  • In my class Father I have a list of Sons
  • I have a MVC form that submits/returns a full object Father with Son entries ==> object obj
  • Somewhere in the code I am loading the actual father from the DB (including the sons) -> object original
  • variable parameters represents a string array with the name of the "child" property name, in this case "Sons" (but it could be more...)
  • I also specify some properties to NOT set the value "Id", "RowVersion", "CreatedOn"

Then I use the following line of code:

MyConversion.ApplyValuesByExcluding(obj, original, parameters, "Id", "RowVersion", "CreatedOn");`

After this I am calling db.SaveChanges(); in a lower layer of the Repository.

At this point I am able to set correctly all the existing properties both from the Father as well of the children....

The problem is:

My reflection code is not affecting the Entity Framework in order to add or remove "Sons"

Here is my code:

public static class MyConversion
    {
        public static void ApplyValuesByExcluding(object source, object target, object parameters, params string[] excludedpropertyNames)
        {
            var typeofT = target.GetType();
            var typeofS = source.GetType();

            var allProperties = typeofT
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.CanWrite);

            string[] childProperties = null;
            if (parameters != null)
            {
                var valueType = parameters.GetType();
                if (valueType.IsArray && (typeof(string)).IsAssignableFrom(valueType.GetElementType()))
                {
                    childProperties = parameters as string[];
                }
            }

            foreach (var propT in allProperties)
            {
                var propS = typeofS.GetProperty(propT.Name);

                var ptT = propT.PropertyType;
                var ptS = propS.PropertyType;

                if (propS == null || ptT != ptS) continue; // properties do NOT match... 

                if (excludedpropertyNames.Contains(propT.Name)) continue;

                if (ptT.IsPrimitive
                    || ptT == typeof(Decimal)
                    || ptT == typeof(Nullable<Decimal>)
                    || ptT == typeof(Nullable<long>)
                    || ptT == typeof(Nullable<int>)
                    || ptT == typeof(Nullable<bool>)
                    || ptT == typeof(String))
                {
                    propT.SetValue(target, propS.GetValue(source));
                }
                else
                {
                    var u = Nullable.GetUnderlyingType(ptT);
                    if (u != null && u.IsEnum) // Process enum properties
                    {
                        propT.SetValue(target, propS.GetValue(source));
                    }
                    else
                    {
                        // Process child properties
                        if (childProperties != null && ptT.IsGenericType && childProperties.Contains(propT.Name))
                        {
                            var sourceListRaw = propS.GetValue(source);
                            var targetListRaw = propT.GetValue(target);

                            if (sourceListRaw == null || targetListRaw == null) continue; //Something is not right: child list wasn't loaded???

                            var sourceList = ((IEnumerable<dynamic>)sourceListRaw).Select(x => x).ToList();
                            var targetList = ((IEnumerable<dynamic>)targetListRaw).Select(x => x).ToList();

                            var baseListTypeT = ptT.GetGenericArguments()[0];
                            var propertyIdT = baseListTypeT.GetProperty("Id");

                            var baseListTypeS = ptS.GetGenericArguments()[0];
                            var propertyIdS = baseListTypeS.GetProperty("Id");

                            var sourceListIds = sourceList.Select(x => (long)propertyIdS.GetValue(x)).ToArray();
                            var targetListIds = targetList.Select(x => (long)propertyIdT.GetValue(x)).ToArray();

                            var idsToRemove = targetListIds.Where(x => !sourceListIds.Contains(x)).ToArray();
                            targetList.RemoveAll(x => idsToRemove.Contains((long)propertyIdT.GetValue(x))); // NOT WORKING...

                            for (var pos = 0; pos < targetList.Count; pos++)
                            {
                                var element = targetList[pos];
                                var elementId = (long)propertyIdT.GetValue(element);

                                var sourceElement = sourceList.Where(x => (long)propertyIdS.GetValue(x) == elementId).FirstOrDefault();
                                if (sourceElement != null)
                                {
                                    MyConversion.ApplyValuesByExcluding(sourceElement, element, null, excludedpropertyNames); // update element
                                }
                            }
                            // Add new ones (Id == 0)
                            targetList.AddRange(sourceList.Where(x => (long)propertyIdS.GetValue(x) == 0)); // NOT WORKING...                           
                        }
                    }
                }
            }
        }
    }

I would appreciate the help. Thanks





Aucun commentaire:

Enregistrer un commentaire