BusinessAction 用于表示可由用戶執行的操作。每個操作都與特定物體相關,因此,例如,如果該物體是 Order,則業務操作可能是 CancelOrder、IssueRefund 等。
public abstract class BusinessAction<T>
{
public Guid Id { get; init; }
public Func<T, bool> IsEnabledFor { get; init; }
}
public class CancelOrderAction : BusinessAction<Order>
{
public CancelOrderAction ()
{
Id = Guid.Parse("0e07d05c-6298-4c56-87d7-d2ca339fee1e");
IsEnabledFor = o => o.Status == OrderStatus.Active;
}
}
然后我需要對與特定型別相關的所有操作進行分組。
public interface IActionRegistry
{
Task<IEnumerable<Guid>> GetEnabledActionIdsForAsync(Guid entityId);
}
public class ActionRegistry<T> : IActionRegistry
where T : BaseEntity
{
private readonly IEnumerable<BusinessAction<T>> _actions;
private readonly IRepository<T> _repository;
public ActionRegistry(IEnumerable<BusinessAction<T>> actions, IRepository<T> repository)
{
_actions = actions;
_repository = repository;
}
public async Task<IEnumerable<Guid>> GetEnabledActionIdsForAsync(Guid entityId)
{
var entity = await _repository.FindByIdAsync(entityId);
return entity == null
? Enumerable.Empty<Guid>()
: _actions.Where(a => a.IsEnabledFor(entity)).Select(a => a.Id);
}
}
最后,有一個 API 端點接收物體型別(一些稍后映射到真實 .NET 型別的列舉)和物體的 ID。API 端點負責回傳為物體的當前狀態啟用的操作 ID。
public class RequestHandler : IRequestHandler<Request, IEnumerable<Guid>>>
{
private readonly Func<Type, IActionRegistry> _registryFactory;
public RequestHandler(Func<Type, IActionRegistry> registryFactory)
{
_registryFactory = registryFactory;
}
public async Task<IEnumerable<Guid>> Handle(Request request, CancellationToken cancellationToken)
{
var type = request.EntityType.GetDotnetType();
var actionRegistry = _registryFactory(type);
var enabledActions = await actionRegistry.GetEnabledActionIdsForAsync(request.EntityId);
return enabledActions;
}
}
問題是:如何在 ASP.NET 中配置依賴注入容器(使用默認選項或 Autofac),以便可以決議 Func<Type, IActionRegistry>?
對于ActionRegistry<T>我想我可以做的引數:
builder.RegisterAssemblyTypes().AsClosedTypesOf(typeof(BusinessAction<>));
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
但是,我如何配置Func<Type, IActionRegistry>以便能夠自動連接對Orderwith的請求ActionRegistry<Order>?有沒有辦法做到這一點,或者我需要通過撰寫一些基于型別的 switch 陳述句(以及它看起來如何)來手動配置工廠?
有沒有更好的方法來實作我在這里需要的東西?最終目標是,一旦我有了運行時型別,我就可以獲得與該型別相關的業務操作串列以及存盤庫(以便我可以從資料庫中獲取物體)。
uj5u.com熱心網友回復:
您嘗試做的事情是可能的,但這不是一件常見的事情,也不是您開箱即用的魔法。您必須撰寫代碼來實作它。
在我開始之前......從未來的角度來看,如果您的重現更小,您可能會更快地獲得幫助并更多地關注您的問題。整體BusinessAction<T>并不是真正需要的;在RequestHandler不需要......說實話,所有你需要瑞普你在做什么是:
public interface IActionRegistry
{
}
public class ActionRegistry<T> : IActionRegistry
{
}
如果其他內容與問題相關,一定要包含它......但在這種情況下,它不是,所以在此處添加它只會使問題更難閱讀和回答。我知道我個人有時會跳過有很多額外內容的問題,因為一天只有這么多小時,你知道嗎?
無論如何,以下是您如何以作業示例形式執行此操作:
var builder = new ContainerBuilder();
// Register the action registry generic but not AS the interface.
// You can't register an open generic as a non-generic interface.
builder.RegisterGeneric(typeof(ActionRegistry<>));
// Manually build the factory method. Going from reflection
// System.Type to a generic ActionRegistry<Type> is not common and
// not directly supported.
builder.Register((context, parameters) => {
// Capture the lifetime scope or you'll get an exception about
// the resolve operation already being over.
var scope = context.Resolve<ILifetimeScope>();
// Here's the factory method. You can add whatever additional
// enhancements you need, like better error handling.
return (Type type) => {
var closedGeneric = typeof(ActionRegistry<>).MakeGenericType(type);
return scope.Resolve(closedGeneric) as IActionRegistry;
};
});
var container = builder.Build();
// Now you can resolve it and use it.
var factory = container.Resolve<Func<Type, IActionRegistry>>();
var instance = factory(typeof(DivideByZeroException));
Assert.Equal("ActionRegistry`1", instance.GetType().Name);
Assert.Equal("DivideByZeroException", instance.GetType().GenericTypeArguments[0].Name);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/403426.html
標籤:
上一篇:格式化Serilog日志
