Избегайте нарушения ключа при вставке в EF - PullRequest
2 голосов
/ 24 января 2011

Я использую Entity Framework 4 и SQL Server 2008 R2.У меня есть ситуация, когда я хотел бы вставить запись, чьи поля первичного ключа могут соответствовать существующей записи.Вместо повышения и ошибки, я бы хотел, чтобы существующая запись молча обновлялась новыми данными.

Моя текущая реализация выглядит примерно так:

// Calculate primary key fields.
DateTime endDate = periodTime.AddMinutes(periodMins);
int meterId = pipe.MeterId;

// Try to load a matching record.
ProfileDatum lpRec = context.ProfileData.FirstOrDefault(rec => rec.MeterId == meterId && rec.EndDate == endDate);
// If it could not be loaded, create a new one.
if (lpRec == null)
{
    lpRec = new ProfileDatum() {MeterId = meterId,
                                EndDate = endDate};

    // Save the object in the context.
    context.ProfileData.AddObject(lpRec);
}

// Fill lpRec.

Это работает, но этодовольно ужасно по эффективности, удобочитаемости и элегантности.

Придерживаясь Entity Framework и SQL Server 2008 R2, есть ли способ сделать что-то вроде MySQL «при обновлении дублированного ключа», где я продолжаю, как будто создаю новую записькаждый раз и обновлять базу данных, когда есть нарушение первичного ключа?Или, может быть, есть какой-то другой элегантный подход?

Эффективность хороша, но вторична в этом случае;это простота, я после.Было бы замечательно иметь возможность написать это следующим образом и опустить проверку на стороне клиента:

// Assign primary key fields.
var lpRec = new ProfileDatum()
{
    MeterId = pipe.MeterId,
    EndDate = periodTime.AddMinutes(periodMins)
};

// Fill lpRec.

// Save the object in the context.
context.ProfileData.AddObject(lpRec);

Заранее благодарен за любую помощь, даже если это говорит о том, что моя существующая реализация - лучший способ.

Ответы [ 3 ]

2 голосов
/ 24 января 2011

Можете ли вы использовать ObjectContext.GetObjectByKey / TryGetObjectByKey?

Согласно справке MSDN: http://msdn.microsoft.com/en-us/library/bb738728.aspx

TryGetObjectByKey сначала рассмотрит список загруженных объектов. Если объекта там нет, он попытается загрузить его из хранилища с помощью PK. Если загрузка не удалась (ее нет в базе данных), TryGetObjectByKey вернет false (GetObjectByKey сгенерирует)


IEnumerable<KeyValuePair<string, object>> entityKeyValues =
  new KeyValuePair<string, object>[]
  {
    new KeyValuePair<string, object> ("MeterId", meterId);
  }

  EntityKey key = new EntityKey("YourContainerName.YourEntitySetName", entityKeyValues);
  object entity = null;
  if(context.TryGetObjectByKey(key, out entity))
  {
     //the entity exists either in memory, or in the db
  }
  else
  {
     // you need to insert a new one
  }

1 голос
/ 24 января 2011

С точки зрения эффективности, наилучшим вариантом будет позволить, чтобы это произошло в БД. Итак, вам нужно установить для свойства StoreGeneratedPattern вашего ключа значение None, а затем указать методы вставки и обновления для хранимых процедур в БД, использующих функцию MERGE в SQL Server, как описано здесь: http://msdn.microsoft.com/en-us/library/bb522522.aspx

Это именно для того, чтобы делать то, о чем вы просили, максимально эффективно.

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

0 голосов
/ 24 января 2011

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

Если у вас возникнут серьезные проблемы с эффективностью, вы можете настроитьваша база данных для автоматической вставки значений, когда они не найдены.Но это приводит к тому, что ваша модель ведет себя «магически», поэтому, если эффективность вторична, я бы оставил ваш код более или менее как есть (возможно, воспользовавшись предложением JMarsh, чтобы повысить вероятность того, что вам не придется делать обход базы данных)).

...