包含多个 dbcontexts 的范围 class

wrapped scoped class with multiple dbcontexts

我有一个 .net 核心 api,它对不同的数据库使用不同的 dbcontext。

我写了一个 class 包装了所有的 dbcontexts:

public class BoundedContext : IDisposable
{
    public EcommerceDbContext EcommerceContext { get; }
    public SecurityDbContext SecurityContext { get; }
    public CRMDbContext CrmContext { get; }

    public BoundedContext(string EcommerceConnectionString,
                          string SecurityConnectionString,
                          string CRMConnectionString)
    {
        EcommerceContext = new EcommerceDbContext(EcommerceConnectionString);
        SecurityContext = new SecurityDbContext(SecurityConnectionString);
        CrmContext = new CRMDbContext(CRMConnectionString);
    }

    public void SaveChanges()
    {
        if (SecurityContext != null)
            SecurityContext.SaveChanges();
        if (CrmContext != null)
            CrmContext.SaveChanges();
        if (EcommerceContext != null)
            EcommerceContext.SaveChanges();
    }

    public void Dispose()
    {
        if (SecurityContext != null)
            SecurityContext.Dispose();
        if (CrmContext != null)
            CrmContext.Dispose();
        if (EcommerceContext != null)
            EcommerceContext.Dispose();            
    }
}

在启动时class我将它定义为一个作用域实例:

services.AddScoped((_) => new BoundedContext(Configuration["Data:Ecommerce:ConnectionString"],
                                                     Configuration["Data:Security:ConnectionString"], 
                                                     Configuration["Data:CRM:ConnectionString"]));

控制器操作正在调用静态 class 传递一个或多个 "commands" 所以这个 class 负责执行它并提交更改

namespace Test.Business.Services
{
public static class CommandService
{
    static BoundedContext _context;
    public static void Process(BoundedContext context, IEnumerable<ICommand> commands)
    {
        _context = context;            

        //actions
        foreach (var command in commands)
        {
            command.Execute(_context);                                
        }

        foreach (var command in commands)
        {
            if (command is IBulkInsertCommand)
            {
                (command as IBulkInsertCommand).BulkInsert();
            }
        }
        //commit 
        _context.SaveChanges();

        //post actions
        foreach (var command in commands)
        {
            if (command is IPostCommitCommand)
            {
                (command as IPostCommitCommand).PostCommitAction();
                _context.SaveChanges();
            }                
        }
    }        
}
}

我有一个 .net 核心网站,它使用 swagger 生成的 sdk 调用此 api。网络控制器有一个过滤器来获取登录用户的属性:

public override void OnActionExecuting(ActionExecutingContext context)
{
    if (User.Identity.IsAuthenticated)
        {
            if (_currentUser == null)
            {
                _currentUser = ApiHandler.GetCurrentUser(_applicationId, _accessToken);
            }

            return _currentUser;
        }

        return null; 
}

以及一个动作示例:

// GET: /<controller>/
    [HttpGet("{All}")]
    public async Task<IActionResult> Index([FromRoute]string All)
    {
        GetNotificationResponse result = await ControllerHandler.GetNotifications(All, _accessToken());

        return PartialView("~/Views/Notifications/v1/NotificationsList.cshtml",result);
    }

我们用 jquery ajax 调用来调用此操作。问题是有时我们在 "OnActionExecuting" 中收到 System.ObjectDisposedException,但我不知道为什么,因为管理 dbcontexts 的 class 被注入了 scoped 选项。

你觉得这个架构不好还是我遗漏了什么?

我找到是谁惹的System.ObjectDisposedException了。我有一个检查访问令牌的中间件,在调用方法中我创建了一个上下文的新实例,因为这个上下文是每个租户一个数据库。这是我在 BoundedContext Class

中的代码
public void ChangeReportConnection(string connnectionSring)
    {
        if (_PowerBIContext == null)
        {
            _PowerBIContext = new PowerBIContext(connnectionSring);
        }
    }

这是改变上下文的中间件调用方法的一部分

public Task Invoke(HttpContext context, BoundedContext dbcontext, ILogger<MyAuthentication> logger, IMapper mapper)
    {
        _logger = logger;
        _mapper = mapper;
        _dbcontext = dbcontext;
        _context = context;

        StringValues headerValue;
        string encodedJwt = null;

        if (!_context.Request.Headers.TryGetValue("Authorization", out headerValue))
        {
            return _next(_context);
        }

        encodedJwt = headerValue.FirstOrDefault(h => h.Contains(_options.AuthentiacionOptions.AuthenticationScheme));

        if (!string.IsNullOrWhiteSpace(encodedJwt))
        {
            encodedJwt = encodedJwt.Substring((_options.AuthentiacionOptions.AuthenticationScheme.Length + 1));
        }

        if (!string.IsNullOrWhiteSpace(encodedJwt))
        {
            var handler = new JwtSecurityTokenHandler();
            ClaimsPrincipal principal = null;
            SecurityToken validToken = null;

            principal = handler.ValidateToken(encodedJwt, _options.tokenValidationParameters, out validToken);
            _context.User = principal;

            setReportConnectionString();
        }

        return _next(_context);
    }               

    private void setReportConnectionString()
    {
        var changeDatabaseCommand = new ChangeDatabaseCommand(_mapper, _context.User);

        CommandService.Process(_dbcontext, new ICommand[] { changeDatabaseCommand });            
    }

所以我删除了这个方法,我不从中间件的调用中调用它class。我把变化放在有界class的powerbicontext 属性中,像这样。

public PowerBIContext PowerBIContext{
        get
        {
            if (_PowerBIContext == null)
            {
                string ticket = GetTicket();
                if (!string.IsNullOrEmpty(ticket))
                {
                    int company = GetUserCompany(ticket);
                    if (company > 0)
                    {
                        string connectionString = GetPowerBIConnectionString(company);
                        if (!string.IsNullOrEmpty(connectionString))
                        {
                            _PowerBIContext = new PowerBIContext(connectionString);
                        }
                    }                        
                }                    
            }
            return _PowerBIContext;
        }

        private set {}
    }

看来错误已经消失了