知らせ:
1. この記事で説明されている「エンティティ」はすべて LINQ TO SQL (つまり .dbml) によって生成されます。
2. LINQ TO SQL がテーブルの関連付け、EntitySet および EntityRef を実装する方法を理解する必要があります。
おそらく、タイトルを見た後、質問が比較的抽象的だと思われるでしょう。そこで、問題を詳しく説明するために例を挙げてみましょう。
LINQ TO SQL に基づく N 層アーキテクチャでは、エンティティを更新する必要がある場合、プロセスは次のようになります。
プロセス
BLL.GetModel(p=>p.id==1) --> 対応する属性 (フィールド) 値を変更 --> BLL.Update(Entity エンティティ) --> DAL.Update(Entity エンティティ) --> 更新成功
この時点で、このエンティティをビジネス層 (BLL) からデータ アクセス層 (DAL) に渡す必要があります。GetModel メソッドがエンティティを返すと、DataContext はすぐに解放され、DAL が返されたときに再インスタンス化されます。 Update (エンティティ エンティティ) メソッドが実行され、更新操作を実行するために DataContext が作成されます。LINQ TO SQL で異なる DataContext を操作する場合、渡されたエンティティが別の DataContext に渡されることがわかります。最初に実行される、つまり context.Entity.Attach(entity,true); 最後に context.SubmitChanges();
コードを見てみましょう:
コード
クラスBLL
{
private readonly DAL dal = new DAL();
public LinqToSqlProvider.User GetModel(Expression<Func<LinqToSqlProvider.User, bool>> 式)
{
dal.GetModel(式);
}
public void Update(LinqToSqlProvider.User エンティティ)
{
dal.Update(エンティティ);
}
}
クラスDAL
{
public LinqToSqlProvider.User GetModel(Expression<Func<LinqToSqlProvider.User, bool>> 式)
{
LinqToSqlProvider.User エントリ = 新しい CriTextBroadcast.LinqToSqlProvider.User();
(CriTextBroadcastDBDataContext コンテキスト = DataContext) を使用します。
{
エントリ = context.User.SingleOrDefault(式);
}
エントリーを返す。
}
public void Update(LinqToSqlProvider.User エンティティ)
{
(CriTextBroadcastDBDataContext コンテキスト = DataContext) を使用します。
{
context.User.Attach(entry, true);
context.SubmitChanges();
}
}
}
実際、上記のコードを使用して操作すると、次の例外が発生します。
エンティティの添付または追加が試行されました。このエンティティは新しいエンティティではなく、他の DataContext からロードされた可能性があります...
多くの情報をチェックしても無駄でしたが、最終的に海外の投稿で同様の質問を見つけました。このエンティティに対応するテーブルには、ユーザー -> メッセージ、ユーザー -> 画像など、いくつかの関連テーブルがあることがわかります。
EntitySet<Message> などの属性はエンティティクラス生成時にメッセージが自動生成されますが、エンティティを取得する際にこれらの関連付けられた内容を一緒に読み出さないため、これらの属性では DataContext のアタッチ(Attach)時に ObjectDisposedException が発生します。このエンティティのクエリに使用されるものがリリースされました。
解決:
コード
/// <概要>
/// 補助的な LinqToSql エンティティは元の DataContext から分離されます
/// </概要>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
public static void Detatch<TEntity>(TEntity エンティティ)
{
タイプ t = エンティティ.GetType();
System.Reflection.PropertyInfo[] プロパティ = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (プロパティ内の var プロパティ)
{
文字列名 = プロパティ.名前;
if (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(EntitySet<>))
{
property.SetValue(entity, null, null);
}
}
System.Reflection.FieldInfo[] フィールド = t.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (フィールド内の var フィールド)
{
文字列名 = フィールド.名前;
if (field.FieldType.IsGenericType &&
field.FieldType.GetGenericTypeDefinition() == typeof(EntityRef<>))
{
フィールド.SetValue(エンティティ、null);
}
}
System.Reflection.EventInfoeventPropertyChanged = t.GetEvent("PropertyChanged");
System.Reflection.EventInfoeventPropertyChanging = t.GetEvent("PropertyChanging");
if (eventPropertyChanged != null)
{
eventPropertyChanged.RemoveEventHandler(entity, null);
}
if (eventPropertyChanging != null)
{
eventPropertyChanging.RemoveEventHandler(entity, null);
}
}
エンティティを取得した後、Detach(entity) メソッドを使用して元の DataContext からエンティティを分離し、それを新しい DataContext にアタッチする必要があります。