web-dev-qa-db-ja.com

EntityFramework、存在しない場合は挿入、それ以外の場合は更新

データベースにデータベーステーブル '<' char(2)、char(3)、nvarchar(50>)を反映するエンティティセットカントリーがあります。

解析された国のCountry []配列を返すパーサーがあり、正しい方法で更新することに問題があります。私が欲しいのは:国の配列を取り、データベースにまだない国についてはそれらを挿入し、フィールドが異なる場合は既存の更新を挿入します。これはどのように行うことができますか?

void Method(object sender, DocumentLoadedEvent e)
{
    var data = e.ParsedData as Country[];
    using(var db = new DataContractEntities)
    {
       //Code missing


    }
}

私は何かを考えていました

for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields.

誰かが以前にこの問題を抱えていて、私のための解決策があることを願っています。国オブジェクトを使用できず、それらの配列を簡単に挿入/更新できない場合、フレームワークを使用するメリットはあまりありません。パフォーマーからは、国かどうかをチェックする代わりにそれらを挿入するカスタムSQLスクリプトを書く方が速いと思います挿入する前にすでにデータベースにありますか?

解決

代わりに投稿の回答をご覧ください。

私は私の国のクラスに等しいオーバーライドを追加しました:

    public partial class Country
{

    public override bool Equals(object obj)
    {
        if (obj is Country)
        {
            var country = obj as Country;
            return this.CountryTreeLetter.Equals(country.CountryTreeLetter);
        }
        return false;
    }
    public override int GetHashCode()
    {
        int hash = 13;
        hash = hash * 7 + (int)CountryTreeLetter[0];
        hash = hash * 7 + (int)CountryTreeLetter[1];
        hash = hash * 7 + (int)CountryTreeLetter[2];
        return hash;
    }
}

そしてしました:

        var data = e.ParsedData as Country[];
        using (var db = new entities())
        {
            foreach (var item in data.Except(db.Countries))
            {
                db.AddToCountries(item); 
            }
            db.SaveChanges();
        }
23

私はそれを簡単に行います:

void Method(object sender, DocumentLoadedEvent e)
{
    var data = e.ParsedData as Country[];
    using(var db = new DataContractEntities)
    {
        foreach(var country in data)
        {
            var countryInDb = db.Countries
                .Where(c => c.Name == country.Name) // or whatever your key is
                .SingleOrDefault();
            if (countryInDb != null)
                db.Countries.ApplyCurrentValues(country);
            else
                db.Countries.AddObject(country);
        }
        db.SaveChanges();
     }
}

あなたのアプリケーションがこれを実行しなければならない頻度や、あなたの世界がいくつの国を持っているのか、私にはわかりません。しかし、これは高度なパフォーマンスの最適化について考えなければならないことではないと感じています。

編集

1つのクエリのみを発行する代替アプローチ:

void Method(object sender, DocumentLoadedEvent e)
{
    var data = e.ParsedData as Country[];
    using(var db = new DataContractEntities)
    {
        var names = data.Select(c => c.Name);
        var countriesInDb = db.Countries
            .Where(c => names.Contains(c.Name))
            .ToList(); // single DB query
        foreach(var country in data)
        {
            var countryInDb = countriesInDb
                .SingleOrDefault(c => c.Name == country.Name); // runs in memory
            if (countryInDb != null)
                db.Countries.ApplyCurrentValues(country);
            else
                db.Countries.AddObject(country);
        }
        db.SaveChanges();
     }
}
25
Slauma

最新のEFバージョンを使用した形式は次のとおりです。

context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added);
context.SaveChanges();

AlreadyExistsは、キーのチェックから、またはデータベースにクエリを実行して、アイテムが既にそこに存在するかどうかを確認することで取得できます。

9
Gábor

独自の_IEqualityComparer<Country>_を実装して、それをExcept()メソッドに渡すことができます。 CountryオブジェクトにIdおよびNameプロパティがあるとすると、その実装の一例は次のようになります。

_public class CountryComparer : IEqualityComparer<Country>
{
    public bool Equals(Country x, Country y)
    {
        return x.Name.Equals(y.Name) && (x.Id == y.Id);
    }

    public int GetHashCode(Country obj)
    {
        return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
    }
}
_

そしてそれを

_data.Countries.Except<Country>(db, new CountryComparer());
_

あなたの場合、新しいオブジェクトを抽出する必要があるように見えますが、IDがGuidの場合はvar newCountries = data.Where(c => c.Id == Guid.Empty);を使用できます。

最善の方法は、_Country.EntityState_プロパティを検査し、そこから値(分離、変更、追加など)に関してアクションを実行することです。

dataコレクションに含まれるものに関する詳細情報を提供する必要があります。つまり、entityframeworkを介してデータベースから取得されたCountryオブジェクトです。この場合、コンテキストを追跡できるか、他の方法で生成していますか。

0

これが最良の解決策になるかどうかはわかりませんが、すべての国をDBから取得し、解析済みデータで確認する必要があると思います

 void Method(object sender, DocumentLoadedEvent e)
 {
    var data = e.ParsedData as Country[];
    using(var db = new DataContractEntities)
    {
       List<Country> mycountries = db.Countries.ToList();
       foreach(var PC in data)
       {
          if(mycountries.Any( C => C.Name==PC.Name ))
          {
             var country = mycountries.Any( C => C.Name==PC.Name );
             //Update it here
          }
          else
          {
               var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters
               newcountry.Name = PC.Name;
               db.AddToCountries(newcountry)
          }
       }
       db.SaveChanges();
   }
  }
0
Amir Ismail