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...
-
\$\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– Jethro2011年08月04日 08:22:32 +00:00Commented Aug 4, 2011 at 8:22
-
\$\begingroup\$ @George Duckett, well I mean add a new feature... \$\endgroup\$offer– offer2011年08月04日 08:24:36 +00:00Commented Aug 4, 2011 at 8:24
-
\$\begingroup\$ Ahh ok, you're probably right. (deleted my previous comment) \$\endgroup\$George Duckett– George Duckett2011年08月04日 08:25:50 +00:00Commented Aug 4, 2011 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– Bob Vale2011年08月04日 08:25:52 +00:00Commented Aug 4, 2011 at 8:25
-
\$\begingroup\$ Bob Vale, can you please show what you mean? \$\endgroup\$offer– offer2011年08月04日 08:31:21 +00:00Commented Aug 4, 2011 at 8:31
1 Answer 1
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.
-
\$\begingroup\$ What do you mean not a generic argumen?
CopyPropertiesFrom<TObjectTarget>
? \$\endgroup\$offer– offer2011年08月04日 10:47:49 +00:00Commented Aug 4, 2011 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– svick2011年08月09日 19:53:44 +00:00Commented Aug 9, 2011 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– War2016年02月04日 16:26:28 +00:00Commented Feb 4, 2016 at 16:26