加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 构建一组可在任何linq表上运行的通用方法

发布时间:2020-12-15 21:19:10 所属栏目:百科 来源:网络整理
导读:问题:我们广泛使用存储库模式来促进跨多个应用程序和功能子部分对我们的数据存储区(使用LINQ的MS SQL)进行读/写操作.我们有一系列方法都可以做彼此相似的事情. 例如,我们有ProcessAndSortXXXXX类方法. private static IEnumerableClassErrorEntry ProcessAn
问题:我们广泛使用存储库模式来促进跨多个应用程序和功能子部分对我们的数据存储区(使用LINQ的MS SQL)进行读/写操作.我们有一系列方法都可以做彼此相似的事情.

例如,我们有ProcessAndSortXXXXX类方法.

private static IEnumerable<ClassErrorEntry> ProcessAndSortClassErrorLog(IQueryable<ClassErrorDb> queryable,string sortOrder)
{
    var dynamic = queryable;
    if (!String.IsNullOrEmpty(sortOrder.Trim()))
    {
        dynamic = dynamic.OrderBy(sortOrder);
    }
    return dynamic
        .Select(l =>
            new ClassErrorEntry(l.Id)
            {
                ClassId = l.ClassId,Code = l.Code,Message = l.Message,Severity = l.Severity,Target = l.Target
            }
        );
}

…和…

private static IEnumerable<ClassTimerLogEntry> ProcessAndSortClassTimerLog(IQueryable<ClassTimerDb> queryable,string sortOrder)
{
    var dynamic = queryable;
    if (!String.IsNullOrEmpty(sortOrder.Trim()))
    {
        dynamic = dynamic.OrderBy(sortOrder);
    }
    return dynamic
        .Select(l =>
            new ClassTimerLogEntry(l.Id)
            {
                ClassName = l.ClassName,MethodName = l.MethodName,StartTime = l.StartTime,EndTime = l.EndTime,ParentId = l.ParentId,ExecutionOrder = l.ExecutionOrder
            }
        );
}

正如您可以从代码中看出的那样,它们都非常相似,直到您查看签名然后转到返回语句,我们正在构建ClassErrorEntry和ClassTimerLogEntry的实例.

我想构建一个实用程序方法,我将其添加到所有存储库继承的基类中.

我希望能够传入可用于实例化对象的参数,并将它们打包到返回的IEnumerable中.

我在ScottGu年找到了this post,这让我得到了我需要的大部分内容.它看起来像这样(来自文档中的示例):

var query =
    db.Customers.
    Where("City = @0 and Orders.Count >= @1","London",10).
    OrderBy("CompanyName").
    Select("new(CompanyName as Name,Phone)");

不过,这是我被困的地方.我需要一个指针或建议如何以通用方式传递LINQ表和DataContext,以便我可以构建动态查询.

如果我用伪代码模拟签名,我认为它看起来像这样:

protected internal IEnumerable ProcessAndSort(IQueryable source,string selectClause,string whereClause,string orderByClause);

我意识到完成的签名可能看起来不同,因为我们想出来了.

谢谢!

更新!

我现在有代码可以生成匿名类型,但在转换为具体类型时失败.

public static IEnumerable<TResult> ProcessAndSort<T,TResult>(IQueryable<T> queryable,string selector,Expression<Func<T,bool>> predicate,string sortOrder)
{
    var dynamic = queryable.Where(predicate).AsQueryable();
    if (!String.IsNullOrEmpty(sortOrder.Trim()))
    {
        dynamic = dynamic.OrderBy(sortOrder);
    }
    var result= dynamic.Select(selector).Cast<TResult>();

    return result;
}

以下是调用此方法的代码:

[TestMethod]
public void TestAnonymousClass()
{
    var loggingContext = new LoggingDbDataContext(DatabaseConnectionString);
    var repo = new LoggingRepository(loggingContext);

    var result = repo.TestGetClassErrorLog(4407,10,"new ( ClassId as ClassId," +
        "Code as Code," +
        "Message as Message," +
        "Severity as Severity," +
        "Target as Target )","Target");
    TestContext.WriteLine(result.ToList().Count.ToString());
}

最后一行TestContext.WriteLine(result.ToList().Count.ToString());抛出异常System.InvalidOperationException:类型’DynamicClass1’和’Utilities.Logging.ClassErrorEntry’之间没有定义强制运算符.

这段代码虽然失败了:

[TestMethod]
public void TestNamedClass()
{
    var loggingContext = new LoggingDbDataContext(DatabaseConnectionString);
    var repo = new LoggingRepository(loggingContext);

    var result = repo.TestGetClassErrorLog(4407,"new ClassErrorEntry(Id) { ClassId = ClassId," +
        "Code = Code," +
        "Message = Message," +
        "Severity = Severity," +
        "Target = Target }","Target");
    TestContext.WriteLine(result.ToList().Count.ToString());
}

这在解析错误时失败.测试方法eModal.Repositories.Test.RepositoryBaseTest.TestConcreteClass抛出异常:
System.Linq.Dynamic.ParseException:'(‘期望,在’新ClassErrorEntry(Id)中的char 19处找到’ClassErrorEntry'(‘Identifier’){ChassisAuthId = ChassisAuthId,Code = Code,Message = Message,Severity = Severity,Target =目标}’

我不确定字符位置是否可疑,因为第19个字符位置是a(并且传入Validate方法的类型表示位置为4,或者第一个’C’.

解决方法

我完全建议你不要仅仅为了代码重用而进行弱类型查询.
代码重用是为了提高可维护性,但如果以错误的方式使用,弱类型可能会导致代码重用.
通过以纯文本形式编写查询,您有效地使类很难重构和更改,并引入了许多模糊的依赖项.

我建议你看看LinqKit,它允许组合表达式.例如,我们编写了一个Paging方法,该方法按页面拆分查询,并在不同类型的项目中使用它:

var query = CompiledQuery.Compile(
    BuildFolderExpr( folder,false )
        .Select( msg => selector.Invoke( msg,userId ) ) // re-use selector expression
        .OrderBy( mv => mv.DateCreated,SortDirection.Descending )
        .Paging() // re-use paging expression
        .Expand() // LinqKit method that "injects" referenced expressions
    )

public static Expression<Func<T1,T2,PagingParam,IQueryable<TItem>>> Paging<T1,TItem>(
    this Expression<Func<T1,IQueryable<TItem>>> expr )
{
    return ( T1 v1,T2 v2,PagingParam p ) => expr.Invoke( v1,v2 ).Skip( p.From ).Take( p.Count );
}

在我的示例中,BuildMessageExpr返回一个相对简单的select表达式(已经依赖于文件夹和另一个参数),并且不同的方法通过应用过滤,排序,获取计数,进一步选择并将选择器表达式作为参数传递来重用此表达式,等等.创建查询后,当参数相似时,它将被缓存以供将来使用.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读