Partager via


Using DbContext in EF 4.1 Part 1: Introduction and Model

 


The information in this post is out of date.

Visit msdn.com/data/ef for the latest information on current and past releases of EF.


 

Introduction

Version 4.1 of the Entity Framework contains both the Code First approach and the new DbContext API. This API provides a more productive surface for working with the Entity Framework and can be used with the Code First, Database First, and Model First approaches. This is the first post of a twelve part series containing collections of patterns and code fragments showing how features of the new API can be used.

The posts in this series do not contain complete walkthroughs. If you haven’t used EF 4.1 before then you should read Code First Walkthrough or Model and Database First with DbContext before tackling this post.

Series Contents

Part 1: Introduction and Model

  • Introduces the topics that will be covered in the series
  • Presents a Code First model that will be used in the subsequent parts

Part 2: Connections and Models

  • Covers using DbContext constructors to configure the database connection and set the context to work in Code First, Model First, or Database First mode
  • Shows different ways that the DbSet properties on the context can be defined

Part 3: Finding Entities

  • Shows how an entity can be retrieved using its primary key value.

Part 4: Add/Attach and Entity States

  • Shows different ways to add a new entity to the context or attach an existing entity to the context
  • Covers the different entity states and shows how to change the state of an entity

Part 5: Working with Property Values

  • Shows how to get and set the current and original values of individual properties
  • Covers different ways of working with all the property values of an entity
  • Shows how to get and set the modified state of a property
  • Covers the loading related entities eagerly, lazily, and explicitly

Part 7: Local Data

  • Shows how to work locally with entities that are already being tracked by the context
  • Provides some general information on the facilities provided by DbContext for data binding

Part 8: Working with Proxies

  • Presents some useful tips for working with dynamically generated entity proxies

Part 9: Optimistic Concurrency Patterns

  • Shows some common patterns for handling optimistic concurrency exceptions

Part 10: Raw SQL Queries

  • Shows how to send raw queries and commands to the database

Part 11: Load and AsNoTracking

  • Covers the Load and AsNoTracking LINQ extension methods for loading entities into the context and querying entities with having them be tracked by the context

Part 12: Automatically Detecting Changes

  • Describes when and how DbContext automatically detects changes in entities provides some pointers for when to switch this off

The Model

Many of the code fragments in the posts of this series make use of the following Code First model. Assume that the context and entity types used in the fragments are from this model unless they are explicitly defined differently in the post.

The code below can be pasted into Program.cs of a standard C# console application and many of the code fragments can then be tested by inserting them into the Main method.

Model Description

The model below contains four entity types: Princess, Unicorn, Castle, and LadyInWaiting. A princess can have many unicorns and each unicorn is owned by exactly one princess. A princess has exactly one lady-in-waiting in each castle.

A castle has a location represented by the complex type Location, which in turn has a nested complex type ImaginaryWorld

Model Code

 using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Linq;

