Наследовать от общего класса - PullRequest
0 голосов
/ 18 февраля 2019

У меня есть проект .net core 2.1.И мой репозиторий классов, как показано ниже.Но поскольку в конструкторе MyDbContext есть параметр, я получаю сообщение об ошибке, как показано ниже.Когда я удаляю параметр JwtHelper, он работает отлично. Но мне нужно добавить JwtHelper в MyDbContext.cs для ведения журнала аудита. Как этого добиться?

«MyDbContext» должен быть неабстрактного типа с открытымКонструктор без параметров для использования его в качестве параметра «TContext» в универсальном типе или методе «UnitOfWork»

UnitOfWork.cs

public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext, new()
{ 
    protected readonly DbContext DataContext;

    public UnitOfWork()
    {
        DataContext = new TContext();
    }

    public virtual async Task<int> CompleteAsync()
    {
        return await DataContext.SaveChangesAsync();
    }

    public void Dispose()
    {
        DataContext?.Dispose();
    }
}

IUnitOfWork.cs

public interface IUnitOfWork<U> where U : DbContext
{ 
    Task<int> CompleteAsync();
}

MyRepos.cs

public class MyRepos : UnitOfWork<MyDbContext>, IMyRepos
{
    private IUserRepository userRepo;
    public IUserRepository UserRepo { get { return userRepo ?? (userRepo = new UserRepository(DataContext)); } }
}

IMyRepos.cs

public interface IMyRepos : IUnitOfWork<MyDbContext>
{
  IUserRepository UserRepo { get; }
}

MyDbContext.cs

public class MyDbContext : DbContext
{
    private readonly IJwtHelper jwtHelper;

    public MyDbContext(IJwtHelper jwtHelper) : base()
    {
        this.jwtHelper= jwtHelper;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        var userId=jwtHelper.GetUserId();
        SaveAudits(userId,base.ChangeTracker);
        return (await base.SaveChangesAsync(true, cancellationToken));
    }
}

UserRepository.cs

public class UserRepository : Repository<User>, IUserRepository
{
    private readonly MyDbContext_context;

    public UserRepository(DbContext context) : base(context)
    {
        _context = _context ?? (MyDbContext)context;
    }
}

IUserRepository.cs

public interface IUserRepository : IRepository<User>
{ }

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IJwtHelper, JwtHelper>();
    services.AddScoped<DbContext, MyDbContext>();
    services.AddTransient<IMyRepos, MyRepos>();
}

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Проблема в конструкторе вашего 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);
0 голосов
/ 18 февраля 2019

Ограничение new() требует конструктора без параметров;однако, поскольку вам нужно IJwtHelper в вашем DbContext, а это свойство существует только в MyDbContext, вы можете создать свой собственный базовый класс для получения других контекстов вместо DbContext:

public class MyDbContextBase : DbContext
{
    public IJwtHelper JwtHelper { get; set; } 
} 
  • Удалить свойство IJwtHelper из MyDbContext;удалить конструктор;заставить его наследовать MyDbContextBase вместо DbContext

  • Измените ограничение U на интерфейсе IUnitOfWork<U> на MyDbContextBase

  • Изменить TContext ограничение с DbContext на MyDbContextBase на UnitOfWork<TContext> class;добавьте IJwtHelper в качестве параметра конструктора

  • Как только вы создадите экземпляр TContext в конструкторе класса UnitOfWork<TContext>, присвойте IJwtHelper через открытое свойство.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...