D
D
Dmitry2013-05-28 20:51:18
C++ / C#
Dmitry, 2013-05-28 20:51:18

LINQ to Entity Framework - the base class for working with the database? (C#)?

Good day. Most of the time I wrote in PHP, but now it became necessary to deal with ASP.NET MVC
. And the problem I have is the following.
Working with EF, we can easily create such methods in the class:

public class SiteContext : DbContext
    {
        public DbSet<Problem> Problems { get; set; }
    }

    public class BaseDBProxy 
    {
        protected SiteContext db = new SiteContext();
    }

    public class ProblemsProxy : BaseDBProxy
    {
        public Problem GetById(int id)
        {
            return db.Problems.Where(e => e.Id == id).FirstOrDefault<Problem>();
        }
   }

But how to bring more general methods to the BaseDBProxy level? For example, how to do (in pseudocode)?
public class BaseDBProxy 
    {
        protected SiteContext db = new SiteContext();

        public АВТОПОДСТАНОВКА_НУЖНОЙ_МОДЕЛИ GetById(int id)
        {
            return db.АВТОПОДСТАНОВКА_НУЖНОЙ_МОДЕЛИ.Where(e => e.Id == id).FirstOrDefault<[АВТОПОДСТАНОВКА_НУЖНОЙ_МОДЕЛИ]>();
        }
    }

The goal is to bring typical actions to the top level so as not to copy-paste (albeit through templates and auto-generation of code) identical actions. So far I haven't found a way.

Answer the question

In order to leave comments, you need to log in

4 answer(s)
L
lam0x86, 2013-05-29
@Newbilius

Haven't used EF for a long time, but I think it should work:

    public class BaseDBProxy 
    {
        protected SiteContext db = new SiteContext();

        public TEntity GetById<TEntity>(int id)
        {
            return db.Set<TEntity>.Where(e => e.Id == id).FirstOrDefault();
        }
    }

But in general, it is better to move this method to the base class of the repository:
    public abstract class RepositoryBase<TEntity> where TEntity : class
    {
        private Set<TEntity> _set;

        public RepositoryBase(DbContext context)
        {
            _set = context.Set<TEntity>();
        }

        public TEntity GetById(int id)
        {
            return _set.Where(e => e.Id == id).First();
        }

        public TEntity TryGetById(int id)
        {
            return _set.Where(e => e.Id == id).FirstOrDefault();
        }
    }

And in the heirs, you can add methods specific to specific types of entities.

A
Alexey Gagarin, 2014-05-21
@Alexey_Gagarin

Paulskit correctly said that the author is trying to write the Repository pattern. And Lam correctly wrote how to make a GenericRepository.
An example of a generalized repository, by the way, is here www.asp.net/mvc/tutorials/getting-started-with-ef-...
I used it in my first project. In fact, personally in my understanding, this is not very good.
Now your method returns an object by its ID. I foresee that the next method would be to get a group of objects and return an IQueryable. This approach unwittingly forces you to write the code for selecting objects somewhere (in the controller, in the service layer), but not in the repository, which leads to confusion. In my opinion, it is better to make not a generalized repository, but a unique repository for each entity necessary for the program to work.
Let's say you have a BlogPost class. You want to be able to load this object by ID, load all of these objects into memory in general, load only part of these objects into memory, load objects with a certain condition, etc. To do this, we can create a BlogPostRepository and write these methods in it. Then your program will become more logical and understandable. When working with blogs, you will not need to each declare a generic repository and build requests like

var repository = new GenericRepository<BlogPost>();
var blogs = repository.GetAll().Skip(100).Take(10).OrderByDescending(c => c.Created);

Well, or something like this (example code). Moreover, such constructions will have to be fenced in any place where you need to get the last 10 blogs.
It’s easier to make not a generalized repository, but a special repository for the desired object, then it will turn out something like this:
var repository = new BlogPostRepository();
var blogs = repository.GetTenTopBlogs();

In the GetTenTopBlogs method, respectively, encapsulate the logic for selecting the last 10 blogs. Of course, GetTenTopBlogs can have parameters (such as the number of blogs to get). Here everything depends on the tasks. And GetTopBlogs should return an IEnumerable so that the calling code is already operating on the entities in memory, rather than building new database queries.
In general, the code becomes more concise. Now you do not need to declare a generic repository every time to get something, drag a Queryable collection from it and make a selection on it. You only call the method and already get the result. Now there is no sampling logic in the calling classes.
Something like this... :)
Alexander Bindu has good articles on this topic.
blog.byndyu.ru/2011/08/repository.html
By the way, I just noticed. As far as I can tell, the repository doesn't need to know about the DbContext object. This is necessary in order to implement the Unit Of Work pattern. Imagine that you want to make some transactions (operations with a repository). And if at least one of them failed, you need to roll everything back. Let's say you want to save 3 different entities to the database. To do this, you wrote your generic GenericRepository and declared three variables for each entity. And you will have the Save function in this GenericRepository (otherwise, how will you save your entities).
It turns out that each instance of the GenericRepository class will contain its own copy of the DbContext, and each time you add an entity, you will save the base.
A single transaction will not work.
To get it, you need to pass the DbContext to the repository constructor and do not store anything in the repositories. Those. you declare your context somewhere, then create three repositories and pass this context to each of them. You do all the operations, after which you save the context in the calling class. It turns out UoW. Though DbContext is already UnitOfWork.
The link above also discusses this (see the recommended links there).
ps Paulskit gave a good link, I was just trying to find it.
Lam also says correctly that common methods can, of course, be moved to a generic repository. Only often there are 1-2 of these methods, and a good third of the entities will not need these methods at all, so I would not make an extra hierarchy just to assign a base class to all repositories.

P
Paulskit, 2013-05-28
@Paulskit

It's not clear what you are trying to achieve. How should the program know that by ID you want to get Problem, and not something else?
It seems to me that you are trying to implement the repository pattern

D
Dmitry, 2013-05-29
@Newbilius

I achieve reduction of the code, removal of the general actions in the ancestor. In all the examples I met, a repository was created, but it played the role of an interface - all methods in all descendants had to be re-implemented.
In PHP one could do something like this:

public class BaseDBProxy 
    {
        protected SiteContext db = new SiteContext();
        protected $class_name="Problems";

        public GetById(int id)
        {
            return db.$class_name.Where(e => e.Id == id).FirstOrDefault<$class_name>();
        }
    }

That is, pass the class name as a string and then get what I need - code reduction and the ability to simply rewrite common methods when switching to another database access library, or even to another database.
I looked at the article, there is something to think about (not with the solution of this problem, but with the formulation of the problem in general).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question