.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