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();
}
Ответы
Ответ 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();
}
}