13

Entity Framework モデルを生成した単純なスノーフレーク スキーマがあります。問題は、子エンティティを既存の親および/または祖父母
エンティティ にマップしようとしているが、それでも挿入されることです。

私はこれに従いました:

既存のオブジェクトを使用して新しいオブジェクトを挿入する
Entity Framework がナビゲーション プロパティの値を挿入できないようにする

興味深いのは、親エンティティの EntityState が "Unchanged" であっても、Entity Framework がそれを挿入しようとすることです。


スキーマ

ここに画像の説明を入力


CarRepository.Save() メソッド

public void Save(Car car)
    {
        using (DBContext context = new DBContext())
        {
            // No need to save if it already exists
            if ( context.Cars.FirstOrDefault(x => x.RegistrationNumber == car.RegistrationNumber) != null)
            {
                return;
            }
            else
            {
                // Check if the parent POCOs exist in the DB. 
                Model existingModel = context.Models.FirstOrDefault(x => x.Name == car.Model.Name);
                Manufacturer existingManufacturer = context.Manufacturers.FirstOrDefault(x=> x.Name == car.Model.Manufacturer.Name)
                Trader existingTrader = context.Traders.FirstOrDefault(x=> x.Name == car.Trader.Name)
                TraderCompany existingTraderCompany = context.TraderCompanys.FirstOrDefault(x=> x.Name == car.Trader.TraderCompany.Name)

                context.ContextOptions.LazyLoadingEnabled = false;

                //Attach to the context if existing in the DB, i.e mark the existing POCOs not to be added the DB
                if (existingModel != null)
                {
                    car.Model = existingModel;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model).State == EntityState.Unchanged);
                }

                if (existingManufacturer != null)
                {
                    car.Model.Manufacturer = existingManufacturer;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model.Manufacturer).State == EntityState.Unchanged);
                }

                if (existingTrader != null)
                {
                    car.Trader = existingTrader;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Trader).State == EntityState.Unchanged);
                }

                if (existingTraderCompany != null)
                {
                    car.Trader.TraderCompany = existingTraderCompany;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Trader.TraderCompany).State == EntityState.Unchanged);
                }

                //Mark the Car for Addition to the DB
                context.Cars.AddObject(car);
                context.ObjectStateManager.ChangeObjectState(car, EntityState.Added);


                //If the POCOs do not exist in the DB mark them for addition
                if (existingModel == null)
                {
                   context.ObjectStateManager.ChangeObjectState(car.Model,EntityState.Added);
                }

                if (existingManufacturer == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Model.Manufacturer,EntityState.Added);
                }

                if (existingTrader == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Trader,EntityState.Added);
                }

                if (existingTraderCompany == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Trader.TraderCompany,EntityState.Added);
                }

                context.SaveChanges();

            }
        }
    }

編集:

数日間いじくり回した後、私はうまくいく回避策を思いつくことができました。

CarRepository.Save() に渡されている Car には、検出できないある種の内部コンテキストがあるようです...そのため、そのコンテキストから切り離して、 CarRepository.Save()。実際にこのコンテキストに追加するために、Car オブジェクトとそのナビゲーション プロパティ(存在する場合) をディープ/レイジー コピーします。


回避策

public void Save(Car car)
{
    using (DBContext context = new DBContext())
    {
        // No need to save if it already exists
        if ( context.Cars
                    .Any(x => x.RegistrationNumber == car.RegistrationNumber))
        {
            return;
        }
        else
        {
            //Assign scalar properties to the deep copy
            Car carToBeSaved = new Car 
            {
                carToBeSaved.RegistrationNumber = car.RegistrationNumber,
                carToBeSaved.Price = car.Price
            }


            //Car -> Trader -> ...
            if(car.Trader != null)
            {   
                Trader existingTrader = 
                    context.Traders
                           .FirstOrDefault(x => x.Name == car.Trader.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Trader = existingTrader ??
                    new Trader
                    {
                        Name = car.Trader.Name,
                        JobTitle = car.Trader.JobTitle
                    }

                //Car -> Trader -> TraderCompany
                if(car.Trader.TraderCompany != null)
                {
                    TraderCompany existingTraderCompany = 
                        context.TradersCompanys
                               .FirstOrDefault(x => x.Name == car.Trader
                                                                 .TraderCompany
                                                                 .Name);

                    //If exists in DB assign, if not deep copy  
                    carToBeSaved.Trader.TraderCompany = existingTraderCompany ??
                        new TraderCompany
                        {
                            Name = car.Trader.TraderCompany.Name,
                            Address = car.Trader.TraderCompany.Address,
                            PhoneNumber = car.Trader.TraderCompany.PhoneNumber
                        }
                }
            }

            //Car -> Model -> ...
            if(car.Model != null)
            {   
                Model existingModel = 
                    context.Models
                           .FirstOrDefault(x => x.Name == car.Model.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Model = existingModel ??
                    new Model
                    {
                        Name = car.Model.Name
                    }

                //Car -> Model -> Manufacturer
                if(car.Model.Manufacturer != null)
                {
                    Manufacturer existingManufacturer = 
                        context.Manufacturers
                               .FirstOrDefault(x => x.Name == car.Model
                                                                 .Manufacturer
                                                                 .Name);

                    //If exists in DB assign, if not deep copy
                    carToBeSaved.Model.Manufacturer = existingManufacturer ??
                    new Manufacturer
                        {
                            Name = car.Model.Manufacturer.Name
                        }
                }
            }

            //Mark the Car for Addition to the DB
            context.Cars.AddObject(car);
            context.SaveChanges();

        }
    }
}

誰かがこれについて考えている場合は、共有してください。

ありがとう。

4

3 に答える 3

3

この種の問題に関して私が見つけた最も信頼できる方法は、エンティティ インスタンスを関連付ける代わりに、既存のオブジェクトの Id を使用することです。したがって、既存のトレーダーを見つけた後、次のようにトレーダーに「外部」キーを設定します。

car.TraderId = existingTrader.Id;

しばらくの間、これはハックのように思えましたが、2013 年 4 月の MSDN mag で、Julie Lerman もこのアプローチを推奨しているのを読みました。

于 2013-05-08T18:20:18.143 に答える
0

次の問題があると思います。

ここのように Model をexistingModelに設定すると

if (existingModel != null)
{
    car.Model = existingModel;
    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model).State == EntityState.Unchanged);
}