namespace Magic.Unicorn
{
    public class Princess : IPerson
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Unicorn> Unicorns { get; set; }
        public virtual ICollection<LadyInWaiting> LadiesInWaiting { get; set; }
    }

    public class Unicorn
    {
        public int Id { get; set; }
        public string Name { get; set; }

        [Timestamp]
        public byte[] Version { get; set; }

        public int PrincessId { get; set; } // FK for Princess reference
        public virtual Princess Princess { get; set; }
    }

    public class Castle
    {
        [Key]
        public string Name { get; set; }

        public Location Location { get; set; }

        public virtual ICollection<LadyInWaiting> LadiesInWaiting { get; set; }
    }

    [ComplexType]
    public class Location
    {
        public string City { get; set; }
        public string Kingdom { get; set; }

        public ImaginaryWorld ImaginaryWorld { get; set; }
    }

    [ComplexType]
    public class ImaginaryWorld
    {
        public string Name { get; set; }
        public string Creator { get; set; }
    }

    public class LadyInWaiting : IPerson
    {
        [Key, Column(Order = 0)]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int PrincessId { get; set; } // FK for Princess reference

        [Key, Column(Order = 1)]
        public string CastleName { get; set; } // FK for Castle reference

        public string FirstName { get; set; }
        public string Title { get; set; }

        [NotMapped]
        public string Name
        {
            get
            {
                return String.Format("{0} {1}", Title, FirstName);
            }
        }

        public virtual Castle Castle { get; set; }
        public virtual Princess Princess { get; set; }
    }

    public interface IPerson
    {
        string Name { get; }
    }

    public class UnicornsContext : DbContext
    {
        public DbSet<Unicorn> Unicorns { get; set; }
        public DbSet<Princess> Princesses { get; set; }
        public DbSet<LadyInWaiting> LadiesInWaiting { get; set; }
        public DbSet<Castle> Castles { get; set; }
    }

    public class UnicornsContextInitializer : DropCreateDatabaseAlways<UnicornsContext>
    {
        protected override void Seed(UnicornsContext context)
        {
            var cinderella = new Princess { Name = "Cinderella" };
            var sleepingBeauty = new Princess { Name = "Sleeping Beauty" };
            var snowWhite = new Princess { Name = "Snow White" };

            new List<Unicorn>
            {
                new Unicorn { Name = "Binky" , Princess = cinderella },
                new Unicorn { Name = "Silly" , Princess = cinderella },
                new Unicorn { Name = "Beepy" , Princess = sleepingBeauty },
                new Unicorn { Name = "Creepy" , Princess = snowWhite }
            }.ForEach(u => context.Unicorns.Add(u));

            var efCastle = new Castle
            {
                Name = "The EF Castle",
                Location = new Location
                {
                    City = "Redmond",
                    Kingdom = "Rainier",
                    ImaginaryWorld = new ImaginaryWorld
                    {
                        Name = "Magic Unicorn World",
                        Creator = "ADO.NET"
                    }
                },
            };

            new List<LadyInWaiting>
            {
                new LadyInWaiting { Princess = cinderella,
                                    Castle = efCastle,
                                    FirstName = "Lettice",
                                    Title = "Countess" },
                new LadyInWaiting { Princess = sleepingBeauty,
                                    Castle = efCastle,
                                    FirstName = "Ulrika",
                                    Title = "Lady" },
                new LadyInWaiting { Princess = snowWhite,
                                    Castle = efCastle,
                                    FirstName = "Yolande",
                                    Title = "Duchess" }
            }.ForEach(l => context.LadiesInWaiting.Add(l));
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Database.SetInitializer(new UnicornsContextInitializer());

            // Many of the code fragments can be run by inserting them here
        }
    }
}

Summary

In this part of the series we introduced the topics that will be covered in upcoming parts and also defined a Code First model to work with.

As always we would love to hear any feedback you have by commenting on this blog post.

For support please use the Entity Framework Forum.

Arthur Vickers

Developer

ADO.NET Entity Framework

