Шаблон сканера сборки Autofac - пользовательский атрибут - PullRequest
0 голосов
/ 24 июня 2018

Я хотел бы использовать шаблон сканера сборки и зарегистрировать класс с атрибутом из другой сборки

Проект: AssemblyScanner

using System;

namespace AssemblyScanner
{
    public class RegisterScope : Attribute
    {
        public RegisterScope()
        {

        }
    }
}

Проект: Domian.Service

namespace Domain.Service.Test
{
    [RegisterScope]
    public class CarService
    {
    }
}

Project: UnitTests

using AssemblyScanner;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Domain.Service.Test;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Xunit;

namespace UnitTests
{
    public class AssemblyScannerTests
    {
        [Fact]
        public void AssemblyScannerTest()
        {
            var t = AssemblyScannerPattern().GetService<CarService>();

            //AssemblyScannerPattern -> https://stackoverflow.com/questions/33811015/autofac-how-to-load-assemblies-that-are-referenced-but-not-directly-used
            //Other Example -> https://www.codeproject.com/Articles/1201502/Dependency-Injection-in-ASP-NET-Web-API-using-Auto
        }

        public AutofacServiceProvider AssemblyScannerPattern()
        {
            var serviceCollection = new ServiceCollection();

            ContainerBuilder builder = new ContainerBuilder();

            string[] assemblyScanerPattern = new[] { @"Domain.Service*.dll" };

            // Make sure process paths are sane...
            Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

            //  begin setup of autofac >>

            // 1. Scan for assemblies containing autofac modules in the bin folder
            List<Assembly> assemblies = new List<Assembly>();
            assemblies.AddRange(
                Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.dll", SearchOption.AllDirectories)
                         .Where(filename => assemblyScanerPattern.Any(pattern => Regex.IsMatch(filename, pattern)))
                         .Select(Assembly.LoadFrom)
                );

            foreach (var assembly in assemblies)
            {
                builder.RegisterAssemblyTypes(assembly)
                    .AsImplementedInterfaces();
            }

            foreach (var assembly in assemblies)
            {
                foreach (var attributeClass in assembly.ExportedTypes)
                {
                    foreach (var registerScope in attributeClass.CustomAttributes.Where(s => s.AttributeType.Name.Contains("RegisterScope")))
                    {
                          var importedClassFromAssembly = GetInstance(attributeClass.Namespace + "." + attributeClass.Name);

                          //builder.RegisterType<importedClassFromAssembly.GetType>().As(importedClassFromAssembly);
                    }
                }

            }

            var container = builder.Build();

            var serviceProvider = new AutofacServiceProvider(container);

            return serviceProvider;
        }

        public object GetInstance(string strFullyQualifiedName)
        {
            Type type = Type.GetType(strFullyQualifiedName);
            if (type != null)
                return Activator.CreateInstance(type);
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = asm.GetType(strFullyQualifiedName);
                if (type != null)
                    return Activator.CreateInstance(type);
            }
            return null;
        }
    }
}

Я нашел класс с атрибутом "RegisterScope", но у меня возникла проблема с builder.RegisterType

enter image description here

И здесь я получаю ноль, но я хотел бы получить экземпляр класса

enter image description here

Обновление

Это`Когда я пишу это

builder.RegisterInstance(importedClassFromAssembly).As<CarService>();

, я получаю правильный объект, но я хотел бы что-то вроде этого - Ошибка

builder.RegisterInstance(importedClassFromAssembly).As<importedClassFromAssembly.GetType>();

Я также пытаюсь это сделать (но это дает мне нольв GetService ();)

builder.RegisterInstance(importedClassFromAssembly).As<dynamic>();

Текущий код с последней небольшой проблемой

using AssemblyScanner;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Domain.Service.Test;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Xunit;

namespace UnitTests
{
    public class AssemblyScannerTests
    {
        [Fact]
        public void AssemblyScannerTest()
        {
            var t = AssemblyScannerPattern().GetService<CarService>();

            //AssemblyScannerPattern -> https://stackoverflow.com/questions/33811015/autofac-how-to-load-assemblies-that-are-referenced-but-not-directly-used
            //Other Example -> https://www.codeproject.com/Articles/1201502/Dependency-Injection-in-ASP-NET-Web-API-using-Auto
        }

        public AutofacServiceProvider AssemblyScannerPattern()
        {
            var serviceCollection = new ServiceCollection();

            ContainerBuilder builder = new ContainerBuilder();

            string[] assemblyScanerPattern = new[] { @"Domain.Service*.dll" };

            Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

            List<Assembly> assemblies = new List<Assembly>();
            assemblies.AddRange(
                Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.dll", SearchOption.AllDirectories)
                         .Where(filename => assemblyScanerPattern.Any(pattern => Regex.IsMatch(filename, pattern)))
                         .Select(Assembly.LoadFrom)
                );

            foreach (var assembly in assemblies)
            {
                foreach (var attributeClass in assembly.ExportedTypes)
                {
                    if(attributeClass.CustomAttributes.Where(s => s.AttributeType.Name.Contains("RegisterScope")).Any())
                    {
                        var importedClassFromAssembly = GetInstance(attributeClass.FullName);

                        builder.RegisterInstance(importedClassFromAssembly).As<CarService>();
                    }
                }
            }

            var container = builder.Build();

            var serviceProvider = new AutofacServiceProvider(container);

            return serviceProvider;
        }

        public object GetInstance(string strFullyQualifiedName)
        {
            Type type = Type.GetType(strFullyQualifiedName);
            if (type != null)
                return Activator.CreateInstance(type);
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = asm.GetType(strFullyQualifiedName);
                if (type != null)
                    return Activator.CreateInstance(type);
            }
            return null;
        }
    }
}

1 Ответ

0 голосов
/ 25 июня 2018

Если я понимаю ваш пример, вы сможете сделать это следующим образом:

foreach (var assembly in assemblies) {
    foreach (var attributeClass in assembly.ExportedTypes) {
          if (attributeClass.CustomAttributes.Any(s => s.AttributeType.Name.Contains("RegisterScope"))) {
              builder.RegisterType(attributeClass).AsSelf();
          }
     }
}

Autofac выполнит инстанцирование для вас, вы можете добавить «.SingleInstance ()», чтобы убедиться, чтопредоставляется только один экземпляр.

Другим решением, с моей точки зрения, было бы лучше, было бы использовать сборку сканирования самого Autofac:

foreach (var assembly in assemblies) {
    builder
        .RegisterAssemblyTypes(assembly)
        .Where(t => t.CustomAttributes.Any(s => s.AttributeType.Name.Contains("RegisterScope")))
        .AsSelf();
}

Подробнее здесь: http://autofaccn.readthedocs.io/en/latest/register/scanning.html

Некоторые подсказки:

  • Если возможно использовать интерфейсы для классов, которые вы регистрируете, вы можете использовать ".AsImplementedInterfaces ()"
  • Использовать модули Autofac для большей гибкости, это может толькоработать, если у вас есть доступ к коду используемых вами сборок.Подробнее здесь: http://autofaccn.readthedocs.io/en/latest/register/scanning.html#scanning-for-modules

Надеюсь, это поможет.

...