从通用类继承

时间:2019-02-18 07:00:10

标签: c# entity-framework asp.net-core-2.1

我有.net core 2.1项目。还有我的存储库类,如下所示。但是由于MyDbContext构造函数具有参数,因此出现如下错误。当我删除JwtHelper参数时,它运行良好。 但是,我需要在JwtHelper中添加MyDbContext.cs来记录审核。如何实现?

  

“ MyDbContext”必须是具有公共无参数构造函数的非抽象类型,以便在通用类型或方法“ UnitOfWork”中将其用作参数“ TContext”

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 :(得分:2)

问题出在您的UnitOfWork的构造函数中:

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

这里您使用默认构造函数构造了类MyDbContext的新对象,但是MyDbContext没有默认构造函数。

您决定使您的UnitOfWork非常通用。太好了,因为这使您可以将我们的UnitOfWork与各种DbContexts一起使用。您告诉UnitOfWork的唯一限制是DbContext应该具有默认构造函数。

一个好的方法是自己创建工厂并将其传递给UnitOfWork。

如果您不希望或不能给MyDbContext一个默认的构造函数,请考虑告诉您的UnitOfWork如何创建一个构造函数:“嘿,工作单元,如果您需要创建一个我要您使用的DbContext,请使用此功能”

实际上,您将使用factory design pattern

老式的接口方法

步骤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。

Lambda表达式

您实际上不必实现接口。您的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();称为lambda表达式。这意味着:创建一个没有输入参数(即()部分)和等于factory.Create()的双精度返回值的函数。

摆脱话题:Lambda表达式的解释

类似地,如果您需要创建一个lambda表达式来表示一个函数,该函数的输入参数为Rectangle并作为矩形的表面输出:

Func<Rectangle, double> myFunc = (rectangle) => rectangle.X * rectangle.Y;

用词来说:myFunc是一个函数,其输入为Rectangle,输出为double。功能类似于:

double MyFunc (Rectangle rectangle)
{
    return rectangle.X * rectangle.Y;
}

您这样称呼它:

Func<Rectangle, double> calcSurface = (rectangle) => rectangle.X * rectangle.Y;
Rectangle r = ...;
double surface = calcSurface(r);

同样,一个lambda表达式表示具有两个输入参数和一个输出参数的函数:

Func<double, double, Rectangle> createRectangle = 
    (width, height) => new Rectangle {Width = width, Height = height};

Func <...,...,...,x>的最后一个参数始终是返回值

为了完整起见:具有空返回值的方法称为Action:

Action(Rectangle) displayRectangle = (r) => this.Form.DrawRectangle(r);

答案 1 :(得分:1)

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>;将IJwtHelper添加为构造函数参数

  • TContext类的构造函数中实例化UnitOfWork<TContext>后,请通过public属性分配IJwtHelper