ここでは全く別の答えがあります。親オブジェクトだけではなく、オブジェクト全体を再帰的に切り離します。あなたのコンテキストオブジェクトに拡張メソッドとして記述され、以下:
/// <summary>
/// Recursively detaches item and sub-items from EF. Assumes that all sub-objects are properties (not fields).
/// </summary>
/// <param name="item">The item to detach</param>
/// <param name="recursionDepth">Number of levels to go before stopping. object.Property is 1, object.Property.SubProperty is 2, and so on.</param>
public static void DetachAll(this DbContext db, object item, int recursionDepth = 3)
{
//Exit if no remaining recursion depth
if (recursionDepth <= 0) return;
//detach this object
db.Entry(item).State = EntityState.Detached;
//get reflection data for all the properties we mean to detach
Type t = item.GetType();
var properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.GetSetMethod()?.IsPublic == true) //get only properties we can set
.Where(p => p.PropertyType.IsClass) //only classes can be EF objects
.Where(p => p.PropertyType != typeof(string)) //oh, strings. What a pain.
.Where(p => p.GetValue(item) != null); //only get set properties
//if we're recursing, we'll check here to make sure we should keep going
if (properties.Count() == 0) return;
foreach (var p in properties)
{
//handle generics
if (p.PropertyType.IsGenericType)
{
//assume its Enumerable. More logic can be built here if that's not true.
IEnumerable collection = (IEnumerable)p.GetValue(item);
foreach (var obj in collection)
{
db.Entry(obj).State = EntityState.Detached;
DetachAll(db, obj, recursionDepth - 1);
}
}
else
{
var obj = p.GetValue(item);
db.Entry(obj).State = EntityState.Detached;
DetachAll(db, obj, recursionDepth - 1);
}
}
}
設定型のプロパティになります外を見るために最大のもの - 直接オブジェクトに関連していないデータを表すオブジェクトを。これらは競合を引き起こす可能性があるため、オブジェクトにオブジェクトが含まれていないことを確認することをお勧めします。
注:
このアプローチは、コピーしたいすべてのサブオブジェクトは遅延ロードを避け、事前に移入されている必要があります。これを確実にするために、私は私のEFの問い合わせについては、以下の拡張子を使用:
//Given a custom context object such that CustomContext inherits from DbContext AND contains an arbitrary number of DbSet collections
//which represent the data in the database (i.e. DbSet<MyObject>), this method fetches a queryable collection of object type T which
//will preload sub-objects specified by the array of expressions (includeExpressions) in the form o => o.SubObject.
public static IQueryable<T> GetQueryable<T>(this CustomContext context, params Expression<Func<T, object>>[] includeExpressions) where T : class
{
//look through the context for a dbset of the specified type
var property = typeof(CustomContext).GetProperties().Where(p => p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericArguments()[0] == typeof(T)).FirstOrDefault();
//if the property wasn't found, we don't have the queryable object. Throw exception
if (property == null) throw new Exception("No queryable context object found for Type " + typeof(T).Name);
//create a result of that type, then assign it to the dataset
IQueryable<T> source = (IQueryable<T>)property.GetValue(context);
//return
return includeExpressions.Aggregate(source, (current, expression) => current.Include(expression));
}
この方法は、あなたがDbContext
から継承して、オブジェクトのDbSet<>
コレクションを含むカスタムコンテキストオブジェクトを持っていることを前提としています。適切なものが見つかるでしょうDbSet<T>
そしてあなたのオブジェクトの指定されたサブクラスをあらかじめロードするクエリ可能なコレクションを返します。これらは式の配列として指定されます。たとえば:
//example for object type 'Order'
var includes = new Expression<Func<Order, object>>[] {
o => o.SalesItems.Select(p => p.Discounts), //load the 'SalesItems' collection AND the `Discounts` collection for each SalesItem
o => o.Config.PriceList, //load the Config object AND the PriceList sub-object
o => o.Tenders, //load the 'Tenders' collection
o => o.Customer //load the 'Customer' object
};
は私の照会可能なコレクションを取得するには、私は今のようなそれを呼び出す:
var queryableOrders = context.GetQueryable(includes);
を繰り返しますが、ここでの目的は、熱心にのみサブオブジェクトをロードします照会可能オブジェクトを作成することですあなたが実際に望むサブオブジェクト(とサブサブオブジェクト)を作成します。
、特定のアイテムを取得し、他の照会可能源のようにこれを使用するには:あなたはまた、表現をインラインで提供することができます
var order = context.GetQueryable(includes).FirstOrDefault(o => o.OrderNumber == myOrderNumber);
注意。ただし、汎用を指定する必要があります。
//you can provide includes inline if you just have a couple
var order = context.GetQueryable<Order>(o => o.Tenders, o => o.SalesItems).FirstOrDefault(o => o.OrderNumber == myOrderNumber);
自己参照関係で作業しているか、2つの異なるエンティティが関係していますか? – octavioccl
私はあなたがここでより多くのコンテキスト 'Entities db = new Entities();'と尋ねたいとは思っていませんでした。ここでは外部キーがどのように定義されています。 [テーブル]([Id]) ' –