Search code examples
c#asp.netfastmember

Copy properties recursively with FastMember


I have two objects from this class:

public class A
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<B> Bs { get; set; }
}

public class B
{
    public string Description { get; set; }
    public IList<C> Cs { get; set; }
}

public class C
{
    public string Description { get; set; }
}

Now I want to loop through all the properties of the two object instances with FastMember and copy the properties from object 1 to object 2.

Here's what I got so far (not working!)

// Set accessors
var sourceAccessor = ObjectAccessor.Create(object1);
var targetAccessor = ObjectAccessor.Create(object2);

foreach (var sourceItem in (IList)sourceAccessor.Target)
{
    var targetItemAccessor = ObjectAccessor.Create(targetAccessor.Target);
    var sourceItemAccessor = ObjectAccessor.Create(sourceItem);

    // Overwrite property
    targetItemAccessor[p] = sourceItemAccessor[p];
}

this code is obviously not working...any ideas? Thanks!


Solution

  • You can create a shallow copy with the help of the TypeAccessor using something like:

    var sourceAccessor = ObjectAccessor.Create(object1);
    var targetAccessor = ObjectAccessor.Create(object2);
    
    var access = TypeAccessor.Create(typeof(A));
    var members = access.GetMembers();
    
    foreach (var member in members)
    {
        targetAccessor[member.Name] = sourceAccessor[member.Name];
    }
    

    Have a look at the FastMember tests for usage examples.

    For a deep copy, you could check either the property name or use the property type to determine whether to apply a deep copy of lists or if a shallow copy is sufficient.

    Edit: Here is an example for a simplified version that recursively copies properties of type IList (see this question on how to determine which property is a list), assuming the source and target object's lists have the same length:

    private static void MirrorObject(object object1, object object2)
    {
        var sourceAccessor = ObjectAccessor.Create(object1);
        var targetAccessor = ObjectAccessor.Create(object2);
    
        var access = TypeAccessor.Create(object1.GetType());
        var members = access.GetMembers();
    
        foreach (var member in members)
        {
            if (member.Type.IsGenericType && (member.Type.GetGenericTypeDefinition() == typeof(IList<>)))
            {
                var list1 = (IList)sourceAccessor[member.Name];
                var list2 = (IList)targetAccessor[member.Name];
                if (list1.Count != list2.Count)
                {
                    throw new ArgumentException("Lists need to be of the same length.");
                }
                for (var i = 0; i < list1.Count; ++i)
                {
                    MirrorObject(list1[i], list2[i]);
                }
            }
            else
            {
                targetAccessor[member.Name] = sourceAccessor[member.Name];
            }
        }
    }