Entity Framework and DetectChanges

DetectChanges can be set to false inside the configuration of the DbContext.

public class YourContext : DbContext
{
    public YourContext(): base("DefaultConnection")
    {
        this.Configuration.AutoDetectChangesEnabled = false;
    }
}

or can be turned off for the context life cycle:

using (var context = new YourContext())
{
    context.Configuration.AutoDetectChangesEnabled = false;
}

This will increase the overall performance of Entity Framework because it will not execute the verification of changes of the entities inside the DbContext and the database. Disabling the auto detection of changes come with the cost that if you do not call it manually that you will not have the good result.

In the case that the AutoDetectChangesEnabled is kept to true, or set to true again, than you can be sure that a verification is done when:

  • DbSet.Add
  • DbSet.Find
  • DbSet.Remove
  • DbSet.Local
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • Any Linq query on DbSet

If you are doing few queries or that you add several new entities you may want to detect change only after all commands instead of letting Entity Framework detecting change on every command.

To tell Entity Framework to detect changes, you must use the DbContext and call the ChangeTracker property that has the DetectChanges method.

The followed example will not update the user.

private static void DetectChangesExample()
{
    using (var context = new YourContext())
    {
        context.Configuration.AutoDetectChangesEnabled = false;
        var personToModify = context.Persons.Find(1);
        personToModify.BirthDate = new DateTime(3050,12,12);
        context.SaveChanges();
    }
}

This is because the DbContext does not know about the change of the Person Id 1. If we change the above code by adding the DetectChanges than the database is notified of the changes.

private static void DetectChangesExample()
{
    using (var context = new YourContext())
    {
        context.Configuration.AutoDetectChangesEnabled = false;
        var personToModify = context.Persons.Find(1);
        personToModify.BirthDate = new DateTime(3050,12,12);
        context.ChangeTracker.DetectChanges();
        context.SaveChanges();
    }
}

It is also possible to get from the Entry collection the state of the entity. If we execute both example by adding a simple check of the state, the one that has auto detect changes to false will return an unmodified state while the other one will have a modified state.

Unchanged

With DetectChanges:
StateChanged

Something interesting about Complex Type is that the whole class has a simple state. If you change one property of the whole class that is marked as complex type, the whole complex type is market has modified. The reason is that Entity Framework does not create a proxy object for complex type.

This say, you must use DetectChanges before saving otherwise your changes will never reach the database. You can find the code in this post on GitHub or get the Zip file.

Entity Framework Eager Loading and Lazy Loading Produce Same Queries

If you are using Entity Framework with Lazy Loading or Eager Loading you may realize that both work the same way. With Lazy Loading, you have to access the property and Entity Framework load the property on-demand. On the other side, with Eager Loading, we need to specify the property to load before accessing it. Lazy Loading has the disadvantage that you must have virtual property and has some overhead code that is executed to remember if it must load from the database or from the context, while Eager Loading lets you handle everything so you can optimized your database calls.

In a scenario where you have a one to one relationship, for example a Person that has an House.

var person = new Person {Id = 1, Name = "SeededPerson", BirthDate = new DateTime(1900, 1, 1), Friends = new Collection<Person> {person2, person3}};
 person.Residence = new House {Id = 1, Address = new Address{City="Montreal", Number = 123, Street = "Owl"}, Price = 350000};
context.Persons.Add(person);
[/cshapr]

You can load the Residence by using Eager Loading this way:


private static void LazyLoadingAndEagerLoadingSameResult()
{
    using (var context = new YourContext())
    {
        Console.WriteLine("No Lazy Loading, Eager Loading");
        var person = context.Persons.Find(1);
        context.Entry(person).Reference(d=>d.Residence).Load();
        Console.WriteLine("City is " + person.Residence.Address.City);
    }
}

If we kick in the Microsoft SQL Profiler, we see two queries. One that load the Person and one that load the Residence object.

