Tutorial: Code First with EF 4.1

This tutorial introduces the new functionalities that were added to Entity Framework 4.1.
It shows the differences between the approaches and acts as an introduction to these new features.

Database First & Model First

The Database First approach is interesting when the database already exists. You use Visual Studio and the Entity Framework Designer to generate the C# and VB.NET classes which reflect the existing database model.
You may then change relations and structures using the Designer (or the XML mapping files) to further optimize the model. The priority is the database - the code and the model are only secondary.

The Model First approach is important when you begin from scratch. You start with the entity model and use the Entity Framework Designer to design the relations and the entities.
Then you generate the C# and VB.NET classes and also the database from within Visual Studio. The priority is the model - the code and the database are only secondary.

Code First

When your priority is the code and you want to begin from scratch without any existing schemas or XML mapping files using source code, then the approach is called CodeFirst.
You simply create your domain classes and Entity Framework 4.1 will easily allow to use them with the database and the future model.
You use a context and are able to execute Linq2Entities queries in no time ! You may even take advantage of the change tracking features !
Entity Framework handles all of the background work and manages the interaction with the database. All the classes that are used during runtime are auto-generated.
In this case the configuration is not read from XML files but instead read from the configuration of the DbContext object in memory.

Code First allows:

  • Developing without the need to use the Designer or XML mapping files
  • Defining the objects of the model based on a POCO (Plain Old CLR Objects) approach


Explanation:

  • Design and implement a company domain with Managers, Collaborators and Departments.
  • Create your classes using a standard object oriented approach and using inheritance when appropriate.
  • Use the POCO approach for your classes where possible.
  • Note that for being able to use the auto-mapping feature for the primary keys you need to have named your class properties like this : [Classname]Id.
    public abstract class Person
    {
        public string Name { getset; }
        public int DepartmentId { getset; }
        public virtual Department Department { getset; }
    }

    public class Collaborator : Person
    {
        public int CollaboratorId { getset; }
        public string ManagerCode { getset; }
        public virtual Manager Manager { getset; }
    }
  • Entity Framework 4.1 allows connecting those POCO classes with the database very easily by using the ObjectContext class (and in particular the DbContext class).
    public class CompanyContext : DbContext
    {
        public CompanyContext() : base("CompanyDatabase") { }

        public DbSet<Collaborator> Collaborators { getset; }
        public DbSet<Department> Departments { getset; }
        public DbSet<Manager> Managers { getset; }
    }
  • The last step consists of defining the connection to the database within the web.config or app.config files. Note that EF will search for the connection string that has the same name as the class that was inherited from the DbContext class. You may however define a different name ("CompanyContext" in our example).
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <connectionStrings>
        <add
          name="CodeFirstDatabase"
          providerName="System.Data.SqlClient"
          connectionString="Server=.\SQLEXPRESS;Database=Products;Trusted_Connection=true;"/>
      </connectionStrings>
    </configuration>

Data Annotations & CodeFirst Fluent API

The new version of Entity Framework supports the usage of Data Annotations. Data Annotations are used for validation purposes and also for mapping definitions between code and columns in the database.
All annotations are implemented as attributes which can be found in the System.ComponentModel.DataAnnotations  namespace (same attributes that are used for ASP.NET MVC validation purposes).

Common attributes:

  • [Key] – Column(s) that define the Primary Key in the database table
  • [StringLength] – Minimal and maximal string length (only used for validation purposes not as database constraints)
  • [MaxLength] – Can be used instead of [StringLength] to just define the maximal string length of a column
  • [ConcurrencyCheck] – Flag that indicates that concurrency checks are activated for the column 
  • [Required] – Flag that indicates that a value is required for the column (column is flagged as not null)
  • [Timestamp] – Flag on time stamp columns which is used for concurrency checks
  • [Column] – Specify the name of a column (default property name), can also be used to define the column position
  • [Table] – Specify the name of a table (default class name)
  • [DatabaseGenerated] – Flag that indicates that the value is filled by the database (possible values are Computed, Identity, None)
  • [NotMapped] – Is used to define a property in the class that does not get generated in the database.
  • [ForeignKey] and [InverseProperty] – Column(s) that are defined as Foreign Keys

Following an example for the usage of Data Annotations that was applied to our company domain. The Manager class does not currently contain any properties that could automatically be identified as primary key.
By adding the [Key] attribute to the ManagerCode property you define that this property should act as primary key. You furthermore add a concurrency check as well as a minimum and maximum length setting for the Name property.

    public class Manager : Person
    {
        [Key]
        public string ManagerCode { getset; }
        [ConcurrencyCheck]
        [MinLength(5)]
        [MaxLength(20,ErrorMessage="Le nom ne peut pas avoir plus de 20 caractères")]
        public new string Name { getset; }
        public virtual ICollection<Collaborator> Collaborators { getset; }
    }

