Подтвердить что ты не робот

EntityFramework, Insert, если не существует, в противном случае обновление

У меня есть страны-объекты, отражающие таблицу базы данных '<' 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.

Надеюсь, что у кого-то были эти проблемы раньше, и у меня есть решение для меня. Если я не могу использовать объект Country и вставлять/обновлять массив из них легко, я не вижу много пользы от использования фреймворка, так как от исполнителей я думаю, что быстрее написать пользовательский sql script, который вставляет их вместо проверки ect, если страна уже находится в базе данных перед вставкой?

Решение

См. ответ сообщения вместо.

Я добавил, что переопределение соответствует классу моей страны:

    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();
        }
4b9b3361

Ответ 1

Я сделал бы это прямо:

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();
     }
}

Я не знаю, как часто ваше приложение должно запускать это или сколько стран мира. Но я чувствую, что это ничего, где вы должны думать о сложных оптимизации производительности.

Edit

Альтернативный подход, который выдает только один запрос:

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();
     }
}

Ответ 2

Современная форма, использующая более поздние версии EF, будет:

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

AlreadyExists может исходить из проверки ключа или запроса базы данных, чтобы узнать, существует ли элемент там.

Ответ 3

Вы можете реализовать свой собственный IEqualityComparer<Country> и передать его методу Except(). Предполагая, что у объекта вашей страны есть свойства 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());

Хотя в вашем случае похоже, что вам просто нужно извлекать новые объекты, вы можете использовать var newCountries = data.Where(c => c.Id == Guid.Empty);, если ваш Id - Guid.

Лучший способ - проверить свойство Country.EntityState и предпринять действия оттуда относительно значения (Отдельно, Модифицировано, Добавлено и т.д.)

Вам нужно предоставить дополнительную информацию о том, что содержит ваша коллекция data, т.е. объекты Страны, извлеченные из базы данных через сущность, в этом случае их контекст можно отслеживать или вы генерируете их каким-либо другим способом.

Ответ 4

Я не уверен, что это будет лучшее решение, но я думаю, что вам нужно получить все страны от БД, а затем проверить его с помощью проанализированных данных.

 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();
   }
  }