EntityFramework更新Collection的拓展

EntityFramework更新Collection不能够简单的进行赋值运算,因为EntityFramework需要跟踪更新操作。

为了让代码更简洁和代码复用,需要对EntityFramework Collection进行拓展。

下面是拓展代码:

 1 // Copyright (c) 雾里看花 Q397386036
 2 public static class CollectionExtensions
 3     {
 4      public static void ToDb<Tsource, Ttarget>(
 5             this ICollection<Tsource> tsource,
 6             ICollection<Ttarget> ttarget, Func<Tsource, object> outKeySelector, Func<Ttarget, object> innerKeySelector)
 7             where Tsource:class, new()
 8             where Ttarget:class, new()
 9         {
10             if (tsource == null)
11                 tsource = new HashSet<Tsource>();
12             if (ttarget == null)
13                 throw new ArgumentNullException();
14 
15             var intersectSources = (from source in tsource.AsQueryable()
16                     join target in ttarget.AsQueryable() on outKeySelector(source) equals innerKeySelector(target) 17                     select source).ToList();
18             var intersectTargets = (from source in tsource.AsQueryable()
19                     join target in ttarget.AsQueryable() on outKeySelector(source) equals innerKeySelector(target) 20                     select target).ToList();
21 
22             //更新
23             foreach (Tsource sourceItem in intersectSources)
24             {
25                 var targetItem = (from source in tsource.AsQueryable()
26                     join target in ttarget.AsQueryable() on outKeySelector(source) equals innerKeySelector(target) 27                     where source.Equals(sourceItem)
28                     select target).FirstOrDefault();
29                 if (targetItem == null) continue;
30                 Mapper.Map<Tsource, Ttarget>(sourceItem, targetItem);
31             }
32 
33             //删除
34             var removeTargets = (from target in ttarget.AsQueryable()
35                 where !intersectTargets.Any(t=>t.Equals(target))
36                 select target).ToList();
37             foreach (var targetItem in removeTargets)
38             {
39                 ttarget.Remove(targetItem);
40             }
41 
42             //添加
43             var addSources = (from source in tsource.AsQueryable()
44                 where !intersectSources.Any(t => t.Equals(source))
45                 select source).ToList();
46             foreach (var sourceItem in addSources)
47             {
48                 ttarget.Add(Mapper.Map<Tsource, Ttarget>(sourceItem));
49             }
50 
51         }
52 }
从一个Collection更新另一个Collection(ttarget EF Collection),我们发现其实俩个基类从没有任何关系,他通过Mapper.Map进行映射。他们更新又靠 内联关联起来,红色部分所标记。
Mapper.Map使用的ValueInjecter对象转换,Mapper的代码如下(一时找不到从哪下,就直接贴出来了):
View Code
  1 /// <summary>
  2     /// DtoMapper
  3     /// </summary>
  4     public static class Mapper
  5     {
  6         //map source to an existing target
  7         public static TTarget Map<TSource, TTarget>(TSource source, TTarget target)
  8         {
  9             target = MapperFactory.GetMapper<TSource, TTarget>().Map(source, target);
 10             return target;
 11         }
 12 
 13         //create a new target and map source on it 
 14         public static TTarget Map<TSource, TTarget>(TSource source)
 15         {
 16             var target = (TTarget)Creator.Create(typeof(TTarget));
 17             return MapperFactory.GetMapper<TSource, TTarget>().Map(source, target);
 18         }
 19 
 20         public static object Map(object source, object target, Type sourceType, Type targetType)
 21         {
 22             target = target ?? Creator.Create(targetType);
 23             var getMapper = typeof(MapperFactory).GetMethod("GetMapper").MakeGenericMethod(sourceType, targetType);
 24             var mapper = getMapper.Invoke(null, null);
 25             var map = mapper.GetType().GetMethod("Map");
 26             return map.Invoke(mapper, new[] { source, target });
 27         }
 28     }
 29 
 30     public static class MapperFactory
 31     {
 32         private static readonly IDictionary<Type, object> Mappers = new Dictionary<Type, object>();
 33 
 34         public static ITypeMapper<TSource, TTarget> GetMapper<TSource, TTarget>()
 35         {
 36             //if we have a specified TypeMapper for <TSource,Target> return it
 37             if (Mappers.ContainsKey(typeof(ITypeMapper<TSource, TTarget>)))
 38                 return Mappers[typeof(ITypeMapper<TSource, TTarget>)] as ITypeMapper<TSource, TTarget>;
 39 
 40             //if both Source and Target types are Enumerables return new EnumerableTypeMapper<TSource,TTarget>()
 41             if (typeof(TSource).IsEnumerable() && typeof(TTarget).IsEnumerable())
 42             {
 43                 return (ITypeMapper<TSource, TTarget>)Activator.CreateInstance(typeof(EnumerableTypeMapper<,>).MakeGenericType(typeof(TSource), typeof(TTarget)));
 44             }
 45 
 46             //return the default TypeMapper
 47             return new TypeMapper<TSource, TTarget>();
 48         }
 49 
 50         public static void AddMapper<TS, TT>(ITypeMapper<TS, TT> o)
 51         {
 52             Mappers.Add(typeof(ITypeMapper<TS, TT>), o);
 53         }
 54 
 55         public static void ClearMappers()
 56         {
 57             Mappers.Clear();
 58         }
 59     }
 60 
 61     public interface ITypeMapper<TSource, TTarget>
 62     {
 63         TTarget Map(TSource source, TTarget target);
 64     }
 65 
 66     public class TypeMapper<TSource, TTarget> : ITypeMapper<TSource, TTarget>
 67     {
 68         public virtual TTarget Map(TSource source, TTarget target)
 69         {
 70             target.InjectFrom(source)
 71                 .InjectFrom<NullablesToNormal>(source)
 72                 .InjectFrom<NormalToNullables>(source)
 73                 .InjectFrom<IntToEnum>(source)
 74                 .InjectFrom<EnumToInt>(source)
 75                 .InjectFrom<MapperInjection>(source);// apply mapper.map for Foo, Bar, IEnumerable<Foo> etc.
 76 
 77             return target;
 78         }
 79     }
 80 
 81     public class EnumerableTypeMapper<TSource, TTarget> : ITypeMapper<TSource, TTarget>
 82         where TSource : class
 83         where TTarget : class
 84     {
 85         public TTarget Map(TSource source, TTarget target)
 86         {
 87             if (source == null) return null;
 88             var targetArgumentType = typeof(TTarget).GetGenericArguments()[0];
 89 
 90             var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(targetArgumentType));
 91             var add = list.GetType().GetMethod("Add");
 92 
 93             foreach (var o in source as IEnumerable)
 94             {
 95                 var t = Creator.Create(targetArgumentType);
 96                 add.Invoke(list, new[] { Mapper.Map(o, t, o.GetType(), targetArgumentType) });
 97             }
 98             return (TTarget)list;
 99         }