In the following example we add a required constraint to the Department class by applying the [Required] Data Annotation to the Name property.

    public class Department
    {
        public int DepartmentId { getset; }
        [Required]
        public string Name { getset; }
        public virtual ICollection<Collaborator> Collaborators { getset; }
    }

Data Annotations are definitely easy to use but it  is preferable to use a programmatic approach that provides much more flexibility.
This approach is based on the Code First Fluent API. You have to implement the OnModelCreating method within the derived CompanyContext class.
Then you define your constraints applicable to your domain within this method programmatically.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Department>().Property(dp => dp.Name).IsRequired();
        modelBuilder.Entity<Manager>().HasKey(ma => ma.ManagerCode);
        modelBuilder.Entity<Manager>().Property(ma => ma.Name)
            .IsConcurrencyToken(true)
            .IsVariableLength()
            .HasMaxLength(20);
    }

When using this approach you may even describe relations between tables and columns.
The following code example defines that the Manager entity contains a Department entity. The key that relates both is the DepartmentId.
If the Manager entity is deleted then the corresponding Department must also be deleted  (CascadeOnDelete).

    modelBuilder.Entity<Manager>()
        .HasRequired(d => d.Department)
        .WithMany()
        .HasForeignKey(d => d.DepartmentId)
        .WillCascadeOnDelete();

Data Validation

Data validation is a very important subject when developing applications. You have to assure good quality and high integrity of your data for being able to exploit it correctly.
You need to avoid storing any corrupt, incomplete or wrong data in your database which will most certainly lead to misbehavior within your applications.
The following example shows how to implement a constraint on the Name property (required / string length between 5 and 20 characters) via Data Annotations.

    [MinLength(5)]
    [MaxLength(20,ErrorMessage="Le nom ne peut pas avoir plus de 20 caractères")]
    public new string Name { getset; }

Validation is automatically done when calling the Save method to persist any changes applied to your objects in memory. But you may as well do it before by calling the GetValidationErrors method.

    public static void Validation()
    {
        using (var context = new CompanyContext())
        {
            var manager = new Manager() { Name = string.Empty };
            context.Managers.Add(manager);

            var validationErrors = context.GetValidationErrors()
                .Where(vr => !vr.IsValid)
                .SelectMany(vr => vr.ValidationErrors);

            foreach (var error in validationErrors)
            {
                Console.WriteLine(error.ErrorMessage);
            }

            Console.ReadKey();
        }
    }

Lets say when the code above is executed it will fail due to validation errors. In this case error messages will be displayed for each constraint that was not respected.
In our example we defined that the Name property should have a minimum string length of 5 characters, but the property only contains 4 characters.
Since we did not define a specific error message, a generic error message will be displayed for this validation error.

There are multiple Data Annotations that allow for type validation, format validation, regular expression matching, etc….

Data Audit

All properties managed by the Entity Framework can be audited. This is very helpful if you need to display changes to objects before saving them, cancel modifications by resetting the current value with the initial value or handling concurrency problems.

Entity Framework 4.1 permits accessing the following property versions of an entity:

  • Database Value
  • Original Value
  • Current Value

.

    public static void Audit()
    {
        using (var context = new CompanyContext())
        {
            var manager = context.Managers.Find("JDO");

            // Change value directly in the DB
            using (var contextDB = new CompanyContext())
            {
                contextDB.Database.SqlCommand(
                    "UPDATE managers SET Name = Name + '_DB' WHERE ManagerCode = 'JDO'");
            }

            // Change the current value in memory
            manager.Name = manager.Name + "_Memory";

            string value = context.Entry(manager).Property(m => m.Name).OriginalValue;
            Console.WriteLine(string.Format("Original Value : {0}", value));

            value = context.Entry(manager).Property(m => m.Name).CurrentValue;
            Console.WriteLine(string.Format("Current Value : {0}", value));

            value = context.Entry(manager).GetDatabaseValues().GetValue<string>("Name");
            Console.WriteLine(string.Format("DB Value : {0}", value));

            Console.ReadKey();
        }

    }

When executing the code above the different values for the Name property of the Manager class are displayed.

As you see, adding audit functionality to your applications is really quick to do, Entity Framework 4.1 contains already everything necessary to implement it very easily.

Concurrency Management

Assuring the consistence and coherence of data in the database is a very important topic. Concurrency problems come up when multiple source (clients, systems, etc…) try to modify data at the same time.
Your application must be able to choose whether to accept or reject any data changes that arrive simultaneously.

