下面是第二个完全不同的答案:递归地分离整个对象而不是仅仅是父对象。写为扩展方法对上下文对象以下:
/// <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);
您是使用自引用关系还是涉及两个不同的实体? – octavioccl
我没有得到你想要的,但在这里更多的上下文'实体db =新实体();'这里是如何定义外键。 ALTER TABLE [dbo]。[TABLE] WITH NOCHECK ADD CONSTRAINT [FK_Table_Table] FOREIGN KEY([ParentId]) REFERENCES [dbo]。[Table]([Id])' –