SELECT 
    [Limit1].[Id] AS [Id], 
    [Limit1].[Name] AS [Name], 
    [Limit1].[BirthDate] AS [BirthDate], 
    [Limit1].[Residence_Id] AS [Residence_Id], 
    [Limit1].[House_Id] AS [House_Id]
    FROM ( SELECT TOP (2) 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Name] AS [Name], 
        [Extent1].[BirthDate] AS [BirthDate], 
        [Extent1].[Residence_Id] AS [Residence_Id], 
        [Extent1].[House_Id] AS [House_Id]
        FROM [dbo].[People] AS [Extent1]
        WHERE [Extent1].[Id] = @p0
    )  AS [Limit1]

SELECT 
    [Extent2].[Id] AS [Id], 
    [Extent2].[Price] AS [Price], 
    [Extent2].[Address_Street] AS [Address_Street], 
    [Extent2].[Address_Number] AS [Address_Number], 
    [Extent2].[Address_City] AS [Address_City], 
    [Extent2].[Owner_Id] AS [Owner_Id]
    FROM  [dbo].[People] AS [Extent1]
    INNER JOIN [dbo].[Houses] AS [Extent2] ON [Extent1].[Residence_Id] = [Extent2].[Id]
    WHERE ([Extent1].[Residence_Id] IS NOT NULL) AND ([Extent1].[Id] = @EntityKeyValue1)

The Lazy Loading version is similar but without the Reference/Load line.

using (var context = new YourContext())
{
    context.Configuration.LazyLoadingEnabled = true;
    Console.WriteLine("Lazy Loading, No Eager Loading");
    var person = context.Persons.Find(1);
    Console.WriteLine("City is " + person.Residence.Address.City);
}

It also require you to change the Person class to have the Residence to be virtual.

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
        
    public DateTime BirthDate { get; set; }

    public ICollection<Person> Friends { get; set; }

    public virtual House Residence { set; get; }
}

If you forget to set the virtual, you will not receive an error message about the virtual but about a NullReferenceException. Unfortunately, the exception does not speak for itself! You have to remember to change the property to virtual.

You can find the source code of this article on GitHub or inside this Zip File.

Entity Framework Default Collection Value

If you have a class that has collections, the default value of these collections are NULL when you Entity is loaded by Entity Framework. If you are using Lazy Loading, than these properties are virtual. It is not recommended to initialize the collection in the constructor if these are virtual. Not because it is Entity Framework but because initializing a property that is virtual is never a good practice in .Net. The reason is beyond the scope of this article but just keep in mind that even if it is initialized by the constructor, the class that inherit your class may still use the property before it is initialized which could cause an exception.

Still, if you are using Entity Framework without Lazy Loading, your properties should not be virtual and by default, these collections must be initialized otherwise, a NULL value is returned.

Converting anonymous object to Dictionnary

Some Asp.Net MVC Html Helpers use the possibility to add anonymous object as parameter to assign key value. It is the case of HtmlAttributes parameter. If you want to create your own Html Helper or simply having the possibility to use anonymous object, you may stumble into the case that you need to enumerate keys and values.

public string Url(string action, string controller, string area = null, object routeValues = null)
{
    //Code here
}

The code above is an example. You may want to have a method that generate an url from some parameters. The last parameter named “routeValues” is of type object. This one is created to be used for anonymous object.

    Url("action", "controller", "area", new {Id = "123", Name="This is my name"});

The Url method can then loop through all the properties. Something that can help you is to create an anonymous method for object that convert everything into an IDictionary where it will be easy to manipulate keys (property name) and values.

public static class ObjectExtensions
{
    public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
    {
        return source.GetType().GetProperties(bindingAttr).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source, null)
        );
    }
}

It uses reflection to get all properties and from these properties to get all values.

Here is a unit test for the AsDictionary extension method.

[TestClass]
public class ObjectExtensionsTest
{
    [TestMethod]
    public void GivenAnObject_WhenThisOneHasMultipleProperties_ThenDictionary()
    {
        // Arrange
        var objectToConvert = new {Id="Test", Name="Test2"};

        // Act
        var dictionary = objectToConvert.AsDictionary();

        // Assert
        Assert.AreEqual(2,dictionary.Keys.Count);
        Assert.AreEqual(objectToConvert.Id,dictionary["Id"]);
        Assert.AreEqual(objectToConvert.Name,dictionary["Name"]);
    }
}

