抽象实体框架中不同表上的动作
本文关键字:实体 框架 抽象 | 更新日期: 2023-09-27 18:18:55
我有几个表,例如:Cats
, Dogs
和Rabbits
。每个表有两个字段:整数主键XId
和字符串XName
,其中X
是表的名称:Cat
、Dog
或Rabbit
。
然后,每个包含名称的表都有一个string[]
。我需要同步表与数组:删除表记录,没有在数组中列出他们的名字,并添加新的记录,只存在于数组中的名字。
最简单的方法是将代码复制三次,并在所有地方将CatName
更改为DogName
等。然而,我正在寻找一个更干净的解决方案,不重复代码。
如果它不是一个表,而是一个简单的IList<T>
,我将编写以下代码片段:
void SyncData<T>(IList<T> source, string[] names, Func<T, string> nameGetter, Func<string, T> creator)
{
var toRemove = source.Where(x => !names.Contains(nameGetter(x))).ToArray();
foreach(var item in toRemove)
source.Remove(item);
var toCreate = names.Where(x => !source.Any(i => nameGetter(i) != x)).ToArray();
foreach(var item in toCreate)
source.Add(creator(item));
}
SyncData(cats, catNames, c => c.CatName, n => new Cat { CatName = n });
SyncData(dogs, dogNames, d => d.DogName, n => new Dog { DogName = n });
...
IQueryable<T>
可以实现类似的东西吗?不幸的是,它不能按原样工作,因为LINQ提供程序不能将函数调用转换为SQL查询。
由于字段名不同,我不能为我的对象创建一个公共接口。更改POCO类定义也不是一个选项。
在Linq的帮助下。动态,可以实现。
并且IQueryable<T>
没有Add
方法,所以我们需要使用IDbSet<T>
或类似的方法。
你可以试试这个
public static class SyncExtension
{
public static void SyncData<T>(
this IDbSet<T> source,
string[] names,
Expression<Func<T, string>> name,
Func<string, T> creator) where T : class
{
var columnName = ((MemberExpression)name.Body).Member.Name;
var removePredicate = string.Format("!{0}.Contains(@0)", columnName);
var toRemove = source.Where(removePredicate, names).ToArray();
foreach (var item in toRemove)
source.Remove(item);
var addPredicate = string.Format("{0} = @0", columnName);
var toCreate = names.Where(x =>
!source.Where(addPredicate, x).Any()).ToArray();
foreach (var item in toCreate)
source.Add(creator(item));
}
}
使用。
using (var db = new AppContext())
{
var names = new[] { "A", "B" };
db.Set<Cat>().SyncData(names, x => x.Name, x => new Cat { Name = x });
db.SaveChanges();
}