2 Stimmen

EntityFramework 5 Code First - Nachschlagetabelle erhält doppelte Datensätze

Ich versuche den EF5 CodeFirst und kann das einfache Setup nicht zum Laufen bringen ;(

Ich habe zwei Klassen Foo und Bar, wobei Bar eine Nachschlagetabelle darstellt.

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Bar Bar { get; set; }

}

public class Bar
{
    public int Id { get; set; }
    public string Description { get; set; }
}

public class MyDbContext : DbContext
{
    static MyDbContext()
    {
        Database.SetInitializer<MyDbContext>(null);
    }

    public MyDbContext(): base("testEF"){}

    public DbSet<Foo> Foos { get; set; }
    public DbSet<Bar> Bars { get; set; }
}

Jetzt habe ich eine statische Klasse erstellt, die als DataAccess Layer dient - in der realen Anwendung wird sie auf einer anderen physikalischen Ebene liegen

public static class DataAccess
{
    public static Bar GetBarById(int id)
    {
        using (var db = new MyDbContext())
        {
            return db.Bars.SingleOrDefault(b => b.Id == id);
        }
    }

    public static Foo InsertFoo(Foo foo)
    {
        using (var db = new MyDbContext())
        {
            db.Foos.Add(foo);

            db.SaveChanges();
        }
        return foo;
    }
}

Ich initialisiere die DB mit der Seed-Methode:

internal sealed class Configuration : DbMigrationsConfiguration<testEF.MyDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }
    protected override void Seed(testEF.MyDbContext context)
    {
            context.Bars.AddOrUpdate(
                new Bar { Description = "Bar_1" },
                new Bar { Description = "Bar_2" }

                );
    }
}

Dadurch werden zwei Datensätze in der Tabelle Bars erstellt. So weit, so gut...

Hier ist meine Hauptfunktion

static void Main(string[] args)
{
    var bar1 = DataAccess.GetBarById(1); 

    var foo = new Foo
    {
        Name = "Foo_1",
        Bar = bar1
    };

    DataAccess.InsertFoo(foo);

}

Nach den App-Runen gibt es einen Eintrag in der Foos-Tabelle:

Id       Name    Bar_Id
1        Foo_1   3   

Warum ist Bar_Id 3? Die EF hat tatsächlich einen neuen Datensatz in die Tabelle Bars eingefügt!

Id  Description
1   Bar_1
2   Bar_2
3   Bar_1

Was mache ich falsch?

UPDATE: Ich habe eine Abhilfe gefunden - die Bar-Eigenschaft vor dem Einfügen des Datensatzes anzuhängen:

public static Foo InsertFoo(Foo foo)
{
    using (var db = new MyDbContext())
    {
        db.Bars.Attach(foo.Bar);

        db.Foos.Add(foo);

        db.SaveChanges();
    }
    return foo;
}

Es funktioniert zwar jetzt, aber das ist eher ein Hack als eine echte Lösung... In der realen Anwendung könnte die Komplexität der Objekte zu einem großen Problem werden. Ich bin offen für bessere Lösungen

5voto

Craig Stuntz Punkte 124703

Das Problem ist, dass bar1 stammt aus einem anderen Datenkontext. Ihr InsertFoo Methode fügt es implizit dem zweiten Kontext hinzu, indem sie eine Beziehung mit der Foo . Sie wollen, dass diese beiden einen gemeinsamen Kontext haben. Verwenden Sie also einen einzigen Kontext für den gesamten Geltungsbereich der Main Methode.

0voto

Rick B Punkte 1146

Die Komplexität, die Sie erwähnen (die ich mit Ihnen übereinstimme) wird durch die Verwendung einer statischen Klasse für Ihre Datenzugriffskomponente verursacht. Es zwingt Sie, Ihre DBContext's über Methodenaufrufe zu trennen. Anstatt es auf diese Weise zu tun, warum nicht erstellen Sie eine normale Klasse, und bauen Sie den Kontext im Konstruktor.

Damit ist es nicht mehr nötig, foo.Bar anzuhängen.

public class DataAccess
{
    private MyDbContext _context;

    public DataAccess(){
        _context = new MyDbContext();
    }

    public Bar GetBarById(int id)
    {
        return _context.Bars.SingleOrDefault(b => b.Id == id);
    }

    public Foo InsertFoo(Foo foo)
    {
        _context.Foos.Add(foo);

        _context.SaveChanges();

        return foo;
    }
}

Es gibt viele Möglichkeiten, dies auszubauen und zu verbessern. Sie könnten eine Schnittstelle für MyDbContext erstellen, die IDbContext und mithilfe eines DI-Frameworks in diese Klasse injizieren. In ähnlicher Weise könnten Sie das Gleiche für die Klasse DataAccess Klasse und injizieren diese überall dort, wo sie benötigt wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X