Integer DisplayTemplates, EditorTemplates and Nullable Value

You can define templates in the DisplayTemplates and EditorTemplates. If you define one template for integer your can have an error saying that the model item require a string.

The model item passed into the dictionary is of type ‘System.Int32’, but this dictionary requires a model item of type ‘System.String’

This error occurs if you name the template int.cshtml or integer.cshtml. Even if you have defined the @model int or @model int?.

The solution is to name the file Int32.cshtml. CLR type name are used and not C# alias.

Another detail important to remember is that if you want to have integer and nullable integer you can only do it withing one template. So, always define the type with its nullable type. For example, the integer should be in the file Int32.cshtml and the template could be something like this (for DisplayTemplate):

@model int?
    @if (Model.HasValue)
    {
        @Html.Raw(Model)
    }
    else
    {
        @:-
    }

Entity Framework Entry Property to Access Tracking Information for Scalar Properties

Entity Framework has a change tracker API exposed by the Entry property of the DbContext. The Entry method or the Entries method can be used to fin or to change information that is tracked by Entity Framework. It is possible to know the current value but also the original value. It also possible to track the state of every entity, to read it and to modify it. This article cover the use of Entry for scalar properties. Scalar properties are property that has primitive type like integer, string, datetime, double, etc. It does not include any of your classes. In a subsequent post, we will cover how to track entity inside entity, and also collection of entity.

The entry method has two methods that let you drill down inside an entity to change collection of others entity from an entity or to have access to other reference of entities. From there, we could use the explicit loading method, Load, to load specific portion of information without loading the whole table (as seen in a previous article with DbSet).

This is a simple example to add a new entity with Entry. The code display the Person in the Local memory. The new entry is only set in the Local when its state is changed to Added.

private static void EntryToAddNewEntity()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        var newPerson = new Person {Name = "New Person", BirthDate = new DateTime(1980, 1, 2) };
        var entryPerson = context.Entry(newPerson);
        Console.WriteLine(context.Persons.Local.Count); //0
        entryPerson.State = EntityState.Added;
        Console.WriteLine(context.Persons.Local.Count); //1
        context.SaveChanges();
    }
}

Modifying an existing entity with Entry is also possible without loading this one from the database and without having to update every fields.

private static void EntryToModifyExistingEntityWithoutLoadingFromDatabase()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        var existingPerson = new Person { Id=1, Name = "Updated Name"};
        context.Persons.Attach(existingPerson);
        var entryPerson = context.Entry(existingPerson);
        Console.WriteLine(context.Persons.Local.Count); //1
        entryPerson.Property(d => d.Name).IsModified = true;
        Console.WriteLine(context.Persons.Local.Count); //1
        context.SaveChanges();
    }
}

It produces a SQL statement with only a single SET.

exec sp_executesql 
N'UPDATE [dbo].[People]
  SET [Name] = @0
  WHERE ([Id] = @1)
',N'@0 nvarchar(max) ,@1 int',@0=N'Updated Name',@1=1

The advantage of loading from the database is that we do not have to specify which property has been changed.

private static void EntryToModifyExistingEntityByLoadingFromDatabase()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        var existingPerson = context.Persons.Find(1);
        Console.WriteLine(context.Persons.Local.Count); //1
        existingPerson.Name = "Updated from database";
        Console.WriteLine(context.Persons.Local.Count); //1
        context.SaveChanges();
    }
}

By modifying the property, Entity Framework has inside the tracking the original value and the current value. It knows that the only property changed is the name. When you are developing Client-Server application, you may want to modify only properties that has changed. For example, if you have a web form that has every fields and that the user modify one value, you would want to have an update executed with only the field that has changed to be in the update sql statement. Entity Framework has a method in the Entry property named CurrentValues. CurrentValues lets you set the value you want to be the active one. If this one differ from the the OriginalValues, then it is updated. To simplify the process, the CurrentValues properties has a SetValues method. It takes from an object all scalar properties and map them into the entry. Scalar properties are property that is not another entity or a collection.

