2015-04-25 3 views
3

Вот простой объект:Entity Framework обновление виртуальной собственности напрямую, без создания новой записи

public class Customer : Entity 
{ 
    public virtual Location Location { get; set; } 
} 

Теперь предположим, что у нас есть клиент уже:

var customer = new Customer() {Location = new Location("China")}; 

и теперь мы хотим обновить его местоположение:

var customer = context.Customers.First(x => x.Location.Country == "China"); 
customer.Location = new Location("America"); 
context.SaveChanges(); 

и теперь, когда я смотрю базу данных, запись местоположения «Китай» не была удалена: В базе данных теперь имеется две записи записей местоположений с одной записью клиента.

Причины этого вопроса является то, что я использую виртуальное ключевое слово на собственности Customer.Location, и когда я запрашиваю объект клиента из базы данных я не использовал Включить метод для загрузки Местонахождение объекта, и я также не использовал доступа к ленивой загрузке. Поэтому EF не может отслеживать и знать China Объект местоположения должен быть удален.

Я думаю, что подход, который я использовал для виртуального свойства обновления, соответствует интуиции. Я хочу обновить свойство, а затем просто использовать инструкцию по обновлению «entity.xxx = ...», добавить принудительно использовать некоторый доступ к вызову свойства или метода при загрузке «entity.xxx» не является интуитивным.

Так что я ищу лучший способ заменить виртуальную собственность объекта напрямую. Какие-либо предложения?


Solutions обновить

Я найти два способа сделать это легко,

Сначала вы можете использовать Identifying relation (рекомендуем).

Другим способом вы можете использовать ObjectContext.DeleteObject метод, ниже приведен пример кода:

public static class EFCollectionHelper 
{ 
    public static void UpdateCollection<T, TValue>(this T target, 
Expression<Func<T, IEnumerable<TValue>>> memberLamda, TValue value)where T : Entity 
    { 
     var memberSelectorExpression = (MemberExpression)memberLamda.Body; 
     var property = (PropertyInfo)memberSelectorExpression.Member; 

     var oldCollection = memberLamda.Compile()(target); 
     oldCollection.ClearUp(); 

     property.SetValue(target, value, null); 
    } 

    public static void ClearUp<T>(this IEnumerable<T> collection) 
    { 
     //Convert your DbContext to IObjectContextAdapter 
     var objContext = ((IObjectContextAdapter) Program.DbContext).ObjectContext; 
     for (int i = 0; i < collection.Count(); i++) 
     { 
      objContext.DeleteObject(collection.ElementAt(i)); 
     } 
    } 
} 

И тогда вы можете просто написать код, как:

customer.UpdateCollection(x => x.Locations, null); 
+0

Когда вы говорите, «запись о местоположении„Китай“не был удален: база данных теперь имеет две записи местоположение» сделать вас означает, что в базе данных теперь есть две записи клиентов, одна с Китаем и одна с Америкой в ​​качестве локаций? Или вы имеете в виду, что теперь есть две записи местоположения, как кажется, вы говорите. Если в настоящее время есть две записи местоположения, это не обязательно неправильно. Если ваша таблица местоположений представляет собой таблицу типа поиска, которая должна иметь возможные местоположения, теперь у вас есть два возможных местоположения в таблице, и ваша запись о клиенте теперь правильна, поскольку она указывает на Америку (Америка). – DWright

+0

@DWright, извините за путаницу, база данных теперь имеет две ассоциации записей местоположения с одной записью клиента, и да, клиент. Место укажет на Америку, но в базе данных он будет генерировать запись местоположения каждый раз, когда я использую «клиент». Местоположение = новое местоположение() для обновления местоположения клиента. – ilovezcd

+0

И еще один способ, если виртуальное свойство представляет собой коллекцию, затем используйте «customer.collection = xxx ...», чтобы вставить новую коллекцию в базу данных, а в следующий раз, когда вы получите клиента из базы данных, «customer.collection» будет двойной на самом деле. – ilovezcd

ответ

4

Не совсем уверен, что вы хотите, но это то, что я получил.

Причина теперь получить два места есть, потому что вы используете new Location("American"); вы на самом деле добавить ссылку на новое место (EF не знаю, если Китай используется другим клиентом, и никогда бы не удалить его, в том, что тип запроса)

Теперь, если вы сказали.

customer.Location.Country = "America" 

The Китай будет перезаписана Америки, как мы сейчас работаем с конкретным свойством Location «s.

Прочитайте коментарии по этому вопросу так немного дополнительных

Если вы хотите обновить информацию о местонахождении полностью (new Location("Some new location")). Тогда вы сделаете это так.

Location oldLocation = customer.Location; 
Location newLocation = new Location("America"); 
//Check if the new location country !exist 
if(!context.Locations.Any(a=> a.Country == newLocation.Country)) 
{ 
    //If it don't exist then add it (avoiding the location duplicate) 
    customer.Location = newLocation; 
    //If its important to delete the old location then do this 
    //(important to do after you removed the dependency, 
    //thats why its after the new location is added) 
    context.Locations.Remove(oldLocation) 
    //Finally Save the changes 
    context.SaveChanges(); 
} 
+0

Да, у меня есть то, что вы пытаетесь предложить на самом деле, это нормально работает. Но я действительно хочу «заменить» не просто «обновление», если я хочу обновить свойство коллекции клиента, потому что новые элементы коллекции, возможно, не равны старой коллекции, это решение не будет работать. – ilovezcd

+0

База данных не заменяет эту базу данных, чтобы найти данные и запустить SQL-запрос на удаление. –

+0

Точно! так есть ли какой-либо способ позволить EF генерировать запрос на удаление SQL, когда я использую «entity.xxxx = ...»? – ilovezcd

1

Другой способ обновить объект использует Entry.OriginalValues.SetValues метод:

var currentLocation = context.Customers.First(x => x.Location.Country == "China").Select(c => c.Location); 
context.Entry(currentLocation).OriginalValues.SetValues(newLocation) ; 
context.SaveChanges(); 
Смежные вопросы