以下の関数で指定されているように従業員レコードを更新できますか、または最初に従業員コレクションのクエリを実行してからデータを更新する必要がありますか?
public int updateEmployee(App3_EMPLOYEE employee)
{
DBContextDataContext db = new DBContextDataContext();
db.App3_EMPLOYEEs.Attach(employee);
db.SubmitChanges();
return employee.PKEY;
}
または、次のことを行う必要がありますか?
public int updateEmployee(App3_EMPLOYEE employee)
{
DBContextDataContext db = new DBContextDataContext();
App3_EMPLOYEE emp = db.App3_EMPLOYEEs.Single(e => e.PKEY == employee.PKEY);
db.App3_EMPLOYEEs.Attach(employee,emp);
db.SubmitChanges();
return employee.PKEY;
}
しかし、私は2番目のオプションを使用したくありません。データを更新する効率的な方法はありますか?
両方の方法を使用すると、このエラーが発生します。
おそらく別のDataContextからロードされた、新しいエンティティではないエンティティをアタッチまたは追加しようとしました。これはサポートされていません。
私はこの問題に対する次の回避策を見つけました:
1)エンティティをフェッチして更新します(私にとっては問題ないため、この方法を使用します)
public int updateEmployee(App3_EMPLOYEE employee)
{
AppEmployeeDataContext db = new AppEmployeeDataContext();
App3_EMPLOYEE emp = db.App3_EMPLOYEEs.Single(e => e.PKEY == employee.PKEY);
emp.FIRSTNAME = employee.FIRSTNAME;//copy property one by one
db.SubmitChanges();
return employee.PKEY;
}
2)次のようにObjectTrackingEnabledを無効にします
// but in this case lazy loading is not supported
public AppEmployeeDataContext() :
base(global::LinqLibrary.Properties.Settings.Default.AppConnect3DBConnectionString, mappingSource)
{
this.ObjectTrackingEnabled = false;
OnCreated();
}
3)関連するすべてのオブジェクトを切り離します
partial class App3_EMPLOYEE
{
public void Detach()
{
this._APP3_EMPLOYEE_EXTs = default(EntityRef<APP3_EMPLOYEE_EXT>);
}
}
public int updateEmployee(App3_EMPLOYEE employee)
{
AppEmployeeDataContext db = new AppEmployeeDataContext();
employee.Detach();
db.App3_EMPLOYEEs.Attach(employee,true);
db.SubmitChanges();
return employee.PKEY;
}
4)列にタイムスタンプを使用します
http://www.west-wind.com/weblog/posts/135659.aspx
5)データを更新するためのストアドプロシージャを作成し、dbコンテキストで呼び出します
RowVersion列がない場合、変更されたエンティティをDataContextにアタッチすることはできません。代わりに、データ変更のコピーを維持している限り、元のエンティティをアプリケーションに保存できます。次に、変更を保存する必要がある場合は、元のエンティティをDataContextにアタッチし、変更されたエンティティの値と一致するようにその値を変更して、変更を送信できます。
次に例を示します。
public int updateEmployee(App3_EMPLOYEE employee, App3_EMPLOYEE originalEmployee)
{
DBContextDataContext db = new DBContextDataContext();
db.App3_EMPLOYEEs.Attach(originalEmployee);
// TODO: Copy values from employee to original employee
db.SubmitChanges();
return employee.PKEY;
}
更新:
データベースには、ID、名前、メモの列を持つテーブルがあります
// fetch an employee which will not be changed in the application
Employee original;
using(var db = new TestDbDataContext())
{
original = db.Employees.First(e => e.ID == 2);
}
// create an instance to work with
var modified = new Employee {ID = original.ID, Name = original.Name, Notes = original.Notes};
// change some info
modified.Notes = string.Format("new notes as of {0}", DateTime.Now.ToShortTimeString());
// update
using(var db = new TestDbDataContext())
{
db.Employees.Attach(original);
original.Notes = modified.Notes;
db.SubmitChanges();
}
このオーバーロードを使用して、アタッチされていない変更されたエンティティをアタッチできます。
db.App3_EMPLOYEEs.Attach(employee, true);//Attach as modfieied
これを機能させるには、テーブルに「timestamp」タイプの「Version」列が必要であることに注意してください。
このトピックに関する議論があります ここMSDNで IsVersionフィールドとAttachメソッドを使用することをお勧めします
これは、エンティティを更新するために使用するリポジトリクラスの関数です。
protected void Attach(TEntity entity)
{
try
{
_dataContext.GetTable<TEntity>().Attach(entity);
_dataContext.Refresh(RefreshMode.KeepCurrentValues, entity);
}
catch (DuplicateKeyException ex) //Data context knows about this entity so just update values
{
_dataContext.Refresh(RefreshMode.KeepCurrentValues, entity);
}
}
TEntityがDBクラスであり、セットアップによっては、実行したい場合があります。
_dataContext.Attach(entity);
このextendメソッドを使用して、列属性であるすべてのプロパティを更新します。
public static void SaveToOriginal<T>(this T original, T actual)
{
foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(info => info.GetCustomAttribute<System.Data.Linq.Mapping.ColumnAttribute>() != null))
{
prop.SetValue(original, prop.GetValue(actual));
}
}
つまり、最初にデータベースからオリジナルを復元し、メソッドを使用してすべての列属性を新しい要素からオリジナルにマップし、最後に送信を実行します。これがお役に立てば幸いです。