EF Core 5.0 如何使用一个通用存储库管理多个实体 class
EF Core 5.0 How to manage multiple entity class with one generic repository
这里是第一个问题,希望我做对了。
我正在使用 Entity Framework Core 5.0(代码优先)和洋葱架构(data/repo/service/mvc),所以我为每个 table(几乎)提供服务。
它运行良好,但现在我需要管理(获取、插入、更新、删除)大约 150 个 table,它们都具有相同的结构(Id、名称、顺序)。
我在我的 DbContext 中添加了它们中的每一个作为实体 class 和它们的 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 字符串名称中获取类型,我只是设法检索了一些数据:
public IEnumerable<object> GetAll(string pstr_DbSetName)
{
return ((IEnumerable<BaseEntity>)typeof(ApplicationContext).GetProperty(pstr_DbSetName).GetValue(mobj_AppContext, null));
}
我尝试了以下方法(显然与 2.0 兼容)来获得良好的 DbSet,但均无效(无查询):
我错过了什么?
非常感谢您的帮助
不确定为什么需要打字?
你可以使用类似这样的东西。
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);
}
然后你可以使用你想要的这两个选项中的任何一个
- 您的表后有多个存储库
用户Repository.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;
}
}
- 如果你懒得创建多个仓库,也可以使用这种方式。您的服务只需使用实体名称简单调用 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();
}
}
我建议使用第一个选项(多个存储库),因为您将来可能需要在自己的存储库中自定义函数。并按照您的控制器名称创建服务 class。比如你有HomeController、UserController等。用BaseService创建HomeService、UserService和link它们,这样你就可以在它们自己的服务中创建自定义功能class.
我假设你有一个像这样的基础实体:
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 组件。该组件将所需实体的名称 class(字符串形式)作为参数。感谢@Asherguru 的回复,我找到了一种方法来做到这一点:
1 - 我做了一个 'SedgmentEntity' Class :
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 函数,但应该没问题。
然后我将能够创建一个将由我的组件调用的服务。
再次感谢!
这里是第一个问题,希望我做对了。
我正在使用 Entity Framework Core 5.0(代码优先)和洋葱架构(data/repo/service/mvc),所以我为每个 table(几乎)提供服务。 它运行良好,但现在我需要管理(获取、插入、更新、删除)大约 150 个 table,它们都具有相同的结构(Id、名称、顺序)。
我在我的 DbContext 中添加了它们中的每一个作为实体 class 和它们的 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 字符串名称中获取类型,我只是设法检索了一些数据:
public IEnumerable<object> GetAll(string pstr_DbSetName)
{
return ((IEnumerable<BaseEntity>)typeof(ApplicationContext).GetProperty(pstr_DbSetName).GetValue(mobj_AppContext, null));
}
我尝试了以下方法(显然与 2.0 兼容)来获得良好的 DbSet,但均无效(无查询):
我错过了什么?
非常感谢您的帮助
不确定为什么需要打字?
你可以使用类似这样的东西。
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);
}
然后你可以使用你想要的这两个选项中的任何一个
- 您的表后有多个存储库
用户Repository.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;
}
}
- 如果你懒得创建多个仓库,也可以使用这种方式。您的服务只需使用实体名称简单调用 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();
}
}
我建议使用第一个选项(多个存储库),因为您将来可能需要在自己的存储库中自定义函数。并按照您的控制器名称创建服务 class。比如你有HomeController、UserController等。用BaseService创建HomeService、UserService和link它们,这样你就可以在它们自己的服务中创建自定义功能class.
我假设你有一个像这样的基础实体:
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 组件。该组件将所需实体的名称 class(字符串形式)作为参数。感谢@Asherguru 的回复,我找到了一种方法来做到这一点:
1 - 我做了一个 'SedgmentEntity' Class :
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 函数,但应该没问题。 然后我将能够创建一个将由我的组件调用的服务。
再次感谢!