在Entity Framework中抽象对不同表的操作

时间:2014-08-21 13:20:52

标签: c# linq entity-framework generics

我有几个表格,例如:CatsDogsRabbits。每个表都有两个字段:整数主键XId和字符串XName,其中X是表的名称:CatDog或{ {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类定义也不是一种选择。

1 个答案:

答案 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();
}