Autofac模块跨层注册树

本文关键字:注册 模块 Autofac | 更新日期: 2023-09-27 18:30:03

所以我有一个典型的三层应用程序,分层如下

DAL->存储库->业务->Web.UI/API

我一直在阅读这篇关于通过模块集中依赖关系来注册依赖关系的文章。

web层仅引用业务,业务仅引用回购,回购仅引用最低DAL层。在这种拓扑结构中,由于UI/API层对存储库一无所知,也没有对其的引用,因此我无法在UI/API层中注册存储库中的模块。同样,我无法在业务层中注册DAL中存在的模块。我想做的是在最顶层开始注册过程,然后在后续层中引发注册的级联效应。

通常情况下,这看起来像是每个层都暴露一个RegisterAllModules方法,并以某种方式从其下面的层触发RegisterAllModule方法。有这样的操作吗?或者有其他方法可以做到这一点吗?在这一点上,我不知道我是否应该像上面提到的那样推出我自己的逻辑,因为我不知道是否有记录在案的方法来做这样的事情。关于如何最好地在这里前进的想法是我正在寻找的。

谢谢。

Autofac模块跨层注册树

Mmm。。。我不知道下面的回答是否正确,但我将尝试为您提供适合您确切要求的解决方案的工具。

  • 您研究过json/xml模块配置吗?您不需要通过交叉引用来了解程序集,你只需要知道app.config(或web.config)中程序集的名称。例如:你可以在Repo程序集中为Repositories注册一个模块,在Business.dll中为Business services注册一个模件。这完全消除了交叉引用各种程序集的需要(对于模块扫描,你仍然需要对方法调用的引用,但这是意料之中的事)。有关详细信息,请参见此处:http://docs.autofac.org/en/latest/configuration/xml.html#configuring-使用microsoft配置
  • 如果您想强制执行从UI到Repo的任何调用,则可以利用"每个匹配生存期范围的实例"函数(请参阅http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-每个匹配的寿命范围)。您可以使用该注册方法来强制执行工作单元方法。例如:存储库只能在"存储库"LifetimeScope中解析,并且只有业务组件才能打开标记为"储存库"的范围
  • 标记作用域的另一种方法是使用"每个拥有的实例<>"模式。通过这种方式,每个业务服务将需要Owned<存储库>。类似于:

    var builder=new ContainerBuilder();建设者RegisterType();建设者RegisterType().InstancePerOwned();

AFAICT,正确的方法是通过Json/Xml配置引用的模块注册组件,并且每个模块都应该针对特定的LifetimeScopes。当一个类调用底层时,它应该打开一个新的LifetimeScope("底层")。

如果你想得到关于执行战略的建议,我将进一步阐述。

最佳,

Alberto Chiesa

编辑:

我不知道"作文词根"的意思。好吧,谢谢你的信息!我喜欢SIMPLE配置文件(无论是.config文件还是单独的.json或.xml文件),因为我觉得要导入的模块列表通过列表比通过类更简单。但这是意见。没有意见的是,您可以以一种简单且经过测试的方式从"CompositionRoot"程序集未引用的程序集导入模块。

因此,我会为每个组件注册选择模块,但为模块注册选择文本配置文件。YMMV。

现在,让我向您展示一个我在许多实时项目中使用的工作单元模式的示例。

在我们的体系结构中,我们大量使用服务层,它负责打开到数据库的连接,并在完成后处理它们,等等。这是一个比你想要的更简单的设计(我喜欢浅的而不是深的),但概念是一样的。

如果您"不在"服务层(例如,在MVC控制器或UI中),则需要ServiceHandle才能访问服务层。ServiceHandle是唯一知道Autofac并负责服务解析、调用和处理的类。

对服务层的访问是这样完成的:

  • 非服务类只能需要ServiceHandle
  • 调用是通过_serviceHandle.Invoke(Func)完成的
  • Autofac通过构造函数注入注入现成的句柄

这是通过使用BeginLifetimeScope(标记)方法完成的,并以这种方式注册服务(在模块中):

// 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("服务")

因此,一个示例调用是:

... 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");
    }
}

希望它能有所帮助!