继承自泛型 class

Inherit from generic class

我有 .net core 2.1 项目。我的存储库 类 如下所示。但是因为 MyDbContext 构造函数有参数,我得到如下错误。当我删除 JwtHelper 参数时,它运行良好。 但是,我需要在 MyDbContext.cs 中添加 JwtHelper 来记录审核。 我怎样才能做到这一点?

'MyDbContext' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TContext' in the generic type or method '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();
    }
}

我UnitOfWork.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)); } }
}

我MyRepos.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;
    }
}

我UserRepository.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>();
}

new() 约束需要无参数构造函数;但是,由于您在 DbContext 中需要 IJwtHelper,而 属性 仅存在于 MyDbContext 中,您可以创建自己的基础 class 以从中派生其他上下文而不是 DbContext:

public class MyDbContextBase : DbContext
{
    public IJwtHelper JwtHelper { get; set; } 
} 
  • MyDbContext中删除IJwtHelper属性;删除构造函数;让它继承 MyDbContextBase 而不是 DbContext

  • IUnitOfWork<U>接口上的U约束改为MyDbContextBase

  • UnitOfWork<TContext> class 上将 TContext 约束从 DbContext 更改为 MyDbContextBase;添加 IJwtHelper 作为构造函数参数

  • UnitOfWork<TContext>class的构造函数中实例化TContext后,通过public[=55]赋值IJwtHelper =].

问题出在你的 UnitOfWork:

的构造函数中
public UnitOfWork()
{
    DataContext = new TContext();
}

此处使用默认构造函数构造class MyDbContext的新对象,但MyDbContext没有默认构造函数。

您决定让您的 UnitOfWork 非常通用。这很好,因为这使您能够将我们的 UnitOfWork 与各种 DbContexts 一起使用。你告诉你的 UnitOfWork 的唯一限制是你的 DbContext 应该有一个默认构造函数。

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

如果您不想或不能给 MyDbContext 默认构造函数,请考虑告诉您的 UnitOfWork 如何创建一个:"Hey unit of work, if you need to create the DbContext that I want you to use, use this function"

事实上,您将使用 factory design pattern

老式的界面方法

第 1 步:使用函数 Create() 创建一个 class,它将准确创建您要使用的 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().

的双 return 值

有点跑题了: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> 的最后一个参数始终是 return 值

为了完整起见:具有 void return 的方法称为 Action:

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