首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架核心-非线程安全- ServiceLifetime.Transient

实体框架核心-非线程安全- ServiceLifetime.Transient
EN

Stack Overflow用户
提问于 2017-07-18 18:44:18
回答 2查看 4.4K关注 0票数 1

看来,我的数据库上下文并不是线程安全的、瞬态的,甚至是我指定的:

代码语言:javascript
复制
ServiceLifetime.Transient 

在数据库的启动配置中。

Startup.cs

代码语言:javascript
复制
services.AddEntityFrameworkSqlServer().AddDbContext<DatabaseContext>((serviceProvider, options) => options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking).UseSqlServer(connectionString).UseInternalServiceProvider(serviceProvider), ServiceLifetime.Transient);

Api

代码语言:javascript
复制
[HttpPost("GetOrganisations")] //Yes post, dont harass me :)
public async Task<IActionResult> GetOrganisations([FromBody] GetOrganisationsModel model)
{
    Task<IEnumerable<OrganisationModel>> organisations = _organisationService.GetOrganisations(model?.Id, model?.StatusIds);

    Task<int> organisationTotalCount = _organisationService.GetOrganisationCount();

    await Task.WhenAll(organisations, organisationTotalCount);

    return Ok(new OrganisationViewModel
    {
        Organisations = await organisations,
        OrganisationTotalCount = await organisationTotalCount
    });
}

回购

代码语言:javascript
复制
public class OrganisationRepository : IOrganisationRepository
{
    private readonly DatabaseContext _database;

    public OrganisationRepository(DatabaseContext database)
    {
        _database = database;
    }

    public async Task<List<OrganisationEntity>> GetOrganisations(int? organisationId, List<int> statusIds)
    {
        IQueryable<OrganisationEntity> organisations = Database.Organisation.Include(o => o.Status).OrderByDescending(d => d.Created).AsQueryable();

        if (organisationId != null)
        {
            organisations = organisations.Where(o => o.Id == organisationId);
        }

        if (statusIds != null && statusIds.Count > 0)
        {
            organisations = organisations.Where(o => statusIds.Contains(o.StatusId));
        }

        return await organisations.ToListAsync();
    }

    public async Task<int> GetOrganisationCount()
    {
        return await Database.Organisation.CountAsync();
    }
}

异常

System.InvalidOperationException:连接没有关闭。连接的当前状态是连接。

这里的问题是,我调用GetOrganisations()和GetOrganisationCount()异步,并且由于某种原因(我认为),对两个调用(不同的线程)使用相同的数据库连接。我认为通过增加选项:

代码语言:javascript
复制
ServiceLifetime.Transient 

它可以解决我的问题(因为默认范围是限定的),但不..

但是,如果我添加使用语句。

代码语言:javascript
复制
public async Task<List<OrganisationEntity>> GetOrganisations(int? organisationId, List<int> statusIds)
{
    var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
        .UseSqlServer(
            "Data Source=.;Initial Catalog=MYDATABASE;Integrated Security=True;Connect Timeout=30;");

    using (var db = new DatabaseContext(optionsBuilder.Options, null))
    {
        IQueryable<OrganisationEntity> organisations =
            db.Organisation.Include(o => o.Status).OrderByDescending(d => d.Created).AsQueryable();

        if (organisationId != null)
        {
            organisations = organisations.Where(o => o.Id == organisationId);
        }

        if (statusIds != null && statusIds.Count > 0)
        {
            organisations = organisations.Where(o => statusIds.Contains(o.StatusId));
        }

        return await organisations.ToListAsync();
    }
}

public async Task<int> GetOrganisationCount()
{
    var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
        .UseSqlServer(
            "Data Source=.;Initial Catalog=MYDATABASE;Integrated Security=True;Connect Timeout=30;");

    using (var db = new DatabaseContext(optionsBuilder.Options, null))
    {
        return await db.Organisation.CountAsync();
    }
}

一切正常!

为什么依赖注入不能正常工作?

,我是不是完全搞错了?如果我需要临时上下文,是否被迫在所有任务中使用use语句?

ServiceLifetime.Transient窃听了?!

