EF Core 5.0 如何使用一个通用存储库管理多个实体类

问题描述

一个问题,我希望我做对了。

我使用 Entity Framework Core 5.0(代码优先)和洋葱架构(data/repo/service/mvc),所以我为每个表提供了一个服务(几乎)。 它运行良好,但现在我需要管理(获取、插入、更新、删除)大约 150 个具有相同结构(ID、名称、顺序)的表。

我已经在我的 DbContext 中添加了它们中的每一个作为实体类和它们的 DbSet,但我不想制作 150 个服务,我想要一个通用的。

如何将其绑定到我的通用存储库?

public class Repository<T> : IRepository<T> where T : BaseEntity
{
    private readonly ApplicationContext context;
    private DbSet<T> entities;
    private readonly RepositorySequence repoSequence;
            
    private string typeName { get; set; }
            
    public Repository(ApplicationContext context)
    {
        this.context = context;
        entities = context.Set<T>();
        this.repoSequence = new RepositorySequence(context);
            
        this.typeName = typeof(T).Name;
    }
            
    public T Get(long plng_Id)
    {
        return entities.SingleOrDefault(s => s.Id == plng_Id);
    }
   [...]
}

在理想的世界中,希望有这样的东西:

public async Task Insert(dynamic pdyn_Entity)
{
    Type DynamicType = Type.GetType(pdyn_Entity);
    Repository<DynamicType> vobj_Repo = new Repository<DynamicType>(mobj_AppContext);
    long Id = await vobj_Repo.InsertAsync(pdyn_Entity);
}

但是我也可以尝试从 DbSet 字符串 Name 中获取类型,我只是设法检索了一些数据:

public IEnumerable<object> GetAll(string pstr_DbSetName)
{
     return ((IEnumerable<BaseEntity>)typeof(ApplicationContext).GetProperty(pstr_DbSetName).GetValue(mobj_AppContext,null));
 }

我尝试了以下方法(显然与 2.0 兼容)来获得良好的 DbSet,但两者都不起作用(无查询):https://stackoverflow.com/a/48042166/10359024

我错过了什么?

非常感谢您的帮助

解决方法

不确定为什么需要输入类型?

你可以使用这样的东西。

Repository.cs

public class Repository<T> : IRepository<T> where T : BaseEntity
{
    private readonly ApplicationContext context;
    private DbSet<T> entities;

    public Repository(ApplicationContext context)
    {
        this.context = context;
        entities = context.Set<T>();
    }
    
    public List<T> Get()
       => entities.ToList();

    public T Get(long plng_Id)
       => entities.Find(plng_Id);
    
    public long Save(T obj)
    { 
        if (obj.ID > 0)
           entities.Update(obj);
        else
           entities.Add(obj);

        return obj.ID;
    }

    public void Delete(T obj)
       => entities.Remove(obj);
}

然后您可以使用这两个选项中的任何一个

  1. 多个存储库跟随您的表格

UserRepository.cs

public class UserRepository : Repository<User> : IUserRepository
{
    private readonly ApplicationContext context;
    public UserRepository(ApplicationContext context)
    {
        this.context = context;
    }
}

BaseService.cs

public class BaseService : IBaseService
{
    private readonly ApplicationContext context;
    private IUserRepository user;
    private IRoleRepository role;
    public IUserRepository User { get => user ??= new UserRepository(context); }
    public IRoleRepository Role { get => user ??= new RoleRepository(context); }

    public BaseService(ApplicationContext context)
    {
        this.context = context;
    }
}
  1. 如果你懒得创建多个仓库,也可以使用这种方式。您的服务只是简单地使用实体名称调用 Repository。

BaseService.cs

public class BaseService : IBaseService
{
    private readonly ApplicationContext context;
    private IRepository<User> user;
    private IRepository<Role> role;
    public IRepository<User> User { get => user ??= new Repository<User>(context); }
    public IRepository<Role> Role { get => role ??= new Repository<Role>(context); }

    public BaseService(ApplicationContext context)
    {
        this.context = context;
    }
}

最后,您可以像这样调用服务。如果需要,您可以使用多个服务而不是 BaseService。

HomeController.cs