Comments

  • Anonymous
    January 27, 2011
    Looking forward to the rest of this series, thanks! PS.  Your 1st link has an href of "todo" :P

  • Anonymous
    January 27, 2011
    Books and Authors example would have been more relative to the real world. But let me know if you come across any unicorns ;-)

  • Anonymous
    January 27, 2011
    Impressive relational mapper i have seen till date... Definitely the way to go..

  • Anonymous
    January 27, 2011
    So far, i really like what I saw with EF 4.  This is great.  I am looking forward to see the rest of the series.  Please complete the rest of the series !!!!!!

  • Anonymous
    January 28, 2011
    So a Princess can own many Unicorns, but a Unicorn can only be owned by one Princess? Also, what about the case where the Unicorn is an orphan (i.e. is owned by no Princess)?

  • Anonymous
    January 28, 2011
    @Chad I fixed the TODO; thanks for pointing that out. @Matthew Unicorns abound here; follow @efmagicunicorns to see them. @Schmendrick In this model the unicorn is required to have a princess, which means if a unicorn has no princess then it must be deleted.  Sad but true.

  • Anonymous
    January 28, 2011
    Thanks Arthur!  Please continue to finish the series.  I will be checking this often. The more I get into EF 4, the more I am starting to see unicorns and glitter. Is this the side effects of EF 4 Arthur???

  • Anonymous
    January 29, 2011
    Great updates on the EF4. One thing I notice here is, whats the use of the PrincessDto? It seems you can use the Princess Entity itself instead of repeating the whole process. Thanks.

  • Anonymous
    January 29, 2011
    I am really sorry but I could not set decimal precission for property of entity class. modelBuilder.HasDecimalPrecission needs two byte parameters. Could you please give me sample? Thanks in advance.

  • Anonymous
    January 29, 2011
    @Glenn The PrincessDto class is used in one of the later posts. I had intended to remove it from the initial code and only mention it in the later post--I have now removed it from the code above.

  • Anonymous
    January 29, 2011
    @Nuri The two parameters are precision and scale. To quote MSDN, "Precision is the number of digits in a number. Scale is the number of digits to the right of the decimal point in a number. For example, the number 123.45 has a precision of 5 and a scale of 2." So to set and 18 digits with 2 to the right of the decimal point you would do: protected override void OnModelCreating(ModelBuilder modelBuilder) {   modelBuilder.Entity<Unicorn>().Property(u => u.Price).HasPrecision(18, 2); }

  • Anonymous
    January 31, 2011
    I am wondering what is ComplexType for? Is it different from just POCO class with ComplexType attribute? I tried generate Database with both case, one is with ComplexType attribute and other is without ComplexType attribute, but both case seem nothing different.

  • Anonymous
    February 01, 2011
    Please direct me to an article WCF or RIA Services using Code First.

  • Anonymous
    February 02, 2011
    Any info/guidance on how to handle defaults on properties, especially in situations where you want a null string value on a model to map to an empty string in a non-nullable (n)varchar in the database (or vice versa), or where you want to have a DateTime property default to the current date/time when inserted or updated in the database?

  • Anonymous
    February 02, 2011
    The comment has been removed

  • Anonymous
    February 02, 2011
    @Arthur: Thanks for the response, that's generally what we do.  It would be a great addition to be able to create a convention to automatically convert null string properties on a model to empty varchars on non-nullable db columns instead of throwing an exception, or to automatically make any property/column with a given name (like DateCreated or UpdatedAt) set the current date on insert and/or update.

  • Anonymous
    February 05, 2011
    Hi, I just read ScottGu's blog about mapping to "Code-First" to an existing database. I tried doing it and it actually work but I can't seem to add an additional column to the Products table (Northwind) and have EF add it on the Database. Let's say the column name is SKU. How can I achieve that? Thanks

  • Anonymous
    February 07, 2011
    @Glenn Updating an existing database in-place (aka migrations) is not yet supported by Code First. This blog post describes some of the things we may do around migrations: blogs.msdn.com/.../code-first-database-evolution-aka-migrations.aspx We would love to hear your feedback.

  • Anonymous
    February 10, 2011
    Hi, is it possible to add a conditional mapping? I have an 1:m Relation, but I always want the last added Entity (1:1). According to your example - i want something like this: public class Princess : IPerson {        public int Id { get; set; }        public string Name { get; set; }        public virtual ICollection<Unicorn> Unicorns { get; set; }        public virtual ICollection<LadyInWaiting> LadiesInWaiting { get; set; } //********** Differences starting here **********        //Unicorns.LastOrDefault() or something like this        public virtual Unicorn LastAddedUnicorn {get; set} }

  • Anonymous
    February 15, 2011
    I'm puzzled by the classes that have both an ID and an object reference.  For instance, LadyInWaiting has both PrincessId and Princess.  This seems redundant.  Does EF require this?

  • Anonymous
    February 17, 2011
    @Fred You can't map this directly, but you can certainly add a method that will return the last added entity or even use the Query method (Part 7) to query for the last added entity. @Kyraless It's not required; you can create an independent association where the FK is not mapped to your class. However, many things (data binding, optimistic concurrency resolution, n-tier, etc) are much more difficult if the FK is not mapped.

  • Anonymous
    April 18, 2011
    I feel using real world business entities, such as Customers, Orders, Suppliers, etc.  (instead of Princess and Unicorns) makes it easier to relate to the subject matter.

  • Anonymous
    September 06, 2011
    I'm surprised by the following code new List<Unicorn>            {                new Unicorn { Name = "Binky" , Princess = cinderella },                new Unicorn { Name = "Silly" , Princess = cinderella },                new Unicorn { Name = "Beepy" , Princess = sleepingBeauty },                new Unicorn { Name = "Creepy" , Princess = snowWhite }            }.ForEach(u => context.Unicorns.Add(u)); This is first time I see this way of creating an instance and initialization of a generic list. Please can someone tell me what is this or send me any link that describes this way of creating an instance and initialization of a generic list?!

  • Anonymous
    October 02, 2011
    It is nice article but i have problem in understanding the concept of precess unicorn. can you give us the solution based on real example

  • Anonymous
    October 02, 2011
    It is nice article but i have problem in understanding the concept of princess,unicorn. can you give us the solution based on real example

  • Anonymous
    December 12, 2011
    posted a rather pertinent question to this post yesterday and am frustrated to see your CMS has failed to persist the data. inspires confidence.

  • Anonymous
    December 13, 2011
    @Jonuz, msdn.microsoft.com/.../bb384062.aspx is a good summary of collection initializers. @grumbling, sorry to hear that.  Would you mind restating the question?  Alternatively, you can use the "Email Blog Author" link to the right.