Моя программа более сложная, но в основном проблема в следующем: есть таблица Customer и таблица City. В Customor у меня есть поле cityId и внешний ключ для него.
Теперь я могу написать такой код
customer.cityIdCity = myCity;
или
customer.cityId = 7;
Проблема в том, что я не могу использовать первую форму, потому что города кэшируются, и в этом случае данные будут «захвачены» текущим контекстом данных (и возникнет исключение), поэтому я предпочитаю использовать вторую форму. Однако, когда я отправляю изменения для клиента, я получаю исключение о несоответствии в данных - что честно говоря - cityId равно 7, но cityIdCity равно нулю.
Вопрос - для второй формы, как заставить LINQ сосредоточиться на идентификаторе (и выполнить простую вставку с использованием идентификатора) и принять пропущенный объект.
редактирует
1
Поле cityId является просто int (как в таблице), но sqlmetal также создал другое свойство для класса отображения cityIdCity, которое является ссылкой на класс City (из таблицы City).
Внутренне это выглядит так:
private EntityRef<City> _cityIdCity;
Существует также длинное свойство cityIdCity, которое устанавливает указанное поле и обрабатывает запуск событий для изменения и изменения состояния поля.
И регулярное поле (int) определяется так:
private System.Nullable<int> _cityId;
плюс аналогичное свойство.
2
Я обнаружил, что это связано с получением данных. После загрузки данных (в данном случае это объект city для клиента), Linq To SQL предполагает, что оно fixed , т. Е. Идентификатор и ссылка на город должны быть синхронизированы. Если вы не читаете их до изменения, это нормально, потому что L2S устанавливает новые значения, но также извлекает новые данные из БД на лету.
И я должен прочитать город покупателя, прежде чем изменить его.
3
Шаги следующие:
- найти в БД клиента
- его не существует? -> создать запись
- совпадает ли город клиента с входящими данными (город)? если да, перейдите к шагу (8)
- проверьте в кеше, если у нас есть соответствующий город, если да, перейдите к (7)
- создать запись города
- сохранить город
- связать город и клиента
- сохранить клиента
Таким образом, в (3) читается поле города клиента, а в (7) есть жалоба на использование данных из другого контекста или несинхронизированные поля (случай, когда данные изменяются).
Для сравнения я протестировал несколько случаев с использованием Entity Framework, и он ведет себя более просто, то есть нет проблем в обоих подходах (при изменении ссылочного поля с целым объектом или просто идентификатором).
4
Это упрощенный код, но показывает проблему.
using (var Db = new L2S.DataClasses1DataContext())
{
var customer = Db.Customers.Single(it => it.cust_Id==2);
customer.Name = "New name";
Console.WriteLine(customer.City.city_Name); // loading city from DB
// cannot change, because we would be out of sync with referenced object
customer.city_Id = 57834;
Db.SubmitChanges();
}
5 - тонкий или жирный контекст данных
Используя тот же пример, что и Pleun. Это жирный ДК:
using (var Db = new L2S.DataClasses1DataContext())
{
...
customer.City = Db.Cities.Single (i=> i.id = 57834 );
Db.SubmitChanges();
}
потому что, как вы можете видеть, клиент использует тот же DC, что и города (города кэшируются!). Это неправильно, потому что все искаженные данные от клиента будут перетекать в DC (кеш). Так что ошибка часа назад тоже будет присутствовать.
Это тонкий DC:
var city = CacheDb.Cities.Single (i=> i.id = 57834 );
...
using (var Db = new L2S.DataClasses1DataContext())
{
...
customer.City = city;
Db.SubmitChanges();
}
Однако это не будет работать, потому что L2S не позволяет совместно использовать объекты между DC (в типичном случае между кешем и работающим - обновление - DC).