包含多个 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 {}
}
看来错误已经消失了
我有一个 .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 {}
}
看来错误已经消失了