Simultaneous execution of transactions must provide the same results as sequential execution of those same transactions.
If you achieve this goal then your system is well prepared for correct concurrency handling.

Entity Framework 4.1 includes a very complete concurrency approach. The DbContext class that we already saw before allows for efficient concurrency handling.

First Wins:

This resolution strategy defines that the first modification wins if there are concurrency conflicts.
All following modifications on the same data are ignored and clients get exceptions that tell them that their transactions did not complete because the data was already changed.

You have to determine which properties might be subject to concurrency problems. You may have properties which do not need any concurrency handling.
A property that contains the last modified date might not need to implement any concurrency handling for example.

All properties that must be checked for concurrency need to have the [ConcurrencyCheck] attribute set as shown below:

    [ConcurrencyCheck]
    public new string Name { getset; }

If you don't like using attributes, you may also use the CodeFirst Fluent API on your properties to activate concurrency checking:

    modelBuilder.Entity<Manager>().Property(ma => ma.Name).IsConcurrencyToken(true);

You have to add concurrency checks only on properties that really need it since this will have some impact on performance within your code.
The more properties with activated concurrency checks you have the worse your performance might be depending on your implementation !

    public static void FirstWins()
    {
        using (var context = new CompanyContext())
        {
            var manager = context.Managers.Find("JDO");
            Console.WriteLine("Initial Value: " + manager.Name);
                
            // Change the DB value
            using (var contexDB = new CompanyContext())
            {
                contexDB.Database.SqlCommand(
                    "UPDATE managers SET Name = 'FirstWins' WHERE ManagerCode = 'JDO'");
                Console.WriteLine("DB Updated Value: FirstWins");
            }

            // Change the memory value
            manager.Name = "NewName";
            try
            {
                context.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                // Not possible to perform change because of differences between original and DB value
                ex.GetEntry(context).Reload();
            }

            manager = context.Managers.Find("JDO");
            Console.WriteLine("Final Value: " + manager.Name);
            Console.ReadKey();
        }
    }

In the example above we retrieve a manager object from the database, store it in memory and change some property values (but only in memory).
We then change some values on the same data directly in the databse to simulate concurrency as if two clients did modifications at the same time.

When we call the SaveChanges() method you will see that we get an exception of type DbUpdateConcurrencyException.
This is where you need to add your custom concurrency conflict resolution logic. In this example we decided to guard the first modification (First Wins).
In the last step we update the context in memory with the data that was changed in the database via ex.GetEntry(context).Reload().

Last Wins:

This is the default resolution strategy of EF 4.1 in case of concurrency conflicts. It defines that the last modification wins if there are concurrency conflicts.
All previous modifications on the same data are overwritten. Clients only get informed if they use the [ConcurrencyCheck] attribute, some kind of log, some event or other treatment.

    public static void LastWins()
    {
        using (var context = new CompanyContext())
        {
            bool savedChanges = false;

            var manager = context.Managers.Find("JDO");
            Console.WriteLine("Initial Value: " + manager.Name);

            // Change the DB value
            using (var contexDB = new CompanyContext())
            {
                contexDB.Database.SqlCommand(
                    "UPDATE managers SET Name = 'LastWins' WHERE ManagerCode = 'JDO'");
                Console.WriteLine("DB Updated Value: LastWins");
            }
                
            manager.Name = "NewName";

            while (!savedChanges)
            {
                try
                {
                    context.SaveChanges();
                    savedChanges = true;
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    // Update original value with DB value
                    var entry = ex.GetEntry(context);
                    entry.OriginalValues.SetValues(entry.GetDatabaseValues());
                }

            } 

            manager = context.Managers.Find("JDO");
            Console.WriteLine("Final Value: " + manager.Name);
            Console.ReadKey();
        }
    }

The example above shows how to implement a Last Wins approach. If there are exceptions due to concurrency conflicts we store the new initial value in the context thus synchronizing with the source.
We then retry saving the data until it is saved to the database and the processing is successfully completed.

Conclusion

I hope this tutorial gave you a first insight on the new features of EF 4.1 which are quite powerful even for code purists.
With the new features presented in this tutorial EF becomes one of the most complete products on the ORM market.
Today, it is the only product that proposes all 3 approaches CodeFirst, Database First and Model First at the same time.

Some features are missing like auto-synchronization of the database if the class structure changes and stored procedure integration but they will be added in the next weeks / months.

 

Find further explications and a more detailed step-by-step guide on my Blog:
http://jasondeoliveira.com

Last edited Oct 17, 2011 at 2:34 PM by JasonOliveira, version 42