1

codefirst Entity Framework で多くの問題が発生しています。

と の 2 つのテーブルを持つデータベースがありUsersますCountriesUsersの外部キーを持つフィールドがありますCountryId。ユーザーが最初にデータベースに追加されると、FK が正常に更新されます。ただし、新しい DbContext にアタッチするユーザーの FK を更新しようとしても、変更は行われません。

最初にユーザーを作成したコンテキストで FK を更新できます。また、データベースからユーザーを取得するコンテキストで FK を更新できます。ただし、Attachユーザーだけのコンテキストでは、通常のフィールドnvarcharまたはintフィールドが更新されても、FK フィールドは更新されません。

ここに私のDbContextがあります:

class DataContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Country> Countries { get; set; }
}

そして、ここに私のモデルがあります:

public class User
{
    public int UserId { get; private set; }
    public string Name { get; set; }
    public virtual Country Country { get; set; }

}

public class Country
{
    public int CountryId { get; private set; }
    public string Name { get; set; }
}

このメソッドは、問題を示しています。データベースに 3 人のユーザーを追加し、3 つの異なる方法でそれらを変更しようとしました。ユーザーが追加された元のコンテキスト、Attach.変更:

static void Main(string[] args)
{
    Country[] countries;

    using (var dc = new DataContext())
    {
        countries = dc.Countries.ToArray();
    }

    var u = new User();

    // FIRST ROW
    using (var dc = new DataContext())
    {
        u.Name = "First";
        u.Country = countries[0];
        dc.Users.Attach(u);
        dc.Entry(u).State = System.Data.EntityState.Added;
        dc.SaveChanges();

        u.Name = "FirstChanged";
        u.Country = countries[1];
        // defaults to 'added' once u is tracked.
        dc.Entry(countries[1]).State = EntityState.Unchanged; 
        dc.SaveChanges();
    }

    // SECOND ROW
    u = new User();
    using (var dc = new DataContext())
    {
        u.Name = "Second";
        u.Country = countries[0];
        dc.Users.Attach(u);
        dc.Entry(u).State = System.Data.EntityState.Added;
        dc.SaveChanges();
    }

    using (var dc = new DataContext())
    {
        u.Name = "SecondChanged";
        u.Country = countries[1];
        dc.Users.Attach(u);
        dc.Entry(u).State = System.Data.EntityState.Modified;
        dc.SaveChanges();
    }

    // THIRD ROW
    u = new User();
    using (var dc = new DataContext())
    {
        u.Name = "Third";
        u.Country = countries[0];
        dc.Users.Attach(u);
        dc.Entry(u).State = System.Data.EntityState.Added;
        dc.SaveChanges();
    }

    using (var dc = new DataContext())
    {
        u = dc.Users.Find(u.UserId);
        u.Name = "ThirdChanged";
        u.Country = countries[1];
        dc.Entry(countries[1]).State = EntityState.Modified;
        dc.SaveChanges();
    }
}

このアプリケーションは、データベースで次の結果を生成します。

UserId  Name    Country_CountryId   
------------------------------------------------------------------------
1   FirstChanged    2
2   SecondChanged   1
3   ThirdChanged    2

ご覧のとおり、すべてのケースNameで正常に更新されました。最初のケースと 3 番目のケースでは、CountryIdが更新されます。ただし、2 番目のケースでは、CountryId は更新されません。残念ながら、DataContext の外部でユーザーが編集するためにモデルを永続化できるようにしたいので、これは私にとって最も関連性の高いケースです。

なぜこれが起こっているのかわかりません.Nameフィールドが更新されなかった場合は理解できましたが、そのCountryId理由はわかりません. どちらの場合も、Countryオブジェクトは追跡され、 としてマークされUnchangedます。2番目のケースだけが機能しない理由がわかりません。

ここに何があるかを理解するのを手伝ってください。私が考えることができる唯一の解決策は、変更する必要があるときに3番目のケースを使用することです.保存すると、永続化されたオブジェクトから保存されたデータベースから取得された新しいオブジェクトにデータをコピーします...しかし、明らかにこれはばかげた解決策。

以下に、この問題を示すコンソール アプリケーションを示します。それを実行するには、NuGet (または同様のもの) と次のような接続文字列を使用して EntityFramework を取得する必要があります。

