For a given Entity Framework data set, I have written several adapters to import data into my entity models. Rather than manually write mapping code for each entity model, I tried for a generalized approach using reflection.My requirements are: The updater must create the entity if it does not exist, or update the existing entity with any changed properties or relationships.External data are often subsets; therefore, do not delete entities if they are not in external dataIf external data reference other entities which do not exist, eg tags, the updater must create them and ensure the references are still valid, while respecting key constraints.My entities implement an interface, IEntity:public interface IEntity{    int Id { get; set; }    T CreateEmpty<T>() where T:class,IEntity;    bool EntityEquals(IEntity entity);}EntityEquals compares entities based on their natural keys. An implementation might look like:public class Tag : IEntity{    public int Id { get; set; }    // Name is a natural key    public string Name { get; set; }    public T CreateEmpty<T>() where T : class, IEntity    {        return new Tag() as T;    }    public bool EntityEquals(IEntity entity)    {        var e = entity as Tag;        return e != null && Name==e.Name;    }}I invoke this likeEntityDataUpdater.Update(context, externalEntitiesList);And here's the implementation. Comments and feedback greatly appreciated!using System;using System.Collections;using System.Collections.Generic;using System.Data;using System.Data.Entity;using System.Linq;using Project.Models;namespace Project.Models.Adapters{    public static class EntityDataUpdater    {        public static void Update(DbContext context, IEnumerable<IEntity> entities)        {            if (!entities.Any()) return;            var set = context.Set(entities.First().GetType());            set.Load(); // prefetch data to memory            foreach (var entity in entities)            {                var tEntity = entity.ImportOrUpdate(context);                // if newly imported, add it                if (tEntity.Id == 0) set.Add(tEntity);                // otherwise, indicate that it is updated                else context.Entry(tEntity).State = EntityState.Modified;            }            context.SaveChanges();        }    }    static class EntityCopyExtensions    {        private static bool TryFindIn<T>(this T entity, DbContext context, out T o) where T : class, IEntity        {            var set = context.Set(entity.GetType());            o = set.Local.Cast<IEntity>()                .FirstOrDefault(item => item.EntityEquals(entity)) as T // check local first                ?? ((IEnumerable)set).Cast<IEntity>()                .FirstOrDefault(item => item.EntityEquals(entity)) as T; // then check DB            return o != null;        }        private static IEntity FindOrCreateIn(this IEntity entityIn, DbContext context)        {            // if item exists, return it; otherwise create it            IEntity entityOut;            return !entityIn.TryFindIn(context, out entityOut) ? entityIn.ImportOrUpdate(context) : entityOut;        }        public static T ImportOrUpdate<T>(this T entityIn, DbContext context) where T : class, IEntity        {            var entityT = entityIn.GetType();            T entityOut;            if(!entityIn.TryFindIn(context, out entityOut))            {                entityOut = entityIn.CreateEmpty<T>();            }            // copy properties            foreach (var property in entityT.GetProperties()                        .Where(p => p.Name != Id &&                            p.CanWrite && (                            p.PropertyType == typeof(string) ||                            p.PropertyType == typeof(int) ||                            p.PropertyType == typeof(DateTime)                        ))){                var value = property.GetValue(entityIn, null);                if (value != null) property.SetValue(entityOut, value, null);            }            // copy direct references            foreach (var reference in entityT.GetProperties()                        .Where(r => r.PropertyType.IsImplementationOf(typeof(IEntity)))                    ){                var origValue = (IEntity) reference.GetValue(entityIn, null);                reference.SetValue(entityOut,origValue.FindOrCreateIn(context),null);            }            // copy collections of references            foreach (var reference in entityT.GetProperties()                .Where(r=>                    r.PropertyType.IsImplementationOf(typeof(IEnumerable)) &&                     r.PropertyType.IsGenericType &&                    r.PropertyType.GetGenericArguments().First().IsImplementationOf(typeof(IEntity))                )){                var itemsIn = (IEnumerable) (reference.GetValue(entityIn, null));                // itemsOut begins with all items from entityOut                var itemsOut = ((IEnumerable) (reference.GetValue(entityOut, null))).Cast<IEntity>().ToList();                // then adds any new items from entityIn                foreach (IEntity item in itemsIn)                {                    var itemOut = item.FindOrCreateIn(context);                    //only add new items                    if (!itemsOut.Any(i => i.Id == itemOut.Id) || item.Id==0)                    {                        itemsOut.Add(item.FindOrCreateIn(context));                    }                }                reference.SetValue(entityOut,itemsOut.CastTo(reference.PropertyType),null);            }            return entityOut;        }        /// <summary>        /// Cast each element in <paramref name=list/> to type of the first object,        /// or null        /// </summary>        private static IEnumerable CastTo(this IList list, Type targetT)        {            if (!(targetT.IsImplementationOf(typeof(IEnumerable)) && targetT.IsGenericType))                 throw new ArgumentException(targetT must be an IEnumerable of T.);            var itemT = targetT.GetGenericArguments().First();            var ret = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(new[] {itemT}));            foreach (var i in list)                ret.Add(i);            return ret;        }        /// <summary>        /// Returns true if type <paramref name=t/> implements interface <paramref name=i/>        /// </summary>        private static bool IsImplementationOf(this Type t, Type i)        {            if (!i.IsInterface) throw new ApplicationException(Method must be of type Interface);            return t.GetInterfaces().Contains(i);        }        /// <summary>        /// Returns true if type <paramref name=t/> implements interface <paramref name=i/>        /// </summary>        private static bool IsImplementationOf(this Type t, string i)        {            if (!Type.GetType(i, true).IsInterface) throw new ApplicationException(Method must be of type Interface);            return t.GetInterface(i) != null;        }        }}
Entity Framework Code First Data Updater
c#;entity framework