次に、ObjectStateManager は車が新しいことを検出し、自動的にコンテキストに追加します。このため、EF は、たとえば、プリセットされた車を検出します。Traderは不明 (新しいことを意味します) であり、それも追加します。等々。

そういうわけで、論文はとにかく保存されます。

于 2012-11-16T16:50:39.923 に答える
0

数日間いじくり回した後、私はうまくいく回避策を思いつくことができました。

CarRepository.Save() に渡されている Car には、検出できないある種の内部コンテキストがあるようです...そのため、そのコンテキストから切り離して、 CarRepository.Save()。実際にこのコンテキストに追加するために、Car オブジェクトとそのナビゲーション プロパティ(存在する場合) をディープ/レイジー コピーします。


回避策

public void Save(Car car)
{
    using (DBContext context = new DBContext())
    {
        // No need to save if it already exists
        if ( context.Cars
                    .Any(x => x.RegistrationNumber == car.RegistrationNumber))
        {
            return;
        }
        else
        {
            //Assign scalar properties to the deep copy
            Car carToBeSaved = new Car 
            {
                carToBeSaved.RegistrationNumber = car.RegistrationNumber,
                carToBeSaved.Price = car.Price
            }


            //Car -> Trader -> ...
            if(car.Trader != null)
            {   
                Trader existingTrader = 
                    context.Traders
                           .FirstOrDefault(x => x.Name == car.Trader.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Trader = existingTrader ??
                    new Trader
                    {
                        Name = car.Trader.Name,
                        JobTitle = car.Trader.JobTitle
                    }

                //Car -> Trader -> TraderCompany
                if(car.Trader.TraderCompany != null)
                {
                    TraderCompany existingTraderCompany = 
                        context.TradersCompanys
                               .FirstOrDefault(x => x.Name == car.Trader
                                                                 .TraderCompany
                                                                 .Name);

                    //If exists in DB assign, if not deep copy  
                    carToBeSaved.Trader.TraderCompany = existingTraderCompany ??
                        new TraderCompany
                        {
                            Name = car.Trader.TraderCompany.Name,
                            Address = car.Trader.TraderCompany.Address,
                            PhoneNumber = car.Trader.TraderCompany.PhoneNumber
                        }
                }
            }

            //Car -> Model -> ...
            if(car.Model != null)
            {   
                Model existingModel = 
                    context.Models
                           .FirstOrDefault(x => x.Name == car.Model.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Model = existingModel ??
                    new Model
                    {
                        Name = car.Model.Name
                    }

                //Car -> Model -> Manufacturer
                if(car.Model.Manufacturer != null)
                {
                    Manufacturer existingManufacturer = 
                        context.Manufacturers
                               .FirstOrDefault(x => x.Name == car.Model
                                                                 .Manufacturer
                                                                 .Name);

                    //If exists in DB assign, if not deep copy
                    carToBeSaved.Model.Manufacturer = existingManufacturer ??
                    new Manufacturer
                        {
                            Name = car.Model.Manufacturer.Name
                        }
                }
            }

            //Mark the Car for Addition to the DB
            context.Cars.AddObject(car);
            context.SaveChanges();

        }
    }
}

誰かがこれについて考えている場合は、共有してください。

于 2012-11-01T09:18:59.200 に答える