public class HomeController : Controller
{
   private readonly IBaseService service;
   public HomeController(IBaseService service)
   {
       this.service = service;
   }

   public IActionResult Index()
   {
       var user = service.User.Get();
       return View(user);
   }

   public IActionResult Add(User user)
   {
       var id = service.User.Save(user);
       return View();
   }
}

我建议使用第一个选项(多个存储库),因为您将来可能需要在自己的存储库中自定义功能。并按照您的控制器名称创建服务类。比如你有 HomeController、UserController 等,创建 HomeService、UserService 并将它们与 BaseService 链接起来,这样你就可以在它们自己的服务类中创建自定义函数。

,

我假设你有一个这样的基本实体:

public class BaseEntity 
    {
        [Key]
        public int Id { get; set; }

        public string Name { get; set; }

        public string Order { get; set; }
    }

然后你可以像这样在你的通用存储库中执行 CRUD 操作:

public int Create(T item)
    {
        if (item == null) return 0;
        entities.Add(item);////SaveChanges    
        return item.Id;
    }

public void Update(T updatedItem)
    {
      context.SetModified(updatedItem);//SaveChanges    
    }

public IQueryable<T> All()
    {
        return entities();
    }

并且在每个方法中,您都可以访问 BaseEntity 中的 3 个公共字段

,

感谢大家的回复。

我需要有类型,因为我使用的是自动绑定到这些表的 blazor 组件。该组件将所需实体类的名称(以字符串形式)作为参数。感谢@Asherguru 的回应,我找到了一种方法:

1 - 我创建了一个“SedgmentEntity”类:

public abstract class SegmentEntity : ISegmentEntity
{
    public abstract long Id { get; set; }

    public abstract string Name { get; set; }

    public abstract short? Order { get; set; }
}

2 - 通过反射输入的 SegmentRepository:

public class SegmentRepository : ISegmentRepository
{
    private readonly ApplicationContext context;
    private readonly RepositorySequence repoSequence;
    
    public SegmentRepository(ApplicationContext context)
    {
        this.context = context;
        this.repoSequence = new RepositorySequence(context);
    }
    
    public async Task<long> Insert(string pstr_EntityType,SegmentEntity pobj_Entity)
    {
            Type? vobj_EntityType = Assembly.GetAssembly(typeof(SegmentEntity)).GetType("namespace.Data." + pstr_EntityType);
        
            if (vobj_EntityType != null)
            {
                // create an instance of that type
                object vobj_Instance = Activator.CreateInstance(vobj_EntityType);
        
                long? nextId = await repoSequence.GetNextId(GetTableName(vobj_EntityType));
        
                if (nextId == null)
                {
                    throw new TaskCanceledException("Sequence introuvable pour " + vobj_EntityType.FullName);
                }
        
                PropertyInfo vobj_PropId = vobj_EntityType.GetProperty("Id");
                vobj_PropId.SetValue(vobj_Instance,nextId.Value,null);
        
                PropertyInfo vobj_PropName = vobj_EntityType.GetProperty("Name");
                vobj_PropName.SetValue(vobj_Instance,pobj_Entity.Name,null);
        
                PropertyInfo vobj_PropOrder = vobj_EntityType.GetProperty("Order");
                vobj_PropOrder.SetValue(vobj_Instance,pobj_Entity.Order,null);
        
                return ((SegmentEntity)context.Add(vobj_Instance).Entity).Id;
            }
        }
    
    public IEnumerable<object> GetAll(string pstr_EntityType)
    {
        Type? vobj_EntityType = Assembly.GetAssembly(typeof(SegmentEntity)).GetType("namespace.Data." + pstr_EntityType);
    
        if (vobj_EntityType != null)
        {
            PropertyInfo vobj_DbSetProperty = typeof(ApplicationContext).GetProperties().FirstOrDefault(prop =>         
            prop.PropertyType.FullName.Contains(vobj_EntityType.FullName));
            return (IEnumerable<object>)vobj_DbSetProperty.GetValue(context,null);
        }
        return null;
    }
}

我仍然需要处理 Get 和 Delete 函数,但应该没问题。 然后我将能够创建一个服务,该服务将由我的组件调用。

再次感谢!

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...