Проблема в конструкторе вашего UnitOfWork
:
public UnitOfWork()
{
DataContext = new TContext();
}
Здесь вы создаете новый объект класса MyDbContext
, используя конструктор по умолчанию, но MyDbContext
не имеет конструктора по умолчанию.
Вы решили сделать свой UnitOfWork
очень универсальным.Это здорово, потому что это позволяет вам использовать наш UnitOfWork
со всеми видами DbContexts
.Единственное ограничение, которое вы сказали вашему UnitOfWork
, заключается в том, что ваш DbContext
должен иметь конструктор по умолчанию.
Хорошим способом было бы создать фабрику самостоятельно и передать ее в UnitOfWork.
Если вы не хотите или не можете дать MyDbContext
конструктор по умолчанию, рассмотрите возможность сообщить UnitOfWork
, как он может его создать: «Привет, единица работы, если вам нужно создать DbContextчто я хочу, чтобы вы использовали, используйте эту функцию "
На самом деле, вы будете использовать шаблон фабричного дизайна
Старомодный метод интерфейса
Шаг 1: Создайте класс с функцией Create()
, которая будет создавать именно тот DbContext, который вы хотите использовать.
interface IDbContextFactory<TContext>
where TContext : DbContext
{
DbContext Create();
}
// The MyDbContextFactory is a factory that upon request will create one MyDbcontext object
// passing the JwtHelper in the constructor of MyDbContext
class MyDbContextFactory : IDbContextFactory<MyDbContext>
{
public IJwthHelper JwtHelper {get; set;}
public MyDbContext Create()
{
return new MyDbContext(this.JwtHelper);
}
DbContext IDbContextFactory<HsysDbContext>.Create()
{
throw new NotImplementedException();
}
}
Шаг 2: сообщите вашему UnitOfWork, как он должен создавать DbContext.
public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
public static IDbContextFactory<TContext> DbContextFactory {get; set;}
protected readonly DbContext DataContext;
public UnitOfWork()
{
this.DataContext = dbContextFactory.Create();
}
...
}
public void ConfigureServices(IServiceCollection services)
{
// create a factory that creates MyDbContexts, with a specific JwtHelper
IJwtHelper jwtHelper = ...
var factory = new MyDbContextFactory
{
JwtHelper = jwtHelper,
}
// Tell the UnitOfWork class that whenever it has to create a MyDbContext
// it should use this factory
UnitOfWork<MyDbContext>.DbContextFactory = factory;
... // etc
}
С этого момента всякий раз, когда создается объект UnitOfWork<MyDbContext>
с использованием конструктора по умолчанию, этот конструктор будет предписывать фабрике создать новый MyDbContext.
Лямбда-выражение
Вам не нужно реализовывать интерфейс.Все, что нужно знать вашему UnitOfWork - это как создать DbContext.
Вместо интерфейса вы можете передать ему Func:
public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
// use this function to create a DbContext:
public static Func<TContext> CreateDbContextFunction {get; set;}
protected readonly DbContext DataContext;
public UnitOfWork()
{
// call the CreateDbContextFunction. It will create a fresh DbContext for you:
this.DataContext = CreateDbContextFunction();
}
}
public void ConfigureServices(IServiceCollection services)
{
// create a factory that creates MyDbContexts, with a specific JwtHelper
IJwtHelper jwtHelper = ...
var factory = new MyDbContextFactory
{
JwtHelper = jwtHelper,
}
// Tell the UnitOfWork class that whenever it has to create a MyDbContext
// it should use this factory
UnitOfWork<MyDbContext>.CreateDbContextFunction = () => factory.Create();
Добавлено после комментария
Часть: () => factory.Create();
вПоследнее утверждение называется лямбда-выражением.Это означает: создать функцию без входных параметров (то есть ()
часть) и двойное возвращаемое значение, равное factory.Create()
.
Бит не по теме: объяснение лямбда-выражения
Аналогичноесли вам нужно создать лямбда-выражение, представляющее функцию с входным параметром Rectangle и в качестве выходной поверхности прямоугольника:
Func<Rectangle, double> myFunc = (rectangle) => rectangle.X * rectangle.Y;
Другими словами, myFunc - это функция, которая имеет Rectangle в качестве входных данных, идвойной как выход.Функция выглядит так:
double MyFunc (Rectangle rectangle)
{
return rectangle.X * rectangle.Y;
}
Вы называете это как:
Func<Rectangle, double> calcSurface = (rectangle) => rectangle.X * rectangle.Y;
Rectangle r = ...;
double surface = calcSurface(r);
Аналогично, лямбда-выражение, представляющее функцию с двумя входными параметрами и одним выходным параметром:
Func<double, double, Rectangle> createRectangle =
(width, height) => new Rectangle {Width = width, Height = height};
Последний параметр Func <..., ..., ..., x> всегда является возвращаемым значением
И для полноты: метод с возвращением void называется Action:
Action(Rectangle) displayRectangle = (r) => this.Form.DrawRectangle(r);