Entity Framework Code First in practice

Hello!

In this publication I want to share my personal experience using Entity Framework (EF) in a real application and give some useful tips on using this framework.

I have been developing enterprise applications on the .NET platform for more than 7 years, during this time I have tried several ORM libraries, but now I use the Entity Framework Code First for new projects.

Initially, as the name implies, this approach assumed that the database storing application data is described first with code, and then the framework itself creates or updates it. However, many developers chose to use the exact opposite approach, when the database is first created, and then objects are mapped to it. This is especially convenient for enterprise applications where data is almost always at the forefront and a rather advanced DBMS such as Oracle or MSSQL Server is used. I do not like the EF designer when in it the number of tables exceeds one hundred. Therefore, Code First was perceived by me personally as something insanely convenient and extremely useful.

Once again, I want to repeat that, in my opinion, the main thing in an enterprise application is data, not your application. Applications come and go, are replaced with newer ones, and data remains and is used for years. Therefore, in any of my applications, the database is always created by the database designer, and not by my application.

The first thing to do if you already have a ready-made database is to create classes that match the tables. Manually doing this is very tiring, therefore:

Tip number 1. Table-based class generation


The T-SQL script, which can be taken from here , really works and is very convenient.

Tip number 2. Regeneration view


EF is known for the very long processing time of the first request to receive and store data. In order to correct this situation, you can use the regeneration view.

Regenerating a view is something that happens inside the framework when the first request is executed. This process can be transferred to the compilation stage of your application, which will significantly reduce the speed of the first request. For different versions of EF, the methods are slightly different. I used this approach and was completely satisfied with it.

Tip number 3. Bulk data update with DetectChanges


By default, the framework is configured to automatically track changes that will then need to be saved to the database.
For example, consider this simple example:

var dbContext = new MyDbContext();
//Добавляем большое число записей в некоторую таблицу
for(var i=0;i<1000;i++)
{   
    dbContext.People.Add(new Person());//!!! ОЧЕНЬ ДОЛГО РАБОТАЕТ
}
//Сохраняем изменения в БД
dbContext.SaveChanges();


By launching it, many will probably be surprised that most of the time will be spent not on actually querying the database, but on inserting objects into dbContext itself. The fact is that when data is changed inside DbContext, there are a lot of checks and other little-studied things, more about which can be read here . To avoid this, you can turn off tracking changes in the DbContext class, and then explicitly call the DetectChanges () method, which will detect these changes. So, this code will work much faster:

var dbContext = new MyDbContext();
//Отключаем автоматическое слежение за изменениями
dbContext.Configuration.AutoDetectChangesEnabled = false;
//Добавляем большое число записей в некоторую таблицу
for(var i=0;i<1000;i++)
{   
    dbContext.People.Add(new Person());//теперь этот метод работает значительно быстрее
}
dbContext.ChangeTracker.DetectChanges(); //Обновляем сведения об изменениях. Работает быстро
//Сохраняем изменения в БД
dbContext.SaveChanges();


Tip number 4. Follow the sql queries that the framework makes


This is a fairly universal advice, applicable, probably, to all ORMs, but for some reason many people forget about it. And after implementing the application, opening the profiler and seeing 50 database queries to display a fairly simple form, they are extremely surprised.

Meanwhile, the framework offers mechanisms for some optimization. Among these mechanisms, the most famous is the Include method, which “pulls” child objects in the same query. For instance:

var dbContext = new MyDbContext();
//Наряду с объектом Person в этом запросе также будет получен дочерний объект Address
var person = dbContext.People.Include(p=>p.Address).FirstOrDefault();
//Запроса к БД не будет
var address = person.Address;


Tip number 5. Isolate database logic


Although EF has the word “framework” in its name, it can be used as a regular library just to simplify working with the database.
In most books and presentations, we see code similar to this:

//Контроллер MVC 
public class PersonController: Controller
{
    //Контекст EF приложения
    AppDbContext dbContext;
    public PersonController(AppDbContext dbContext)
    {
        this.dbContext = dbContext;
    }
    public IActionResult ViewPerson(int id)
    {
        var person = dbContext.People.First(p=>p.Id == id);
        return View(person);
    }
}


The problem with this code is that it tightly binds all parts of the application to the Entity Framework. This is neither good nor bad in itself, but you should understand that you significantly complicate your life if you have to change the architecture of access to data. And there may be plenty of options:

  • You suddenly find that a particular request is extremely slow and decide to replace it with a stored procedure or View
  • You will be surprised to find how difficult it is to save data received in different DbContexts
  • You will be surprised to see the TransactionScope, DbConnection classes inside the controller when you need a nontrivial logic to update data
  • You’ll think about reusing code to work with the database.
  • You decide to use NHibernate instead of EF
  • Why not use the SOA architecture and start getting and saving data through web services?

I like the approach when EF is used ONLY inside a project called DAL (DataAccess, etc.) in classes called Repository, for example:

//Репозиторий для получения объектов класса Person
public class PersonRepository: IPersonRepository
{
    public Person GetPerson(int personId)
    {
        var dbContext = new AppDbContext();
        var person = dbContext.People.First(p=>p.Id == id);
        return person;
    }
    public void SavePerson(Person person)
    {
        var dbContext = new AppDbContext();
        var dbPerson = dbContext.People.First(p=>p.Id == person.Id);
        dbPerson.LastName = person.LastName;
        .....
        dbContext.SaveChanges();
        return person;
    }
}


This approach is good because your application will only know about your objects and how you can get them from the database and how to save them in the database. Believe me, this will make your life much easier.

Also popular now: