【问题标题】:How to use Dapper with Linq如何在 Linq 中使用 Dapper
【发布时间】:2016-08-08 10:01:46
【问题描述】:

我正在尝试从 Entity Framework 转换为 Dapper,希望能提高数据访问性能。

我使用的查询是谓词的形式,例如Expression<Func<TModel, bool>>

举个例子:

我需要使用 Dapper 转换以下代码。

我目前在做什么:

public async Task<List<TModel>> Get(Expression<Func<TModel, bool>> query)
{
    // this.Context is of type DbContext
    return await this.Context.Set<TModel>().Where(query).ToListAsync();
}

我想做的事:

public async Task<List<TModel>> Get(Expression<Func<TModel, bool>> query)
{
    using (IDbConnection cn = this.GetConnection)
    {
        return await cn.QueryAsync<TModel>(query);
    }
}

我的 google-fu 让我失望了,有人可以帮忙吗?

编辑:

请注意,我确实找到了: https://github.com/ryanwatson/Dapper.Extensions.Linq

但我似乎无法弄清楚如何使用它。

【问题讨论】:

  • LINQ to EF 从实体模型和 LINQ 谓词构造 SQL 语句。 Dapper 执行实际的 SQL 语句并“简单地”映射结果。您必须编写完整的 SQL 参数化 SQL 语句。
  • 换一种说法,你确定你没有解决错误的问题吗?您可以简单地将又一个Where 子句添加到您的任何查询中,例如var query=context.SomeEntity.Where()....; query=query.Where(...); query=query.Select(...);。如果您只想将过滤器添加到现有查询,则无需使用表达式。迁移到微 ORM 不会使查询的运行时构造变得更容易
  • 对于 Dapper, cn.QueryAsync(query) ,查询应该是 sql 字符串而不是 Expression>。您必须在从 EF 转换时进行重大更改。到 Dapper。

标签: c# linq expression dapper predicate


【解决方案1】:

首先,Dapper 的一位作者说,当有人问起时

是否有计划让 Dapper.net 与 IQueryable 接口兼容?

那个

没有计划这样做。 dapper 试图做的事情远非如此。到目前为止,我会说这是对立的。 Dapper core 试图成为那些热爱 SQL 的人的朋友。

(见https://stackoverflow.com/a/27588877/3813189)。

正如您所建议的那样,这确实表明 NuGet 的各种扩展包可能会有所帮助。

我尝试过DapperExtensions,这使得以编程方式编写查询过滤器更容易一些 - 例如。

using System.Data.SqlClient;
using DapperExtensions;

namespace StackOverflowAnswer
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var cn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
            {
                var list = cn.GetList<Products>(
                    Predicates.Field<Products>(f => f.Discontinued, Operator.Eq, false)
                );
            }
        }

        class Products
        {
            public int ProductId { get; set; }
            public string ProductName { get; set; }
            public bool Discontinued { get; set; }
        }
    }
}

我也试过Dapper.Extensions.Linq(你建议的包),它承诺

以此为基础,通过 Linq 查询提供高级数据库访问。流畅的配置让设置变得简单快捷。

不幸的是,我也无法做到这一点。没有太多文档,测试似乎也没有涵盖 QueryBuilder,这似乎是用于将 Linq 表达式转换为 Dapper Extensions 谓词的类(正如问题 Parsing boolean expressions with the QueryBuilder 所建议的那样)。我尝试了以下方法,这需要将 IEntity 接口添加到我的 DTO -

using System;
using System.Data.SqlClient;
using System.Linq.Expressions;
using Dapper.Extensions.Linq.Builder;
using Dapper.Extensions.Linq.Core;
using DapperExtensions;

namespace StackOverflowAnswer
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var cn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
            {
                Expression<Func<Products, bool>> filter = p => !p.Discontinued;
                var queryFilter = QueryBuilder<Products>.FromExpression(filter);

                var list = cn.GetList<Products>(
                    queryFilter
                );
            }
        }

        class Products : IEntity
        {
            public int ProductId { get; set; }
            public string ProductName { get; set; }
            public bool Discontinued { get; set; }
        }
    }
}

.. 但它在运行时失败并出现错误

找不到 StackOverflowAnswer.Program+Products 的操作员

我不确定为什么手动生成谓词(第一个示例)有效,但 QueryBuilder 却不行..

我想说的是,您的问题上留下的 cmets 越来越看起来是正确的,您将需要重新处理代码,使其远离您在 Entity Framework 中使用的表达式。由于很难找到有关此 QueryBuilder 类的任何信息,因此我担心(即使您确实使它正常工作)您遇到的任何问题都难以获得帮助(并且错误可能无法修复)。

【讨论】:

  • 感谢您花时间研究我的问题。我很高兴(也很伤心)有人得出了与我相同的结论。非常感谢。
  • 只是添加到这个。尽管 Dapper 作者可能认为 Dapper 的 LINQ 扩展与其预期目的“对立”,但我确实相信这样的事情有充分的理由和好处。首先,此类库提供针对 sql 注入攻击的内置保护。其次,您可以获得智能感知的好处。您可以获得所有这些以及 Dapper 的稳定性和可靠性,而没有实体框架的泄漏抽象、警告和开销堆栈。手工编写纯 SQL 并不总是选择 Dapper 作为 ORM 的决定因素。
【解决方案2】:

我编写了一个实用程序来使用属性与 Dapper 一起使用 EF。我解析谓词并转换为 SQL。

“用户”POCO:

[Table("Users")]
public class User
{
    [Key]
    [Identity]
    public int Id { get; set; }

    public string Login { get; set;}

    [Column("FName")]
    public string FirstName { get; set; }

    [Column("LName")]
    public string LastName { get; set; }

    public string Email { get; set; }

    [NotMapped]
    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }
    }
}

简单查询:

using (var cn = new SqlConnection("..."))
{
    var usersRepository = new DapperRepository<User>(cn)
    var allUsers = await userRepository.FindAllAsync(x => x.AccountId == 3 && x.Status != UserStatus.Deleted);
}

也许对你有用?

MicroOrm.Dapper.Repositories

【讨论】:

  • 我了解您的回复不是对原始问题的回答,因为您没有使用 LINQ。但是,您的解决方案是任何人在不使用实体框架的情况下最接近类型安全、进程外的查询表达式的解决方案。惊人的!干得好!
  • 这似乎是一个有效的答案。事实上,你可以输入var result = await new DapperRepository&lt;TModel&gt;(cn).FindAllAsync(query).ToListAsync(),这正是他想要的。
猜你喜欢
  • 1970-01-01
  • 2013-03-07
  • 2013-11-24
  • 2019-07-29
  • 1970-01-01
  • 2018-01-26
  • 1970-01-01
  • 2019-08-10
  • 2011-09-06
相关资源
最近更新 更多