10
\$\begingroup\$

I'm currently writting piece of logic which copies entities from one user account to another. My current strategy in doing this is like that: Consider the following code:

 public class MobileOrderSettings:ICloneable
{
 public long Id { get; set; }
 public bool allowCash { get; set; }
 public int deliveryPrice { get; set; }
 public int deliveryTreshold { get; set; }
 public bool orderingEnabled { get; set; }
 public bool paymentsEnabled { get; set; }
 public int shippingType { get; set; }
 public virtual MobileApp MobileApp { get; set; }
 public object Clone()
 {
 var copy = CopyUtils.ShallowCopyEntity(this);
 copy.Id = default(int);
 copy.MobileApp = null;
 return copy;
 }
}

The ShallowCopyEntity method defined like in this way:

 public static TEntity ShallowCopyEntity<TEntity>(TEntity source) where TEntity : class, new()
 {
 // Get properties from EF that are read/write and not marked witht he NotMappedAttribute
 var sourceProperties = typeof(TEntity)
 .GetProperties()
 .Where(p => p.CanRead && p.CanWrite &&
 p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute), true).Length == 0);
 var notVirtualProperties = sourceProperties.Where(p => !p.GetGetMethod().IsVirtual);
 var newObj = new TEntity();
 foreach (var property in notVirtualProperties)
 {
 // Copy value
 property.SetValue(newObj, property.GetValue(source, null), null);
 }
 return newObj;
 }

So, as you can see, I firstly copy all fields which is not virtual, reassing Id value and then perform copy of the dependent object(collection)(In this particular situation MobileOrderSettings depends on MobileApp entity, so I make MobileApp null, and in MobileApp Clone menthod I assign MobileOrderSettings virtual field the copy of MobileOrderSettings). Is this approach is good, or you can suggest any better solution?

Marc-Andre
6,7795 gold badges39 silver badges65 bronze badges
asked Apr 8, 2016 at 10:51
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Would .AsNoTracking() work for your scenario? \$\endgroup\$ Commented Jun 27, 2018 at 8:28

1 Answer 1

9
\$\begingroup\$

Two possible sources of errors:

  1. If you forget to use (or don't want to apply) the virtual modifier, it will no longer be a shallow copy (because virtual is typically used for navigation properties).
  2. It relies on unmapping properties by attributes. If at any time in the future you start using the fluent API for that purpose you'll change behavior in an unexpected place.

You evidently want to clone mapped and scalar properties only. Your approach works (if you remember the error sources), but I'd prefer to use a method provided by EF itself:

MobileOrderSettings settings = context.MobileOrderSettings.FirstOrDefault();
var shallowCopy = (MobileOrderSettings)context.Entry(settings)
 .CurrentValues.ToObject()

As you see, the CurrentValues property of an entry in EF's change tracker is used to build a clone, which is always a shallow clone of mapped properties.

Of course, you can't apply this method inside an entity class, because it requires a context to which the entity object is attached. But is that bad? I'd say no, because this clone method has a very specific purpose which is tied to an EF environment. To me it would break the persistence ignorance principle if a class would require knowledge of the data layer implementation for which it clones itself.

answered Apr 8, 2016 at 13:19
\$\endgroup\$
2
  • \$\begingroup\$ PropertyValues (what CurrentValues returns) has both ToObject and Clone methods, but the docs don't really distinguish them -- any reason why you'd choose one over the other? \$\endgroup\$ Commented Nov 29, 2018 at 16:13
  • \$\begingroup\$ Never mind - Clone returns another PropertyValues, ToObject returns an entity type. Overthinking it. \$\endgroup\$ Commented Nov 29, 2018 at 16:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.