从 Dapper 连接但从 EF 6 工作的神秘身份验证错误 - 相同的 ConnectionString

Mysterious authentication error connecting from Dapper but works from EF 6 - same ConnectionString

我们有 IoC 加载的服务和存储库。

/* registration in the service class for repository */
container.Register<IContainerRepository, ContainerRepository>();

/* registration in GUI app from service class and Form class for constructor injection usage */
container.Register<IContainerService, ContainerService>();
container.Register<rfrmContainerList, rfrmContainerList>();

所以我们有 ContainerRepository 用于数据访问和 ContainerService 用于业务逻辑。

ContainerRepository 使用 EF6 和 Dapper(基本上 EF6 用于简单查询和 insert/update/delete 方法,而我们使用 Dapper 用于针对复杂连接数据的 运行 优化查询)。每个存储库都有 IoC 在 class 构造函数中传递的 EF6 上下文。 Dapper 始终从上下文中获取 ConnectionString:

var testItem = Context.Container.FirstOrDefault();

using (SqlConnection connection = new SqlConnection(Context.Database.Connection.ConnectionString))
{
    var builder = new SqlBuilder();
...
    var items = connection.Query<ContainerDto>(sql.RawSql, sql.Parameters);
}

构造函数注入注入的ContainerService

private readonly IContainerService _containerService;

public rfrmContainerList(IContainerService containerService)
{
    _containerService = containerService ?? throw new ArgumentNullException(nameof(containerService));
}

现在一切都已设置,所以我希望如果 Dapper 使用与 EF6 相同的 ConnectionString,它将使用相同的身份验证,对吗?

没有。虽然 var testItem = Context.Container.FirstOrDefault(); 成功 returns 预期结果 Dapper 在 connection.Query<> 失败并抛出异常说 Login failed for user 'username' (用户名替换为我的测试用户的实际登录名)。

所以经过多次测试和尝试,我终于定位了主要问题,它在 IoC 中。

/* This query will return successfully */

var localContainerService = IoC.Resolve<IContainerService>();
var container = localContainerService.ListContainers(new Model.QueryParams.DesktopApp.Operation.ContainerListQueryParams() { StatusDateFrom = new DateTime(2019, 8, 1), StatusDateTo = DateTime.Now.Date });

/* This query will fail with authentication */

var container2 = _containerService.ListContainers(new Model.QueryParams.DesktopApp.Operation.ContainerListQueryParams() { StatusDateFrom = new DateTime(2019, 8, 1), StatusDateTo = DateTime.Now.Date });

因此,使用本地解析的服务对象的第一个查询运行没有任何问题,从上下文中获取 ConnectionString 和 connection.Query<ContainerDto>(sql.RawSql, sql.Parameters) returns 预期结果。

使用构造函数注入服务的第二个查询在同一行失败说 "Login failed for user 'username'"。

奇怪,但 EF6 查询 var testItem = Context.Container.FirstOrDefault(); 适用于这两种情况。

有人遇到过类似的问题吗?我已经检查了我所有的 IoC 注册,但无法弄清楚为什么会发生这种奇怪的行为。

在多种情况下这将不起作用。好消息是您一开始就不应该使用两个单独的 SqlConnections。只需使用 Dapper/ADO.NET 的 DbContext 的 SqlConnection。 DbContext 将清理 SqlConnection,您甚至不需要额外的 using 块。

就像这样:

class MyDb : DbContext
{

    public SqlConnection GetConnection()
    {
        var con = (SqlConnection)Database.Connection;
        if (con.State != System.Data.ConnectionState.Open)
        {
            con.Open();
        }
        return con;
    }

    // . . .
}

如果您使用 SQL 身份验证 (UId / Pwd),那么当您使用 context.Database.Connection.ConnectionString 时,EF 不会 泄露密码。例如,将其输出到控制台,您会得到如下内容:

"Data Source=QH10373138\DEV2008R2;Initial Catalog=Spikes;uid=sqladmin;MultipleActiveResultSets=True"

我的初始连接字符串包含“;pwd=HeyYouShouldntSeeThi$”

如果您使用 Windows 身份验证,应该 有效。

虽然对 David 的回答 +1,但如果您需要做一些棘手的事情,请使用 EF 的 DbConnection。

我还会看看这些问题是否可以通过更好的 EF 查询来解决,例如利用 Select 的投影来获取与 EagerLoading 相关的详细信息,或者寻找延迟加载的陷阱。就个人而言,在使用 NHibernate 处理大量项目之后,当 EF(4) 除了最琐碎的场景外,对任何事情都毫无用处时,到现在几乎完全使用 EF 6,我还没有找到我需要的场景像 Dapper 来解决最坏的情况,即通过有界上下文引入优化的实体模型。这很少见,当它被使用时,它类似于支持批量操作。变通办法的风险是它们成为隐藏设计问题并增加维护复杂性的拐杖。默认方法是,如果 EF 看起来很慢,则编写一个 Dapper 替代方案,而不是解决 EF 代码编写方式中可能存在的简单低效问题。

感谢这些回答,我要添加另一个:ConnectionString 必须包含 Persist Security Info=true 并且它再次起作用。

不过我更喜欢@David 的解决方案。