.NET Core (MYSQL) Универсальный репозиторий AutoWiring с использованием Autofac

Я стремлюсь достичь следующего. Я пытаюсь настроить новое решение, используя: MySQL, .NET Core, Autofac, EF Core... используя (универсальный) шаблон репозитория.

В конечном счете, я перейду к проекту с существующей базой данных, поэтому моя цель состоит в том, чтобы как-то использовать (t4) шаблонов и EF для генерации некоторых моделей, тогда это "должно" (известные последние слова) быть настолько простым, насколько это возможно. сделать репо для каждой модели, с которой мне нужно взаимодействовать. (Каждый репо будет просто небольшим легким классом, наследующим базу)

Startup.cs

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        this.Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; private set; }

    // This method gets called by the runtime. 
    // Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.
            .AddDbContext<MyContext>(o => o.UseMySQL(
          Configuration.GetConnectionString("MyConnection")));
    }

    public void ConfigureContainer(ContainerBuilder builder)
    {               
        builder.RegisterGeneric(typeof(Repository<>))
            .As(typeof(IRepository<>))
            .InstancePerLifetimeScope();

       // the below works (before adding the repos)
       builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
           .AssignableTo<IService>()
           .AsImplementedInterfaces()
           .InstancePerLifetimeScope();
    }
}

(Универсальный) Repository.cs

public abstract class Repository<T> : IRepository<T>
    where T : class
{
    private readonly MyContext _context;

    protected Repository(MyContext context)
    {
        _context = context;
    }

    public virtual string Add(T entity)
    {
        if (ValidateAdd(ref entity, out var message))
        {
            _context.Set<T>().Add(entity);
        }

        return message;
    }

    public virtual bool ValidateAdd(ref T entity, out string message)
    {
        message = default(string); 
        return true;
    }

}

Реализация репозитория:

FooRepository.cs

// public interface IFooRepository: IRepository<Foo>

public class FooRepository: Repository<Foo>, IFooRepository
{
    public FooRepository(MyContext context) : base(context)
    {
    }

    public override bool ValidateAdd(ref Foo entity, out string message)
    {
        // just a hook for pre-insert stuff
        message = "All foos shall fail add";
        return false;
    }
}

Тогда использование внутри службы или контроллера, или что у вас.

FooService.cs

public class FooService: IFooService
{
    private readonly IFooRepository _repository;

    public FooService(IFooRepository repository)
    {
        _repository = repository;
    }

    public void DoSomethingThenAdd()
    {
       // some other business logic specific to this service
        _repository.Add(new Foo()
        {
            Id = 1,
            LastName = "Bar",
            Name = "Foo"
        });
    }
}

Проблема:

Как мне все это связать... Я изо всех сил пытался найти правильную документацию по MySQL + Ef, и я как бы продолжаю понимать, что эта часть "работает". Но, как вы можете видеть в журнале ошибок ниже, моя регистрация в репозиториях испорчена.

Ошибка:

Произошла ошибка во время активации определенной регистрации.

Смотрите внутреннее исключение для деталей.

Регистрация: Activator = FooService (ReflectionActivator), Services = [MyApp.Services.Interfaces.IFooService, MyApp.Services.Interfaces.IService], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = Shared, Ownership = OwnedByLife

---> Ни один из конструкторов, найденных с помощью 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' для типа 'MyApp.Services.FooService', не может быть вызван с доступными службами и параметрами:

Не удается разрешить параметр "MyApp.Data.Interfaces.IFooRepository репозиторий" конструктора "Void .ctor(MyApp.Data.Interfaces.IFooRepository)". (См. Внутреннее исключение для деталей.)

-> Autofac.Core.DependencyResolutionException: Ни один из конструкторов, найденных с Autofac.Core.Activators.Reflection.DefaultConstructorFinder 'для типа' MyApp.Services.FooService ', не может быть вызван с доступными службами и параметрами:

Не удается разрешить параметр "MyApp.Data.Interfaces.IFooRepository репозиторий" конструктора "Void .ctor(MyApp.Data.Interfaces.IFooRepository)".

1 ответ

Решение

Следующая строка будет регистрироваться Repository<Foo> как IRepository<Foo> в автофаке:

builder.RegisterGeneric(typeof(Repository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

но IRepository<Foo> это не IFooRepository а также FooService нужен IFooRepository, Вот почему Autofac терпит неудачу со следующим сообщением об ошибке:

Не удается разрешить параметр "MyApp.Data.Interfaces.IFooRepository репозиторий" конструктора "Void .ctor(MyApp.Data.Interfaces.IFooRepository)".

Если вы хотите сохранить свой FooRepository а также IFooRepository Вы должны будете зарегистрировать их:

builder.RegisterType<FooRepository>()
       .As<IFooRepository>()

Другим решением было бы зарегистрировать всю реализацию IRepository<>

builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
       .AsClosedTypesOf(typeof(IRepository<>))

а также FooService следует полагаться на Irepository<Foo> вместо IFooRepository

public class FooService: IFooService
{
    private readonly IRepository<Foo> _repository;

    public FooService(IRepository<Foo> repository)
    {
        this._repository = repository;
    }

    // ...
}

Кстати, будьте осторожны со сканированием сборки при использовании IIS: Сканирование сборки - приложение IIS Hosted

Другие вопросы по тегам