0

Code

public class Test1
{
    [Conversion("UserId")]
    Public int id { get; set; }

    [Conversion("UserValue")]
    public int value { get; set; }

    [Conversion("UserInfo")]
    public List<Test2> info { get; set; }
}
public class User
{
    public int UserId { get; set; }
    public int UserValue { get; set; }      
    public List<UserInformation> UserInfo { get; set; }
}

public class Test2
{
    [Conversion("UserId")]
    public int id { get; set; }

    [Conversion("UserName")]
    public string name { get; set; }

    [Conversion("UserLocation")]
    public string location { get; set; }
}

public class UserInformation
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string UserLocation { get; set; }
}

public class ConversionAttribute    
{
    public string Name { get; set; }
    public ConversionAttribute(string name)
    {
        this.Name = name;
    }
}

public dynamic Convert(dynamic source, Type result)
{
    var p = Activator.CreateInstance(result);
    foreach (var item in source.GetType().GetProperties().Where(m => m.GetCustomAttributes(typeof(ConversionAttribute)).Count() > 0))
    {
        p.GetType().GetProperty(item.GetCustomAttributes(typeof(ConversionAttribute)).Cast<ConversionAttribute>().Name).Value = item.GetValue(source,null); // This should work for system types... but not for lists / custom models.          
    }       
}

public void DoShit()
{
    var t1 = new Test1 { id = 1, name = value = "Test", info = new Test2 { id = 1, name = "MyName", location = "UserLocation" } };
    var obj = Convert(t1, typeof(User));
}

Situation

I've been tasked to transform our database model to our WCF model. They are different in several ways which I've shown a little bit in the example code above.

I've managed to create instances using Activator.CreateInstance(result) and I can use source.GetType().GetProperties() to copy all the properties, but I seem to have a problem when it comes to a Model (custom class) or a list.

Problem

I have no idea how to handle custom classes or lists (both of system type aswell as custom classes).

In my old method I used two methods, one for a normal conversion and one for a list conversion.. but my boss didn't approve it so I'd like to know if it is possible to have one method for every use without checking wether the type is a list, a custom class or a system type

The main reason this has to be fully generic and by use of attributes is because we plan to use this method in several projects aswell as over more then 100 models.

4

1 回答 1

0
      Here is something that I tried. It works albeit it's a little slow on lager object graphs. One could use expression trees           which are harder to get but give a really impressive performance.



      'private static IEnumerable<Tuple<PropertyInfo, PropertyInfo>> MapProperties(Type source, Type target)
        {
            var targetProperies = target.GetProperties();

            foreach (var property in source.GetProperties())
            {
                var conversionAttribute =
                    property.GetCustomAttributes(typeof (ConvertAttribute), false).FirstOrDefault() as
                    ConvertAttribute;

                if (conversionAttribute == null)
                    throw new InvalidOperationException(
                        String.Format("Source property {0} doesn't have ConvertAttribute defined", property.Name));

                var targetProperty = targetProperies.FirstOrDefault(p => p.Name == conversionAttribute.Name);


                if (targetProperty == null)
                    throw new InvalidOperationException(String.Format(
                        "Target type doesn't have {0} public property", conversionAttribute.Name));

                yield return Tuple.Create(targetProperty, property);
            }
        }

        public static bool IsIList(Type type)
        {
            return type.GetInterface("System.Collections.Generic.IList`1") != null;
        }


        private static object Convert(object source, Type resaultType)
        {
            var resault = Activator.CreateInstance(resaultType);

            var sourceType = source.GetType();


            if (IsIList(resaultType) && IsIList(sourceType))
            {
                var sourceCollection = source as IList;

                var targetCollection = resault as IList;

                var argument = resaultType.GetGenericArguments()[0];

                if (argument.IsAssignableFrom(sourceType.GetGenericArguments()[0]))
                {
                    foreach (var item in sourceCollection)
                    {
                        targetCollection.Add(item);
                    }
                }
                else
                {
                    foreach (var item in sourceCollection)
                    {
                        targetCollection.Add(Convert(item, argument));
                    }
                }
            }
            else
            {
                foreach (var map in MapProperties(sourceType, resaultType))
                {
                    if (map.Item1.PropertyType.IsAssignableFrom(map.Item2.PropertyType))
                    {
                        map.Item1.SetValue(resault, map.Item2.GetValue(source, null), null);
                    }
                    else
                    {
                        map.Item1.SetValue(resault,
                                           Convert(map.Item2.GetValue(source, null), map.Item1.PropertyType), null);
                    }
                }
            }
            return resault;
        }'
于 2013-02-22T11:09:50.617 回答