private static void EntryToModifyByPropertyChanged()
{
    var objectFromUser = new Person {Id = 1, Name="Test", BirthDate = new DateTime(1801, 12, 25)};
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        var existingPerson = context.Persons.Find(1);
        Console.WriteLine(context.Persons.Local.Count); //1
        context.Entry(existingPerson).CurrentValues.SetValues(objectFromUser);
        Console.WriteLine(context.Persons.Local.Count); //1
        context.SaveChanges();
    }
}

This code update Name and BirthDate only if the entity with the ID one has not already in the database this name and birthday. If the name is the same, than only the birthday change. If you execute twice this method, nothing will be updated.

It is also possible to update without doing any select statement from the database by setting to Modified the state of the object that came from the client side.

private static void EntryToModifyByPropertyChangedWithoutUsingFind()
{
    var objectFromUser = new Person { Id = 1, Name = "Tester #2", BirthDate = new DateTime(1941, 12, 25) };
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        context.Entry(objectFromUser).State = EntityState.Modified;
        Console.WriteLine(context.Persons.Local.Count); //1
        context.SaveChanges();
    }
}

So far we have seen how to access the tracking information for all scalar properties with the Entry method. Three others scenarios exist that has not been covered. First, it is about Complex Object. The second is entity that reference an other (optional or required) property. And the third is entity that has a collection of entities. All code in this article is accessible through GitHub or can be downloaded from a Zip file.

Entity Framework DbContext Local

Entity Framework uses the DbContext has a proxy to the connect to the database. This one keep information that came from the database but also information set from the code. It is the middle man between your entity in your code and your entity in your database. To be more accurate, it is the Local of the DbSet that contain the DbContext where the information remain for the life time of the DbContext.

CodeDbContextDataBase

The DbContext has for every sets a local storage. This is a temporary place where information reside. It is possible to access this container to get information already queried.

DbContextDbSetLocal

The Finds method use the Local to get for the primary key the entity before querying the database. However, method like Single and Where does not. But, it is possible to explicitly do a query against the Local. If we do a test, we see that using SingleOrDefault twice call the database twice. If we query the local with SingleOrDefault, no query is done to the database.

private static void QueryLocal()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        var newPerson = new Person { Id = 500, Name = "New Person", BirthDate = new DateTime(2000, 1, 2) };
        context.Persons.Add(newPerson);
        Console.WriteLine(context.Persons.Local.Count); //1
        var person = context.Persons.SingleOrDefault(d => d.Id == 1); //Query the database
        Console.WriteLine(context.Persons.Local.Count); //2
        var person2 = context.Persons.SingleOrDefault(d => d.Id == 1); //Query the database again
        Console.WriteLine(context.Persons.Local.Count); //2
        var person3 = context.Persons.Local.SingleOrDefault(d => d.Id == 1); //Does not query the database
        Console.WriteLine(context.Persons.Local.Count); //2
    }
}

LocalQueryDatabase

Querying with a where clause load information into the Local storage but does not load everything. If you want to load all entities into your local you can use explicit loading.

private static void ExplicitLoadIntoLocal()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        context.Persons.Load();
        Console.WriteLine(context.Persons.Local.Count); //1
    }
}

The explicit loading do a single query that load everything from the database. The SQL profiler shows the Select statement below.

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[BirthDate] AS [BirthDate], 
    [Extent1].[Person_Id] AS [Person_Id], 
    [Extent1].[Residence_Id] AS [Residence_Id], 
    [Extent1].[House_Id] AS [House_Id]
    FROM [dbo].[People] AS [Extent1]

In a future article, we will see that it is possible to load with explicit loading (.Load) not everything from the database. The goal here is to show that the DbSet.Local is filled up. It is also important to notice that if you call twice Load, it will load from the database everything. If you have one conclusion to remember from this article is that you have to be aware of which operations goes all the time to the database and which ones can are optimized by using the Local cache.

private static void ExplicitLoadIntoLocal()
{
    using (var context = new YourContext())
    {
        Console.WriteLine(context.Persons.Local.Count); //0
        context.Persons.Load();
        Console.WriteLine(context.Persons.Local.Count); //1
        context.Persons.Load(); //Call the database one more time
    }
}

If you are interested to execute the code in this post, you can get the source code from GitHub. It is also possible to get everything from a Zip file.