其他程序集的依赖项注册器

本文关键字:注册 依赖 程序集 其他 | 更新日期: 2023-09-27 18:37:26

>我有一个关于依赖注册的简单问题。

我正在开发一个全新的 Web 应用程序,该应用程序将引擎上下文范式与 Autofac 容器一起使用。对于解决方案上的任何库,我有一个实现IDependencyRegistrar实现通用Register方法的类,因为添加一个容器,一些接口和组件的特定实现。

通过这种方式,基本核心库(在应用程序启动时运行)提供了一种RegisterDependencies方法,该方法查找每个正在执行的程序集,以发现应用程序使用的所有 DDL 并将其注册到 Autofac 容器上。

提供此行为的代码是:

 builder = new ContainerBuilder();
        var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
        var drInstances = new List<IDependencyRegistrar>();
        foreach (var drType in drTypes)
            drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
        //sort
        drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
        foreach (var dependencyRegistrar in drInstances)
            dependencyRegistrar.Register(builder, typeFinder, config);
        builder.Update(container);

由于这样的方法实现,FindClassOfType<IDependencyRegistrar>工作的地方:

        public virtual IList<Assembly> GetAssemblies()
    {
        var addedAssemblyNames = new List<string>();
        var assemblies = new List<Assembly>();
        if (LoadAppDomainAssemblies)
            AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
        AddConfiguredAssemblies(addedAssemblyNames, assemblies);
        return assemblies;
    }

而且,AddAssemblyInAppDomain是:

private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
    {
        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            if (Matches(assembly.FullName))
            {
                if (!addedAssemblyNames.Contains(assembly.FullName))
                {
                    assemblies.Add(assembly);
                    addedAssemblyNames.Add(assembly.FullName);
                }
            }
        }
    }

问题是:当我最终在mysolution中添加MVC项目(前端)时,我只引用了直接访问库(服务层和一些基础设施组件),但没有数据层组件和其他一些DLL。由于 MVC 没有直接引用一些深层库,我的引擎上下文看不到其他子组件,也没有在 Autofac 容器上注册它们,从而导致

"没有注册服务"

执行时异常 对它们发出显式请求。

如果我从 MVC 项目中添加对任何库的引用,整个系统就可以工作,但对于分层架构应用程序,这不是最佳实践:我的 MVC 不需要了解 DataLayer 或其他低层服务。

但是,通过这种方式,不会发现执行程序集,因此不再注册依赖项。

在不直接从主 MVC 项目引用所有程序集的情况下,是否是解决此问题的最佳方法?

其他程序集的依赖项注册器

您尝试执行的操作在 Autofac 文档中描述为程序集扫描,请查看此处。基本上,若要获取 IIS 承载的应用程序中的所有程序集,您需要这段代码:

var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();

编辑:

好的,所以我理解情况是这样的:

Project Web 是一个 MVC Web 应用程序。

项目模型是一个类库,您可以在其中定义合约(接口),例如,用于 DAL,也用于 Web

DAL项目包含模型中的契约的一些实现。

可能还有一些额外的类库,但它们都使用协定模型。

所以总结一下 - 所有项目都有对模型的引用,但它们彼此之间没有引用。

我认为对于每个库(模型除外),您应该创建一个模块。为此,请从库中创建一个实现Module类型的类Autofac并覆盖Load方法 - 将所有模块注册放在那里。然后,在 Web 应用启动中,应加载所有程序集并注册其模块。但是,正如您提到的,bin 目录中不存在 Web 以外的程序集;您应该将它们"手动"复制到那里,例如在"生成后"操作("项目属性"->"生成事件"-">"生成后"操作)中。以下命令应完成工作:

xcopy /Y "$(TargetDir)*.dll" "$(ProjectDir)..'{Your Web App}'bin"

此外,在解决方案属性中,应设置该 Web 项目"依赖于"所有其他项目。它将确保所有其他库将在Web之前构建。它不会在这些程序集之间添加任何引用。

然后,在

应用程序启动期间,您应该在 bin 文件夹中搜索程序集并注册每个程序集模块,如下所示:

var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterControllers(typeof(MvcApplication).Assembly);
var libFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/bin"));
var libFiles = libFolder.GetFiles("*.dll", SearchOption.AllDirectories);
foreach (var lib in libFiles)
{
     var asm = Assembly.LoadFrom(lib.FullName);
     containerBuilder.RegisterAssemblyModules(asm);
}
var container = containerBuilder.Build();

您可能希望向libFolder.GetFiles()添加一些筛选器,以便仅检索程序集,而不是全部从 bin 中检索。

如果其他程序集包含 Mvc 控制器,则应在此处查看如何管理这种情况(请参阅Initializer类)。基本上,在应用程序的预启动中,您需要将程序集添加到BuildManager。否则,上面的代码应该可以正常工作。

如果您正在从事非 Web 项目,那么我的答案可能会有所帮助?

在您的 Ioc 类中添加一个方法,即:

    public static void SetIocForTesting(bool forUnitTesting)
    {
        _testContext = forUnitTesting;
    }

示例容器设置代码,将加载程序集的责任委托给生成器。 即 GetModules():

    public static IContainer Container
    {
        get
        {
            if (_container != null)
            {
                return _container;
            }
            var builder = new ContainerBuilder();
            foreach (var lib in GetModules())
            {
                builder.RegisterAssemblyModules(lib);
            }
            _container = builder.Build();
            return _container;
        }
    }

扫描程序集时,打开 testContext 变量:

    private static IEnumerable<Assembly> GetModules()
    {
        if (_testContext)
        {
            return AppDomain.CurrentDomain.GetAssemblies();
        }
        var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        if (currentPath == null)
        {
            throw new NullReferenceException("Unable to build the container because currentPath variable is null.");
        }
        // XXXX = assign a wild card
        var libFolder = new DirectoryInfo(currentPath);
        var libFiles = libFolder.GetFiles("XXXX.*.dll", SearchOption.TopDirectoryOnly);
        return libFiles.Select(lib => Assembly.LoadFrom(lib.FullName)).ToList();
    }

对 IoC 提供程序和注册进行单元测试时:

    protected virtual void GivenThat()
    {
        IocProvider.SetIocForTesting(true);
    }

.. 您有一个切换 IoC 的方法,以确保它与您的测试项目引用和加载的所有程序集一起正常工作。上面的方法位于我用于 BDD 样式单元测试的抽象基类中。

测试项目通常最终会引用大量程序集,这意味着解析服务的成功率更高。

最后,对于非单元测试代码,添加一个静态构造函数:

static IocProvider()
{
    _testContext = false;
}

这将确保生产代码的默认工作流。

随意使用上述格式以满足您的需求;我希望它能像上述问题和答案帮助我一样帮助某人。