<add name="DataContext" connectionString="DataSource=.\DataContext.sdf" providerName="System.Data.SqlServerCe.4.0" />

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Country[] countries;

            using (var dc = new DataContext())
            {
                countries = dc.Countries.ToArray();
            }

            var u = new User();

            // FIRST ROW
            using (var dc = new DataContext())
            {
                u.Name = "First";
                u.Country = countries[0];
                dc.Users.Attach(u);
                dc.Entry(u).State = System.Data.EntityState.Added;
                dc.SaveChanges();

                u.Name = "FirstChanged";
                u.Country = countries[1];
                // defaults to 'added' once u is tracked.
                dc.Entry(countries[1]).State = EntityState.Unchanged;
                dc.SaveChanges();
            }

            // SECOND ROW
            u = new User();
            using (var dc = new DataContext())
            {
                u.Name = "Second";
                u.Country = countries[0];
                dc.Users.Attach(u);
                dc.Entry(u).State = System.Data.EntityState.Added;
                dc.SaveChanges();
            }

            using (var dc = new DataContext())
            {
                u.Name = "SecondChanged";
                u.Country = countries[1];
                dc.Users.Attach(u);
                dc.Entry(u).State = System.Data.EntityState.Modified;
                dc.SaveChanges();
            }

            // THIRD ROW
            u = new User();
            using (var dc = new DataContext())
            {
                u.Name = "Third";
                u.Country = countries[0];
                dc.Users.Attach(u);
                dc.Entry(u).State = System.Data.EntityState.Added;
                dc.SaveChanges();
            }

            using (var dc = new DataContext())
            {
                u = dc.Users.Find(u.UserId);
                u.Name = "ThirdChanged";
                u.Country = countries[1];
                dc.Entry(countries[1]).State = EntityState.Modified;
                dc.SaveChanges();
            }
        }
    }

    class DataContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public DbSet<Country> Countries { get; set; }

        public DataContext()
        {
            Database.SetInitializer<DataContext>(new ModelInitializer());
            Database.Initialize(false);
        }
    }

    class ModelInitializer : DropCreateDatabaseAlways<DataContext>
    {
        protected override void Seed(DataContext context)
        {
            var jsds = new List<Country>
                {
                    new Country("United Kingdom"),
                    new Country("United States Of America")
                };

            jsds.ForEach(jsd => context.Countries.Add(jsd));
        }
    }

    public class User
    {
        public int UserId { get; private set; }
        public string Name { get; set; }
        public virtual Country Country { get; set; }

    }

    public class Country
    {
        public int CountryId { get; private set; }
        public string Name { get; set; }

        public Country() { }

        public Country(string name)
        {
            Name = name;
        }
    }
}

この質問をご覧いただきありがとうございます。私はそれを修正しようとして2日目ですが、ゆっくりと夢中になっています...

4

1 に答える 1

5

これは、外部キーの関連付けと独立した関連付けの違いに基づくトリッキーな問題です。外部キーアソシエーションを使用する場合(エンティティにCountryIdプロパティがありますUser)、2番目のケースも機能しますが、アソシエーション自体が独自の状態を持ち、それをAdded機能させるために設定する必要がある独立したアソシエーションを使用しています。DbContext アソシエーションの状態を変更するAPIを提供していないため、を使用する必要がありますObjectContext

u = new User();
using (var dc = new DataContext())
{
    u.Name = "Second";
    u.Country = countries[0];
    dc.Users.Attach(u);
    dc.Entry(u).State = System.Data.EntityState.Added;
    dc.SaveChanges();
}

using (var dc = new DataContext())
{
    u.Name = "SecondChanged";
    u.Country = countries[1];
    dc.Users.Attach(u);
    ObjectContext oc = ((IObjectContextAdapter)dc).ObjectContext;
    oc.ObjectStateManager.ChangeObjectState(u, EntityState.Modified);
    oc.ObjectStateManager.ChangeRelationshipState(u, countries[1], x => x.Country, EntityState.Added);
    dc.SaveChanges();
}

独立した関連付けを使用すると、多くの問題が発生する可能性があります。

于 2012-04-17T12:01:36.000 に答える