100     }
101 
102     public static class TypeExtensions
103     {
104         //returns true if type is IEnumerable<> or ICollection<>, IList<> ...
105         public static bool IsEnumerable(this Type type)
106         {
107             if (type.IsGenericType)
108             {
109                 if (type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
110                     return true;
111             }
112             return false;
113         }
114     }
115 
116     public static class Creator
117     {
118         public static object Create(Type type)
119         {
120             if (type.IsEnumerable())
121             {
122                 return Activator.CreateInstance(typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]));
123             }
124 
125             if (type.IsInterface)
126                 throw new Exception("don't know any implementation of this type: " + type.Name);
127 
128             return Activator.CreateInstance(type);
129         }
130     }
131 
132     public class MapperInjection : ConventionInjection
133     {
134         protected override bool Match(ConventionInfo c)
135         {
136             return c.SourceProp.Name == c.TargetProp.Name &&
137                 !c.SourceProp.Type.IsValueType && c.SourceProp.Type != typeof(string) &&
138                 !c.SourceProp.Type.IsGenericType && !c.TargetProp.Type.IsGenericType
139                 ||
140                  c.SourceProp.Type.IsEnumerable() &&
141                    c.TargetProp.Type.IsEnumerable();
142         }
143 
144         protected override object SetValue(ConventionInfo c)
145         {
146             if (c.SourceProp.Value == null) return null;
147             return Mapper.Map(c.SourceProp.Value, c.TargetProp.Value, c.SourceProp.Type, c.TargetProp.Type);
148         }
149     }
150 
151     public class EnumToInt : ConventionInjection
152     {
153         protected override bool Match(ConventionInfo c)
154         {
155             return c.SourceProp.Name == c.TargetProp.Name &&
156                 c.SourceProp.Type.IsSubclassOf(typeof(Enum)) && c.TargetProp.Type == typeof(int);
157         }
158     }
159 
160     public class IntToEnum : ConventionInjection
161     {
162         protected override bool Match(ConventionInfo c)
163         {
164             return c.SourceProp.Name == c.TargetProp.Name &&
165                 c.SourceProp.Type == typeof(int) && c.TargetProp.Type.IsSubclassOf(typeof(Enum));
166         }
167     }
168 
169     //e.g. int? -> int
170     public class NullablesToNormal : ConventionInjection
171     {
172         protected override bool Match(ConventionInfo c)
173         {
174             return c.SourceProp.Name == c.TargetProp.Name &&
175                    Nullable.GetUnderlyingType(c.SourceProp.Type) == c.TargetProp.Type;
176         }
177     }
178 
179     //e.g. int -> int?
180     public class NormalToNullables : ConventionInjection
181     {
182         protected override bool Match(ConventionInfo c)
183         {
184             return c.SourceProp.Name == c.TargetProp.Name &&
185                    c.SourceProp.Type == Nullable.GetUnderlyingType(c.TargetProp.Type);
186         }
187     }

实际应用实例:

 1 public class Demo1
 2     {
 3         public string Key1 { get; set; }
 4         public string Key2 { get; set; }
 5         public string Property { get; set; }
 6     }
 7 
 8     public class Demo2
 9     {
10         public string Key1 { get; set; }
11         public string Key2 { get; set; }
12         public string Property { get; set; }
13     }
14 
15     class Program
16     {
17         public void Main()
18         {
19             (new HashSet<Demo1>()).ToDb((new HashSet<Demo2>()),
20                 d1 => new { d1.Key1, d1.Key2 }, d2 => new { d2.Key1, d2.Key2 });
21         }
22     }
23 }

为了说明 代码使用情况,只是一个简单的事例,并未弄得过于复杂。

(newHashSet<Demo1>())是我们的Collection数据源,
(new HashSet<Demo2>())是ef的目标Collection

你可能感兴趣的:(Collection)