前回の記事では、更新操作に LINQ to SQL を使用するときに発生する可能性のあるいくつかの問題を紹介しました。実際、これは私だけが直面した問題ではなく、インターネットで答えを探したところ、多くの人がこのトピックに関して同様の記事を公開していることがわかりました。しかし、私が満足していないのは、問題を提起したにもかかわらず、詳細な分析を行わず、解決策 (RowVersion 列の追加、関連付けの削除など) を提示しただけで、なぜそうしなければならないのか説明していないことです。 。前回の記事を書いた当初の意図でもあり、LINQ to SQL のソース コードを分析することで、問題の解決策を段階的に見つけていきたいと考えています。この記事では、これらの方法を 1 つずつ説明します。
オプション 1: 再割り当て TerryLee、Anytao、Ding Xue らによるオープンソース フレームワーク Ezsocio では、いくつかの場所で再割り当てが採用されています。 Update メソッド内で、主キーに基づいてデータベース内のエンティティを取得し、パラメータ内のエンティティを使用してそのプロパティに値を 1 つずつ割り当てます。
public void UpdateProfile(プロファイル p)
{
(RepositoryContext db = new RepositoryContext()) を使用します。
{
var profile = db.GetTable<プロファイル>().First<プロファイル>(u => u.ID == p.ID);
プロフィール.誕生日 = p.誕生日;
profile.Gender = p.Gender;
プロフィール.出身地 = p.出身地;
プロファイル.MSN = p.MSN;
プロフィール.ニックネーム = p.ニックネーム;
profile.PhoneNumber = p.PhoneNumber;
プロファイル.QQ = p.QQ;
profile.State = p.State;
プロファイル.TrueName = p.TrueName;
profile.StateRefreshTime = p.StateRefreshTime;
プロフィール.アバター = p.アバター;
プロフィール.ウェブサイト = p.ウェブサイト;
db.SubmitChanges();
}
}
Yang Guo 兄弟は、属性値の自動コピーを実現するためのこのソリューションのリフレクション メソッドも提供しました。
しかし、これは更新操作に LINQ to SQL が提供する API を使用せず、迂遠な戦略を採用している、現実を回避したスキームであると個人的には思います。これは実際には妥協です。Attach メソッドが「使いにくい」ため、使用しないのでしょうか。ふふ。
オプション 2: オブジェクト追跡を無効にする これに関して、lea は、DataContext の ObjectTrackingEnabled プロパティを false に設定することで正しい更新を実現できると提案しました。
public Product GetProduct(int id)
{
NorthwindDataContext db = 新しい NorthwindDataContext();
db.ObjectTrackingEnabled = false;
return db.Products.SingleOrDefault(p => p.ProductID == id);
}
他のコードの変更はありません。
オブジェクト追跡を無効にすると正常に更新できるのはなぜですか?ソースコードから答えを見つけてみましょう。
public bool ObjectTrackingEnabled
{
得る
{
this.CheckDispose();
this.objectTrackingEnabled を返します。
}
セット
{
this.CheckDispose();
if (this.Services.HasCachedObjects)
{
System.Data.Linq.Error.OptionsCannotBeModifiedAfterQuery(); をスローします。
}
this.objectTrackingEnabled = 値;
if (!this.objectTrackingEnabled)
{
this.deferredLoadingEnabled = false;
}
this.services.ResetServices();
}
}
ObjectTrackingEnabled が false に設定されると、DeferredLoadingEnabled も同時に false に設定されることがわかります。この方法では、クエリの実行時に、遅延クエリを必要とするデータがエンティティにロードされないため、アタッチ中に例外がスローされません (前の記事の分析を参照)。
MSDN では、次の有益な情報も得られます。 ObjectTrackingEnable プロパティを false に設定すると、追跡する項目の数が減るため、取得パフォーマンスが向上します。これは非常に魅力的な機能です。
ただし、オブジェクト トラッキングを無効にする場合は、次の 2 つの点に特別な注意を払う必要があります。 (1) クエリを実行する前に無効にする必要があります。 (2) 無効にすると、Attach メソッドと SubmitChanges メソッドを呼び出すことができなくなります。それ以外の場合は、例外がスローされます。
オプション 3: 関連付けを削除する 前の記事で、GetProduct メソッドで Product に関連付けられた カテゴリを手動で null に設定するという面倒な方法を紹介しました。コードのこの部分を抽出して Detach メソッドに入れることができます。この Detach はエンティティ メソッドであるため、部分クラスを使用できます。
パブリック部分クラス Product
{
public void Detach()
{
this._Category =default(EntityRef<Category>);
}
}
パブリック部分クラス カテゴリ
{
public void Detach()
{
foreach (this.Products 内の var product)
{
製品.Detach();
}
}
}しかし、エンティティごとに Detach を定義するこの方法は面倒すぎます。エンティティの数が増えると、関係がますます複雑になり、欠落した属性が現れやすくなります。 Zhang Yi は、リフレクションを使用してこのロジックを抽象化する非常にエレガントな方法を提案しました。
private void Detach(TEntity エンティティ)
{
foreach (entity.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance) のフィールド情報 fi)
{
if (fi.FieldType.ToString().Contains("EntityRef"))
{
var 値 = fi.GetValue(entity);
if (値 != null)
{
fi.SetValue(エンティティ、null);
}
}
if (fi.FieldType.ToString().Contains("EntitySet"))
{
var 値 = fi.GetValue(entity);
if (値 != null)
{
MethodInfo mi = value.GetType().GetMethod("Clear");
if (mi != null)
{
mi.Invoke(値, null);
}
fi.SetValue(エンティティ, 値);
}
}
}
}
また、PropertyChanging および PropertyChanged イベントは、Detach 中に null に設定されるべきだと考える人もいますが、全体的な考え方は同じです。
オプション 4: 委任を使用する。これは、前回の記事のコメントで ZC29 によって示された方法であり、学ぶ価値があると個人的に思います。
public void UpdateProductWithDelegate(Expression<Func<Product, bool>> 述語, Action<Product> アクション)
{
NorthwindDataContext db = 新しい NorthwindDataContext();
var product = db.Products.SingleOrDefault(述語);
アクション(製品);
db.SubmitChanges();
}
//クライアントコード
ProductRepository リポジトリ = new ProductRepository();
repository.UpdateProductWithDelegate(p => p.ProductID == 1, p =>
{
p.ProductName = "変更されました";
});
Lambda 式を使用して GetProduct ロジックを UpdateProduct に埋め込み、デリゲートを使用して更新ロジックの実行を延期します。これにより、検索と更新が DataContext に巧みに組み込まれ、Attach が回避されます。ただし、このメソッドの API は少し複雑すぎるため、高度なレベルのクライアント プログラマが必要になります。さらに、Get ロジックは Update で再度実行する必要がありますが、パフォーマンスの低下は最小限ですが、常に DRY が不十分であると感じられるようです。
オプション 5: UPDATE ステートメントを使用する Ezsocio のソース コードで、RepositoryBase.UpdateEntity メソッドを見つけました。 SQL ステートメントの結合はメソッド内で行われ、変更された列のみが更新されます。 ITable はここでは使用されなくなり、完全なフレームワークのサポートが必要となるため、これ以上のコメントは行いません。詳細については、Ezsocio のソース コードを参照してください。
まとめ この記事では、最近インターネットで見つけたいくつかの解決策をリストします。どれが良いか悪いかは、さまざまな人々の意見によって異なります。次の記事では、これらの方法のパフォーマンスを比較して、最適な解決策を見つけます。