mercredi 29 juillet 2015

Comparing two objects and setting them equal without breaking entity reference

So, here's one you may have done thought experiments with but never tried. Back Story: We are using entity framework with a repository that utilizes a few generic classes. We have our entity table classes and model classes that are very similar to the table classes. I had always done each table object individually until a co-worker started a project and used the navigation properties. Naturally he wondered why he couldn't update his objects. The short is that the navigation properties were being directly set to the model's properties and therefore the link was being broken. It MAY be beneficial to have one generic class to run the comparison as we have 14 tables in this project alone... Here is my second attempt (which took over 6 hours) to remedy the situation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Domain
{
public static class Compare2
{
    /// <summary>
    /// Takes two objects and without breaking the reference sets the second     object equal to the first, including lists within that object.
    /// It does this using reflection and an attribute called PKey which is placed on the property that is the identity.
    /// I don't think that it will propperly pull the properties from the objects in the lists because it sees them as objects. 
    /// It will likely be slow because it uses reflection.
    /// </summary>
    /// <typeparam name="Model">The type of the modified object</typeparam>
    /// <typeparam name="Table">The type of the object to be modified</typeparam>
    /// <param name="model">The modified object</param>
    /// <param name="table">The object to be modified</param>
    public static void obj2obj<Model, Table>(Model model, ref Table table)
    {
        var m = model.GetType();
        var t = table.GetType();
        var propList = m.GetProperties().AsEnumerable().ToList();
        foreach (var prop in propList)
        {
            //if property exists in both
            if (t.GetProperty(prop.Name) != null)
            {
                //if this property isn't a collection
                if (!typeof(ICollection<>).IsAssignableFrom(prop.GetType()))//.GetGenericTypeDefinition()))
                {

                    //if it's not a seperate object
                    if (!t.GetProperty(prop.Name).GetType().IsValueType && t.GetProperty(prop.Name).GetType() != typeof(string))
                    {
                        //set the corresponding property in the table version to the value of that in the model
                        t.GetProperty(prop.Name).SetValue(table, m.GetProperty(prop.Name).GetValue(model, null));
                    }
                    else //if it IS another object
                    {
                        //send it off (won't work because we're breaking the reference)
                        object o2 = t.GetProperty(prop.Name).GetValue(table);
                        obj2obj<object, object>(m.GetProperty(prop.Name).GetValue(model), ref o2);
                    }
                }
                else //if it IS a collection
                {
                    //for every object in the collection
                    foreach (var o in (System.Collections.Generic.IEnumerable<object>)m.GetProperty(prop.Name).GetValue(model, null))
                    {
                        //match up objects with the same Primary keys and send them off to be compared 
                        //(won't work because we're breaking the reference)
                        object o2 = ((List<object>)t.GetProperty(prop.Name).GetValue(table, null))
                            .Where(i => i.GetType().GetProperties().Where(pi => Attribute.IsDefined(pi, typeof(AuthSVC.Data.PKey))).First().GetValue(i)
                                == o.GetType().GetProperties().Where(pi => Attribute.IsDefined(pi, typeof(AuthSVC.Data.PKey))).First().GetValue(o)).First();
                        //i would like to have a catch to simply add new objects... this doesn't account for that
                        obj2obj<object, object>(o, ref o2);
                    }
                }
            }
        }
    }
}
}

I'm sure you can see my dilemma. I can't pass properties by ref and getting their values as objects is breaking my reference to entity... 1. Is there an easy (built-in) way to do this? 2. If not, should I try for an overloaded version which accepts the get and set functions as delegates?





Aucun commentaire:

Enregistrer un commentaire