如何解决用于使用ScopedLifestyle创建范围的装饰器
我需要一些帮助来了解我在配置容器时出了什么问题。
我通过使用this example来实现此实现。
基本上,我需要基于该接口将一些用例实现为数据库命令
components:
parameters:
id:
int:
in: path
name: id
schema:
type: integer
required: true
description: "id"
example: 1
uuid:
in: path
name: id
schema:
type: string
required: true
description: "uuid"
example: "b5fd728d-1c78-4085-89ba-579574b5831f"
每个命令都需要使用专用的DbContext,并且必须在该上下文上执行事务
为此,我已经实现
事务装饰器:
public interface IDatabaseCommand<TResult,TParam>
{
TResult Execute(TParam commandParam);
}
实施示例:
public class TransactionDatabaseCommandDecorator
: IDatabaseCommand<DatabaseResult,BusinessCommandParams1>
{
private readonly Container _container;
private readonly Func<IDatabaseCommand<DatabaseResult,BusinessCommandParams1>>
_decorateeFactory;
public TransactionDatabaseCommandDecorator(
Container container,Func<IDatabaseCommand<DatabaseResult,BusinessCommandParams1>> decorateeFactory)
{
_container = container;
_decorateeFactory = decorateeFactory;
}
public DatabaseResult Execute(BusinessCommandParams1 commandParam)
{
DatabaseResult res;
using (AsyncScopedLifestyle.BeginScope(_container))
{
var _command = _decorateeFactory.Invoke();
var factory = _container
.GetInstance<IDesignTimeDbContextFactory<WpfRaddispenserDbContext>>();
using (var transaction = factory.CreateDbContext(
new[] { "" }).Database.BeginTransaction())
{
try
{
res = _command.Execute(commandParam);
transaction.Commit();
}
catch (Exception e)
{
Console.WriteLine(e);
transaction.Rollback();
throw;
}
}
}
return res;
}
}
一些命令:
public class WpfRaddispenserUOW : IUnitOfWork<WpfRaddispenserDbContext>
{
private readonly IDesignTimeDbContextFactory<WpfRaddispenserDbContext> _factory;
private WpfRaddispenserDbContext _context;
private IDbContextTransaction _transaction;
public bool IsTransactionPresent => _transaction != null;
public WpfRaddispenserUOW(IDesignTimeDbContextFactory<WpfRaddispenserDbContext> fact)
{
_factory = fact ?? throw new ArgumentNullException(nameof(fact));
}
public WpfRaddispenserDbContext GetDbContext() =>
_context ?? (_context = _factory.CreateDbContext(null));
public IDbContextTransaction GetTransaction() =>
_transaction ?? (_transaction = GetDbContext().Database.BeginTransaction());
public void RollBack()
{
_transaction?.Rollback();
_transaction?.dispose();
}
public void CreateTransaction(IsolationLevel isolationLevel) => GetTransaction();
public void Commit() => _transaction?.Commit();
public void Persist() => _context.SaveChanges();
public void dispose()
{
_transaction?.dispose();
_context?.dispose();
}
}
容器的注册:
public class BusinessCommand1 : IDatabaseCommand<DatabaseResult,BusinessCommandParams1>
{
private readonly IUnitOfWork<WpfRaddispenserDbContext> _context;
public BusinessCommand1(IUnitOfWork<WpfRaddispenserDbContext> context)
{
_context = context;
}
public DatabaseResult Execute(BusinessCommandParams1 commandParam)
{
//Todo: use context
return new DatabaseResult();
}
}
问题是当我尝试执行时
var container = new Container();
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
container.Register<IDesignTimeDbContextFactory<WpfRaddispenserDbContext>>(() =>
{
var factory = new WpfRaddispenserDbContextFactory();
factory.ConnectionString =
"Server=.\\sqlExpress;Database=Test;Trusted_Connection=True";
return factory;
});
container.Register<IUnitOfWork<WpfRaddispenserDbContext>,WpfRaddispenserUOW>(
Lifestyle.Scoped);
container
.Register<IUnitOfWorkFactory<WpfRaddispenserDbContext>,WpfRaddispenserUOWFactory>();
//Command registration
container.Register<
IDatabaseCommand<DatabaseResult,BusinessCommandParams1>,BusinessCommand1>();
//Command Decorator registration
container.RegisterDecorator(
typeof(IDatabaseCommand<DatabaseResult,BusinessCommandParams1>),typeof(TransactionDatabaseCommandDecorator),Lifestyle.Singleton);
我正确接收了var transactionCommandHandler =
_container.GetInstance<IDatabaseCommand<DatabaseResult,BusinessCommandParams1>>();
usecase.Execute(new BusinessCommandParams1());
的实例,但是当我尝试从工厂获取实例时,我会收到此错误
SimpleInjector.ActivationException:WpfRaddispenserUOW使用“作用域”生活方式注册,但是在活动(作用域)作用域的上下文之外请求实例。有关如何应用生活方式和管理范围的更多信息,请参见https://simpleinjector.org/scoped。
TransactionDatabaseCommandDecorator
这里的问题是我想使用由他的装饰器创建和控制的dbcontext。 但是构造函数注入是由容器处理的,所以我如何在命令内部注入由装饰器创建的上下文?
基本上我想拥有类似命令修饰器的东西
in SimpleInjector.Scope.GetScopelessInstance(ScopedRegistration registration)
in SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration registration,Scope scope)
in SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
in WpfRaddispenser.DataLayer.Decorator.TransactionDatabaseCommandDecorator.Execute(BusinessCommandParams1 commandParam) in C:\Work\Git\AlphaProject\WpfRaddispenser\WpfRaddispenser.DataLayer\Decorator\TransactionDatabaseCommandDecorator.cs: riga 29
in WpfRaddispenser.Program.Main() in C:\Work\Git\AlphaProject\WpfRaddispenser\WpfRaddispenser\Program.cs: riga 47
但由DI和Simple Injector制成
在我的设计上可能有一个问题或几个问题,但是我是DI的新手,我想更好地了解它们的工作原理。
回顾一下,我需要使用很多命令数据库,其中每个命令必须具有隔离的上下文,并且事务的功能必须由装饰器内部的额外层控制。
解决方法
问题是由流动/关闭范围和环境范围的混合引起的。由于您正在编写WPF应用程序,因此您选择使用Simple Injector的Flowing范围功能。这样,您就可以直接从作用域解析实例(例如,调用Scope.GetInstnace
)。
但是,它不与环境范围界定混合使用,就像AsyncScopedLifestyle.BeginScope
那样。
要解决此问题,您必须将装饰器的实现更改为以下内容:
public class TransactionDatabaseCommandDecorator
: IDatabaseCommand<DatabaseResult,BusinessCommandParams1>
{
private readonly Container _container;
private readonly Func<IDatabaseCommand<DatabaseResult,BusinessCommandParams1>>
_decorateeFactory;
public TransactionDatabaseCommandDecorator(
Container container,Func<IDatabaseCommand<DatabaseResult,BusinessCommandParams1>> decorateeFactory)
{
_container = container;
_decorateeFactory = decorateeFactory;
}
public DatabaseResult Execute(BusinessCommandParams1 commandParam)
{
DatabaseResult res;
using (Scope scope = new Scope(_container))
{
var command = _decorateeFactory.Invoke(scope);
var factory = scope
.GetInstance<IDesignTimeDbContextFactory<WpfRadDispenserDbContext>>();
...
}
return res;
}
}
请注意以下有关上述装饰器的内容:
- 它已注入
Func<Scope,T>
工厂。该工厂将使用提供的Scope
创建被装饰者。 - execute方法现在使用
Scope
创建一个新的new Scope(Container)
,而不是依赖于AsyncScopedLifestyle
的环境范围。 -
Func<Scope,T>
工厂随创建的作用域一起提供。 -
IDesignTimeDbContextFactory<T>
是通过Scope
实例解析的,而不是使用Container
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。