Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

model view controller - EF Core 5.0 How to manage multiple entity class with one generic repository

First question here, I hope I'm doing it right.

I'm using Entity Framework Core 5.0 (Code First) with an onion architecture (data/repo/service/mvc) and so I have a service for each table (almost). It's work well but now I need to manage (get, insert, update, delete) about 150 tables which all have the same structure (Id, name, order).

I have added each of them as Entity class and their DbSet too in my DbContext, but I don't want to make 150 services, I would like to have a generic one .

How can I bind it to my generic repository ?

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);
    }
   [...]
}

In an ideal world, would like to have something like this :

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);
}

But I can try to get type from DbSet string Name too, I just managed to retrieve some data :

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

I've tried the following method (2.0 compatible apparently) to get the good DbSet, not working neither (no Query) : https://stackoverflow.com/a/48042166/10359024

What am I missing?

Thanks a lot for your help

question from:https://stackoverflow.com/questions/65850560/ef-core-5-0-how-to-manage-multiple-entity-class-with-one-generic-repository

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Not sure why you need to get type?

You can use something like this.

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);
}

Then you can use either one of these 2 options you want

  1. Multiple repositories following your tables

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. If you are lazy to create multiple repositories, can use this way also. Your service just simple call Repository with entity name.

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;
    }
}

Finally, you can call service like this. You can use multiple services instead of BaseService if you want.

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

I suggest to use first option (multiple repositories) because you may need to customise functions in own repository in future. And create service class following your controller name. For example, you have HomeController, UserController, etc. Create HomeService, UserService and link them with BaseService so that you can create customised functions in their own service class.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...