Обновление Linq to SQL — исключение NullReferenceException при обновлении после отсоединения

#linq #linq-to-sql #linq-to-entities

#linq #linq-to-sql #linq-to-entities

Вопрос:

В настоящее время я не могу понять, почему мой метод обновления в моем репозитории для объекта style выдает исключение NullReferenceException на моем уровне модели домена Linq to SQL. Проблема, по-видимому, заключается в том, что EntitySet<Product> Product свойство объекта Style, которое я обновляю, равно null после необходимого использования метода Detach. Фактическое исключение генерируется при прикреплении до отправки изменений.

Мое обновление предназначено только для таргетинга на таблицу стилей, ссылка на продукт на самом деле не должна входить в нее, но взаимосвязь отображается в коде, сгенерированном SQLMetal, который является важной частью дизайна моего приложения.

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

Я попытаюсь наилучшим образом объяснить настройку:

Связь в базе данных SQL Server выглядит следующим образом: таблица Product ссылается на таблицу Style:

введите описание изображения здесь

Класс сопоставления сущностей создается с помощью SQLMetal, насколько я знаю, в способе его создания нет ничего необычного.

Метод обновления является:

 public Style Update(Style style)
    {
        using (var dc = new ProjectERPDataContext(Config.ConnectionStringERPDB))
        {
            style.Detach();

            dc.Style.Attach(style);
            dc.Refresh(RefreshMode.KeepCurrentValues, style);
            dc.SubmitChanges();

            return style;
        }
    }
  

Метод отсоединения сохраняется в частичном классе:

Метод отсоединения существует, потому что без него генерируется исключение: «Была предпринята попытка прикрепить или добавить объект, который не является новым, возможно, был загружен из другого DataContext. Это не поддерживается.»

 public partial class Style
{
    public void DetachEntityRefs()
    {
        this._Product = default(EntitySet<Product>);
    }
}
  

Класс сущности начинается как:

 [Table(Name="dbo.Style")]
public partial class Style : INotifyPropertyChanging, INotifyPropertyChanged
{

    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);

    private int _ID;

    private string _Name;

    private EntitySet<Product> _Product;
  

Таким образом, вы можете точно видеть, где генерируется исключение:

введите описание изображения здесь

Полная трассировка стека:

     System.NullReferenceException : Object reference not set to an instance of an object.
at System.Data.Linq.Mapping.EntitySetDefSourceAccessor`2.GetValue(T instance)
at System.Data.Linq.Mapping.MetaAccessor`2.GetBoxedValue(Object instance)
at System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.HasDeferredLoader(MetaDataMember deferredMember)
at System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.get_HasDeferredLoaders()
at System.Data.Linq.ChangeTracker.StandardChangeTracker.Track(MetaType mt, Object obj, Dictionary`2 visited, Boolean recurse, Int32 level)
at System.Data.Linq.ChangeTracker.StandardChangeTracker.Track(Object obj, Boolean recurse)
at System.Data.Linq.Table`1.Attach(TEntity entity, Boolean asModified)
at ProjectERP.DomainModel.Repositories.SQLStyleRepo.Update(Style style) in SQLStyleRepo.cs: line 45
at ProjectERP.UnitTests.DomainModel.SQLStyleRepoTests.Test_can_update_style() in SQLStyleRepoTests.cs: line 67 
  

Комментарии:

1. Можете ли вы показать нам трассировку стека этого исключения?

2. Только что добавил его. Не дает слишком многого!

Ответ №1:

Бьюсь об заклад, причина в этой бомбе замедленного действия, которую вы здесь заложили:

 this._Product = default(EntitySet<Product>);
  

default(EntitySet<Product>) является null . Попробуйте установить для него что-нибудь:

 this._Product = new EntitySet<Product>();
  

Комментарии:

1. Спасибо, мои тесты проходят. Я все еще строю свое понимание Linq-SQL. Не могли бы вы объяснить, почему мне пришлось установить для этого значение new, а не default. Я успешно использовал значение по умолчанию с default(EntityRef<T>) для других классов. Разница, я полагаю, здесь в том, что это EntitySet, а не EntityRef? Я не хочу никаких «бомб замедленного действия» в моем приложении! Мое понимание на сегодняшний день исходит из: geekswithblogs.net/michelotti/archive/2007/12/25/117984.aspx

2. @BombDefused: (вау, только что заметил твое экранное имя. Бомба замедленного действия не была игрой слов), потому что L2S ожидает найти там set, даже если он пустой. null это не пустой набор, это отсутствие набора. И именно поэтому вы получали исключение NullReferenceException. В EntityRefs ожидается, что L2S либо будет содержать ссылку, либо нет. Для этого используется null . Не спрашивайте почему, я это не разрабатывал.