Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IDbSetExtensions.AddOrUpdate and relationships

IDbSetExtensions.AddOrUpdate is meant to help write code that works the same whether the database is empty or populated. But linking objects needs different code. When the database is empty, objects don't have IDs yet and you link them by assigning the navigational property. When the objects already exist, however, navigational properties don't work and you need to set the foreign keys directly. Navigational properties do work for proxies in both cases, at the cost of forfeiting POCOs. Edit: Actually, proxies don't work when both entities are old.

This sample crashes in the second SaveChanges call, when EF tries to set CountryID to 0:

public class Country
{
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }
}

public class Person
{
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }

    public virtual int CountryID { get; set; }
    public virtual Country Country { get; set; }
}

public class Context : DbContext
{
    public DbSet<Person> Person { get; set; }
    public DbSet<Country> Country { get; set; }
}

class Program
{
    static void Foo()
    {
        using (var db = new Context())
        {
            //var c = new Country();
            var c = db.Country.Create();
            c.Name = "usa";
            db.Country.AddOrUpdate(x => x.Name, c);

            //var p = new Person();
            var p = db.Person.Create();
            p.Name = "billg";
            p.Country = c;
            db.Person.AddOrUpdate(x => x.Name, p);

            db.SaveChanges();
        }
    }
    static void Main()
    {
        Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
        Foo();
        Foo();
    }
}

How is AddOrUpdate used?

like image 662
Bruno Martinez Avatar asked Mar 19 '12 19:03

Bruno Martinez


1 Answers

IDbSetExtensions.AddOrUpdate is meant to help write code that works the same whether the database is empty or populated.

AddOrUpdate is meant to be used only in Seed method of code first migrations. It is not supposed to be used in normal code because it has big overhead and some limitations. Overhead is additional query to database and reflection. Limitation is that it checks only the main entity you are passing but not its relations. Each relation is supposed to be handled by separate call to AddOrUpdate:

static void Foo()
{
    using (var db = new Context())
    {
        var c = new Country() {Name = "abc"};
        db.Country.AddOrUpdate(x => x.Name, c);

        var p = new Person()
        {
            Name = "me",
            CountryID = c.ID,
            Country = c 
        };

        db.Person.AddOrUpdate(x => x.Name, p);
        db.SaveChanges();
    }
}
like image 90
Ladislav Mrnka Avatar answered Sep 19 '22 17:09

Ladislav Mrnka