2
\$\begingroup\$

I am trying to create an extension method that builds a POCO object (copies all the fields) for an Entity Object.

When Entity object is simple (no navigation, no sub collections), it works fine. I want to improve it so it could also deal with sub-entities.

For this example I take Nortwind database and use Customer and Order entities. My POCOs:

 public class CustomerPOCO : BaseDataItemModel
    {
        public string CustomerID { get; set; }
        public string CompanyName { get; set; }
        public string ContactName { get; set; }

        List<OrderPOCO> orders;

       [PropertySubCollection]
        public List<OrderPOCO> Orders
        {
            get { return orders; }
            set { orders = value; }
        }


        public CustomerPOCO()
        {
            orders = new List<OrderPOCO>();
        }

    }


public class OrderPOCO : BaseDataItemModel
{

   public int OrderID { get; set; }
   public string ShipName { get; set; }
   public string ShipCity { get; set; }

}

Extension Method:

 public static void CopyPropertiesFrom<TObjectTarget>(
           this ICollection<TObjectTarget> targetitems, System.Collections.IEnumerable sourceitems)
            where TObjectTarget : new()
        {
            foreach (var source in sourceitems)
            {

                TObjectTarget targetObject = new TObjectTarget();

                PropertyInfo[] allProporties = source.GetType().GetProperties();

                PropertyInfo targetProperty;
                foreach (PropertyInfo fromProp in allProporties)
                {
                    targetProperty = targetObject.GetType().GetProperty(fromProp.Name);



                    if (targetProperty == null) continue;
                    if (!targetProperty.CanWrite) continue;

                    //check if property in target class marked with SkipProperty Attribute
                    if (targetProperty.GetCustomAttributes(typeof(SkipPropertyAttribute), true).Length != 0) continue;


                    //Sub Collection -> set it here
                    if (targetProperty.GetCustomAttributes(typeof(PropertySubCollection), true).Length != 0)
                    {

                    }
                    else

                        targetProperty.SetValue(targetObject, fromProp.GetValue(source, null), null);


                }

                targetitems.Add(targetObject);

            }
        }

static void Main(string[] args)
 {
      List<CustomerPOCO> customers;
      using (NorthwindEntities northcontext = new NorthwindEntities())
      {
          var cus = from customer in northcontext.Customers
                  select customer;

          customers = new List<CustomerPOCO>();
          customers.CopyPropertiesFrom(cus);              

      }
   }

Note: Extension should know the type of sub-entity and a type of sub-poco...

\$\endgroup\$

migrated from stackoverflow.com Aug 5 '11 at 13:44

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ Well for better speed I would cache the PropertyInfo[], so you don't have to use reflection everytime, this will speed up your application a lot, if you are calling this extension often. \$\endgroup\$ – Jethro Aug 4 '11 at 8:22
  • \$\begingroup\$ @George Duckett, well I mean add a new feature... \$\endgroup\$ – offer Aug 4 '11 at 8:24
  • \$\begingroup\$ Ahh ok, you're probably right. (deleted my previous comment) \$\endgroup\$ – George Duckett Aug 4 '11 at 8:25
  • \$\begingroup\$ What would speed it up more would be to construct a lamda expression that does the copying. You could then compile the expression so the analysis is only done once. \$\endgroup\$ – Bob Vale Aug 4 '11 at 8:25
  • \$\begingroup\$ Bob Vale, can you please show what you mean? \$\endgroup\$ – offer Aug 4 '11 at 8:31
3
\$\begingroup\$

For this task, i would use AutoMapper, it does exactly what you need, and can work with complex entities.

If you are writing this mapper yourself, consider using expression compilation feature. Such code will execute at the speed of a custom written code, with all reflection interpretation done beforehand. See the code example for Expression.MemberInit

As for the sub-objects in your code, you will figure out the recursive procedure, as soon as you rewrite your extension to work with a Type argument, not a generic argument. This way you can easily call the procedure for the properties, knowing their type from PropertyInfo.

\$\endgroup\$
  • \$\begingroup\$ What do you mean not a generic argumen? CopyPropertiesFrom<TObjectTarget> ? \$\endgroup\$ – offer Aug 4 '11 at 10:47
  • \$\begingroup\$ I think he meant something like CopyPropertiesFrom(Type objectTargetType). You can't call generic methods with computed type parameter without using recursion. \$\endgroup\$ – svick Aug 9 '11 at 19:53
  • \$\begingroup\$ Completely agree with this solution ... there's already a great tool built for exactly this ... might as well use it, it's pretty lightweight too! \$\endgroup\$ – War Feb 4 '16 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy