jeudi 2 août 2018

How to use reflection to copy complex classes with collections to other classes and different proper names

Our legacy database has some bad naming practices. In creating our DateModels, we’ve got rid of a lot of the prefixes and extensions.

What we’re working on is a way to take the EF entities we get back from the database and using reflection copy the property values over into our DataModels.

For a simple class, we have everything work. The issue we haven’t figured out just yet is how to handle collections.

A sample of our sql tables would be.

Customer Table

Cust_Id
Cust_Name
Cust_ProductId -  FK to Product.Id

Product Table
Product_Id
Product_Name

Then our data models would be

    public class CustomerModel : BaseCustomerModel
        {
            [SLSqlTable("Cust_Id")]
            public int Id { get; set; }

            [SLSqlTable("Cust_Name")]
            public string Name { get; set; }

            [SLSqlTable("Cust_ProductId")]
            public string ProductId { get; set; }

            [SLSqlTable("Products")]
            public IList<BaseProduct> Products { get; set; }
        }

        public class BaseProductModel 
        {
            [SLSqlTable("Product_Id")]        
            public int? Id { get; set; }

            [SLSqlTable("Product_Name")]
            public string Name { get; set; }
        }

We’re using the SLSqlTableAttribute we created to map the names.

Then from the internet, we’re using the following code to copy the data between properties. It works fine for everything except our collections right now and that’s what we’re trying to figure out. We were think, we detect a collection and then some how just recursively call back into CopyToModelProperties.

  /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static T CopyToModelProperties<T>(this object source) where T : class, new()
    {
        T destination = new T();


        Type typeDest = destination.GetType();
        Type typeSrc = source.GetType();

        // Collect all the valid properties to map
        var results = from srcProp in typeSrc.GetProperties()
                      let targetProperty = typeDest.GetProperty(GetModelPropertyNameFromSqlAttribute(typeDest, srcProp.Name))
                      where srcProp.CanRead
                      && targetProperty != null
                      && (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
                      && (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
                      select new { sourceProperty = srcProp, targetProperty = targetProperty };

        //map the properties
        foreach (var props in results)
        {

            if (props.targetProperty.PropertyType.IsGenericType)
            {       
                var _targetType = props.targetProperty.PropertyType.GetGenericArguments()[0];


            //    props.sourceProperty.PropertyType.MakeGenericType(_targetType).CopyToModelProperties((dynamic)List<>);
            }
            else
            {
                props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
            }
        }

        return destination;
    }

    public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
    {
        string _ret = "";

        var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();

        if(_property != null)
        {
            _ret = _property.Name;
        }

        return _ret;
    }

Here is a sample of code getting customers with all their products.

using (var _dc = new BulkTestEntities())
                    {
                        var _custs = _dc.Customers.Include("Products").ToList();



                        foreach(var cust in _custs)
                        {
                            _resp.Add(cust.CopyToModelProperties<CustomerModel>());
                        }
                    }

This works fine and the if condition checking IsGenericType works, we just need to figure out what code goes here that handle the collection of products when getting customer back.

We thought it would be a recursive call back to CopyToModelProperties because Products could have a collection inside it as well and that could have a collection so we don’t want to hard code levels.

So the question is how to take props.sourceProperty from the if condition above and copy a collection of SQL Entities over to a collection of DataModels?





Aucun commentaire:

Enregistrer un commentaire