嗯...我不知道接下来的反应是否正确,但我将尝试为您提供适合您确切要求的解决方案的工具。
AFAICT,正确的方法是通过 Json/Xml 配置引用的模块注册组件,并且每个模块应针对特定的 LifetimeScopes。当你一个类调用底层时,它应该打开一个新的 LifetimeScope("underlying layer")。
如果您需要有关实施策略的建议,我将进一步详细说明。
最好的,
阿尔贝托·基耶萨
编辑:
我不知道“组成根”的含义。嗯,谢谢你的信息!我喜欢简单的配置文件(无论是 .config 文件还是单独的 .json 或 .xml 文件),因为我觉得要导入的模块列表通过列表比通过类更简单。但这是意见。没有意见的是,您可以以简单且经过测试的方式从“组合根”程序集未引用的程序集中导入模块。
因此,我会为每个组件注册选择模块,但为模块注册选择文本配置文件。YMMV。
现在,让我向您展示一个我在许多实际项目中使用的工作单元模式的示例。
在我们的架构中,我们大量使用服务层,它负责打开与数据库的连接并在完成后处理它们等。它比你所追求的更简单(我更喜欢浅而不是深),但是概念是一样的。
如果您“脱离”了服务层(例如,在 MVC 控制器中或 UI 中),则需要一个 ServiceHandle 才能访问服务层。ServiceHandle 是唯一了解 Autofac 并负责服务解析、调用和处置的类。
对服务层的访问是这样完成的:
- 非服务类可以只需要一个 ServiceHandle
- 调用是通过 _serviceHandle.Invoke(Func) 完成的
- Autofac 通过构造函数注入注入准备使用的句柄。
这是通过使用 BeginLifetimeScope(tag) 方法完成的,并以这种方式注册服务(在模块中):
// register every service except for ServiceBase
Builder.RegisterAssemblyTypes(_modelAssemblies)
.Where(t => typeof(IService).IsAssignableFrom(t) && (t != typeof(ServiceBase)))
.InstancePerDependency();
// register generic ServiceHandle
Builder.RegisterGeneric(typeof(ServiceHandle<>))
.AsSelf()
.AsImplementedInterfaces()
.InstancePerDependency();
并将每个共享资源注册为 InstancePerMatchingLifetimeScope("service")
因此,一个示例调用将是:
... in the constructor:
public YourUiClass(ServiceHandle<MyServiceType> myserviceHandle)
{
this._myserviceHandle = myserviceHandle;
}
... in order to invoke the service:
var result = _myserviceHandle.Invoke(s => s.myServiceMethod(parameter));
这是 ServiceHandle 实现:
/// <summary>
/// Provides a managed interface to access Model Services
/// </summary>
/// <typeparam name="TServiceType">The Type of the parameter to be managed</typeparam>
public class ServiceHandle<TServiceType> : IServiceHandle<TServiceType> where TServiceType : IService
{
static private readonly ILog Log = LogManager.GetLogger(typeof(ServiceHandle<TServiceType>));
private readonly ILifetimeScope _scope;
/// <summary>
/// True if there where Exceptions caught during the last Invoke execution.
/// </summary>
public bool ErrorCaught { get; private set; }
/// <summary>
/// List of the errors caught during execution
/// </summary>
public List<String> ErrorsCaught { get; private set; }
/// <summary>
/// Contains the exception that was thrown during the
/// last Invoke execution.
/// </summary>
public Exception ExceptionCaught { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="scope">The current Autofac scope</param>
public ServiceHandle(ILifetimeScope scope)
{
if (scope == null)
throw new ArgumentNullException("scope");
_scope = scope;
ErrorsCaught = new List<String>();
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <param name="command">
/// Void returning action to be performed
/// </param>
/// <remarks>
/// The implementation simply wraps the Action into
/// a Func returning an Int32; the returned value
/// will be discarded.
/// </remarks>
public void Invoke(Action<TServiceType> command)
{
Invoke(s =>
{
command(s);
return 0;
});
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <typeparam name="T">Type of the data to be returned</typeparam>
/// <param name="command">Action to be performed. Returns T.</param>
/// <returns>A generically typed T, returned by the provided function.</returns>
public T Invoke<T>(Func<TServiceType, T> command)
{
ErrorCaught = false;
ErrorsCaught = new List<string>();
ExceptionCaught = null;
T retVal;
try
{
using (var serviceScope = GetServiceScope())
using (var service = serviceScope.Resolve<TServiceType>())
{
try
{
retVal = command(service);
service.CommitSessionScope();
}
catch (RollbackException rollbackEx)
{
retVal = default(T);
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(rollbackEx);
Log.InfoFormat(rollbackEx.Message);
ErrorCaught = true;
ErrorsCaught.AddRange(rollbackEx.ErrorMessages);
ExceptionCaught = rollbackEx;
DoRollback(service, rollbackEx.ErrorMessages, rollbackEx);
}
catch (Exception genericEx)
{
if (service != null)
{
DoRollback(service, new List<String>() { genericEx.Message }, genericEx);
}
throw;
}
}
}
catch (Exception ex)
{
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(ex);
var msg = (Log.IsDebugEnabled) ?
String.Format("There was an error executing service invocation:\r\n{0}\r\nAt: {1}", ex.Message, ex.StackTrace) :
String.Format("There was an error executing service invocation:\r\n{0}", ex.Message);
ErrorCaught = true;
ErrorsCaught.Add(ex.Message);
ExceptionCaught = ex;
Log.ErrorFormat(msg);
retVal = default(T);
}
return retVal;
}
/// <summary>
/// Performs a rollback on the provided service instance
/// and records exception data for error retrieval.
/// </summary>
/// <param name="service">The Service instance whose session will be rolled back.</param>
/// <param name="errorMessages">A List of error messages.</param>
/// <param name="ex"></param>
private void DoRollback(TServiceType service, List<string> errorMessages, Exception ex)
{
var t = new Task<string>
service.RollbackSessionScope();
}
/// <summary>
/// Creates a Service Scope overriding Session resolution:
/// all the service instances share the same Session object.
/// </summary>
/// <returns></returns>
private ILifetimeScope GetServiceScope()
{
return _scope.BeginLifetimeScope("service");
}
}
希望能帮助到你!