抽象实体框架中不同表上的动作

本文关键字:实体 框架 抽象 | 更新日期: 2023-09-27 18:18:55

我有几个表,例如:Cats, DogsRabbits。每个表有两个字段:整数主键XId和字符串XName,其中X是表的名称:CatDogRabbit

然后,每个包含名称的表都有一个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();
}