如何创建C#Generic Linq Querier?

Mar*_*175 5 c# linq entity-framework

考虑首先使用Entity Framework代码构建多个表的数据库.每个表都包含不同类型的对象,但我希望为了可扩展性而创建一个通用查询构建器类.到目前为止,这个类的框架我有一个泛型类,因此它可以作为Linq to SQL的包装器:

public class DBQuerier<T>
    where T : class
{
    DbSet<T> relation;

    public DBQuerier(DbSet<T> table)
    {
        relation = table;
    }

    public bool Exists(T toCheck);
    public void Add(T toAdd);
    public T (Get Dictionary<String, Object> fields);
    public bool SubmitChanges();
    public void Update(T toUpdate, Dictionary<String, Object> fields);
    public void Delete(T toDelete);
}
Run Code Online (Sandbox Code Playgroud)

我的问题来自第一个障碍,当我试图检查是否存在记录,因为我无法在泛型类型T和我正在尝试使用的对象类型之间进行转换.如果我使用基地Linq:

public bool Exists(T toCheck)
{
    return (from row in relation
        where row.Equals(toCheck)
        select row).Any();
}
Run Code Online (Sandbox Code Playgroud)

发生运行时异常,因为即使我实现IComparable并指定了比较单个字段的自己的Equals,SQL也无法处理除基本类型之外的任何内容.Lambda表达式似乎越来越接近,但后来我再次遇到问题,SQL无法处理超过原始类型,即使我的理解是Expression.Equal强迫它使用类的可比函数:

public bool Exists(T toCheck)
{
    ParameterExpression T1 = Expression.Parameter(typeof(myType), "T1");
    ParameterExpression T2 = Expression.Parameter(typeof(myType), "T2");
    BinaryExpression compare = Expression.Equal(T1, T2);
    Func<T, T, bool> checker =
        Expression.Lambda<Func<T, T, bool>>
            (compare, new ParameterExpression[] { T1, T2 }).Compile();
    return relation.Where(r => checker.Invoke(r, toCheck)).Any();
}
Run Code Online (Sandbox Code Playgroud)

设计表达式树是为了以后我可以根据我试图查看的类型添加一个switch语句来构建查询.我的问题是:有没有更简单/更好的方法来做到这一点(或修复我迄今为止尝试过的),因为我能看到的唯一其他选项是为每个表编写一个类(不太容易扩展)或检查每个记录应用程序端(如果你必须转移整个数据库,可能会非常慢!)?如果我犯了这么非常基本的错误,我很抱歉,因为我已经很长时间没有这么做了,所以提前感谢!

Dar*_*opp 6

不要编译它.Func<T,bool>意味着"在内存中运行它",Expression<Func<T,bool>>意思是"保持这个谓词是什么的逻辑思想",这允许像实体框架这样的框架将其转换为查询.

作为旁注,我认为实体框架不允许您a.Equals(b)进行查询,因此您必须这样做a.Id == b.Id


Nib*_*Pig 2

实体框架不太可能与您的自定义 linq 一起使用,它支持的命令非常严格。我要闲聊一下,它是伪代码,但我找到了两个适合我的解决方案。

我首先使用泛型方法,我的通用数据库搜索器将接受 aFunc<T, string> nameProperty来访问我要查询的名称。EF 有许多用于访问集和属性的重载,因此我可以通过传入c => c.CatName并使用它以通用方式访问属性来完成这项工作。不过有点乱,所以:

后来我将其重构为使用接口。

我有一个函数可以对您传递给该方法的任何表/列执行文本搜索。

我创建了一个名为 的接口INameSearchable,它只包含一个属性,该属性将成为要搜索的名称属性。然后我扩展了我的实体对象(它们是部分类)来实现INameSearchable. 所以我有一个名为的实体Cat,它有一个CatName属性。我使用该接口return CatName;作为接口的 Name 属性。

然后,我可以创建一个通用搜索方法where T : INameSearchable,它将公开Name我的界面公开的属性。然后,我只需在我的方法中使用它来执行查询,例如。(记忆中的伪代码!)

doSearch(myContext.Cats);

并在方法中

public IEnumerable<T> DoSearch<T>(IDbSet<T> mySet, string catName)
{
   return mySet.Where(c => c.Name == catName);
}
Run Code Online (Sandbox Code Playgroud)

而且非常漂亮的是,它允许我一般性地搜索任何东西。

我希望这有帮助。