编辑:

Stacktrade,(一些信息蒙面)

在System.Data.ProviderBase.DbConnectionClosedConnecting.TryOpenConnection(DbConnection outerConnection,DbConnectionFactory connectionFactory,TaskCompletionSource1 retry, DbConnectionOptions userOptions)\r\n \r\n在System.Data.SqlClient.SqlConnection.OpenAsync(CancellationToken cancellationToken)\r\n--从抛出异常的前一个位置开始的堆栈跟踪的结束--\\r\n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在堆栈跟踪的Microsoft.EntityFrameworkCore.Storage.RelationalConnection.d__31.MoveNext()\r\n---末端,从抛出异常的以前位置开始 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在堆栈跟踪的Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.d__9.MoveNext()\r\n---末端,从抛出异常的以前位置开始 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.d__62.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.<MoveNext>d__8.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 1.MoveNext()\r\n--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n中抛出异常的前一个位置的堆栈跟踪的结束-\r\n (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在Microsoft.EntityFrameworkCore.Query.Internal.TaskResultAsyncEnumerable1.Enumerator.<MoveNext>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 从抛出异常的前一个位置开始的堆栈跟踪的at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.d__5.MoveNext()\r\n---结束-\r\n在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务中)\r\n在System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at XXXXXXX.Data.Repositories.Organisation.OrganisationRepository.<GetOrganisationCount>d__8.MoveNext() in C:\\Users\\XXXXXXX\\Documents\\Visual Studio 2017\\Projects\\XXXXXXX\\XXXXXXX.Data\\Repositories\\Organisation\\OrganisationRepository.cs:line 65\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n C中XXXXXXX.Api.Services.Organisation.OrganisationService.d__4.MoveNext()的at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.1.GetResult()\r\n:\User\XXXXXXX\Documents\Visual 2017\Projects\XXXXXXX\XXXXXXX.Api\Services\Organisation\OrganisationService.cs:line 31\r\n--从抛出异常的前一个位置开始的堆栈跟踪的结束--\\r\n位于System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n \r\n in System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at XXXXXXX.Api.Controllers.Administration.OrganisationController.d__5.MoveNext() in C:\Users\XXXXXXX\Documents\Visual 2017\Projects\XXXXXXX\XXXXXXX.Api\Controllers\Administration\OrganisationController.cs:line 51“

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-18 19:17:02

ServiceLifetime.Transient窃听了?!

这不是窃听器。正如我们所知道的,DbContext并不是线程安全的,这两个方法调用使用相同的OrganisationService实例,这些实例本身是使用DatabaseContext的相同实例创建的。

代码语言:javascript
复制
Task<IEnumerable<OrganisationModel>> organisations = 
    _organisationService.GetOrganisations(model?.Id, model?.StatusIds);

Task<int> organisationTotalCount = _organisationService.GetOrganisationCount();

它是线程交换环境下实体框架DbContext的一个限制。因此,我们采用以下传统方法-

代码语言:javascript
复制
[HttpPost("GetOrganisations")]
public async Task<IActionResult> GetOrganisations([FromBody] GetOrganisationsModel model)
{
    var organisations = 
        await  _organisationService.GetOrganisations(model?.Id, model?.StatusIds);

    var organisationTotalCount = await _organisationService.GetOrganisationCount();

    return Ok(new OrganisationViewModel
    {
        Organisations = organisations,
        OrganisationTotalCount = organisationTotalCount
    });
}

newing在存储库中的缺点是它们变得紧密耦合,您不能像那样对存储库进行单元测试。如果在特定场景中必须在Task.WhenAll与单元测试之间进行选择,那么我将选择Unit。

如果您看到自己经常使用Task.WhenAll,您可能需要查看可以与EF一起使用的Dapper ORM

票数 3
EN

Stack Overflow用户

发布于 2018-09-28 00:18:51

还请注意,在Microsoft依赖项注入中,在作用域结束之前,暂存服务不会被释放。

有一种简单的方法,就是使用WeakReference代替TService作为临时的。然后,WeakReference的用户可以负责处理。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45174739

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档