我有几个表格,例如:Cats
,Dogs
和Rabbits
。每个表都有两个字段:整数主键XId
和字符串XName
,其中X
是表的名称:Cat
,Dog
或{ {1}}。
然后,我为每个包含名称的表都有一个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 });
...
可以达到类似的效果吗?不幸的是,它没有按原样工作,因为LINQ提供程序无法将函数调用转换为SQL查询。
由于字段名称不同,我无法为我的对象创建一个公共接口。更改POCO类定义也不是一种选择。
答案 0 :(得分:1)
在Linq.Dynamic的帮助下,有可能实现这一目标。
并且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();
}