【问题标题】:Get underlying entity object from entity framework proxy从实体框架代理获取底层实体对象
【发布时间】:2014-11-04 09:28:10
【问题描述】:

我从DbEntityEntry.Entity 获得了一个实体。这将返回实体的实体框架代理。

如何将底层对象作为其原始类型而不是代理访问?

或者,我需要动态尝试将代理转换为实体类型。这是一个开始。

var theEntityType = entityEntry.Entity;

if (
    theEntityType.BaseType != null 
    && entityType.Namespace == "System.Data.Entity.DynamicProxies"
)
   theEntityType = entityType.BaseType;

// Now I need to cast to the correct type
// THIS WON'T WORK BECAUSE `theEntityType` is dynamic.
var entityObject = (theEntityType)entityEntry.Entity; 
// My entites also don't implement IConvertible

【问题讨论】:

  • 你为什么要这样做,你想达到什么目的? EF 代理继承自实际实体,那么转换回原始类型会给您带来什么?
  • @Ben 例如重载方法来处理一些实体类型。对于代理,它不适用于checkRequired(Customer c)checkRequired(Order o)
  • 我更正了我的评论。如果我们有checkRequired(Object o)(我的情况),由于代理,它将被调用而不是其他重载。但是如果Object o 的重载不存在,它将调用相关方法(接受客户、订单等的方法)。不完美但至少有用。
  • 这里有另一个答案stackoverflow.com/a/8973887/80434

标签: c# entity-framework


【解决方案1】:

为了在 EF Core 中获取 JSON 友好对象,我使用了以下方法:

T UnProxy<T>(T efObject) where T : new()
{
    var type = efObject.GetType();

    if (type.Namespace == "Castle.Proxies")
    {
        var baseType = type.BaseType;
        var returnObject = new T();
        foreach (var property in baseType.GetProperties())
        {
            var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
            if (propertyType.Namespace == "System")
            {
                var value = property.GetValue(efObject);
                property.SetValue(returnObject, value);
            }
        }
        return returnObject;
    }

    return efObject;
}

【讨论】:

  • 就我而言,ChangeTracker.Entries 有时是代理,有时不是。通过将 Entry.Entity 与硬编码的命名空间“Castle.Proxies”进行比较,我的代码开始工作。但是可以安全地假设这个命名空间很可能永远/很长时间都不会改变吗?
【解决方案2】:

在使用 EF 6 时,我使用以下代码从代理类型获取底层 POCO 实体类型,

var entityType = ObjectContext.GetObjectType(dbEntitymodifiedEntry.Entity.GetType());

ObjectContext.GetObjectType : 从代理对象返回 POCO

参考:https://docs.microsoft.com/en-us/ef/ef6/fundamentals/proxies

【讨论】:

  • 给像我这样眼睛不好的人的注释.. 这是一个大写的 O ObjectContext 意味着它是一个静态方法 =) stackoverflow.com/a/16005340/3507333
  • 这绝对是最好的解决方案。如果它不是实体类型,则此函数仅返回传递的类型。很干净。我从 Anthony Nichols 的回答开始,但检查命名空间不是未来的证明,以后可能会成为一个未被发现的错误。
【解决方案3】:

使用 AutoMapper 4.2.1 它具有可以从对象中删除代理的 DynamicMap。

     var parents = parentsRepo.GetAll().ToList();
     Mapper.CreateMap<Parent,ParentDto>();
     var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);

【讨论】:

  • 这没有帮助,因为在项目中间更改 ORM 通常是不可能的。
  • @MichaelMilom 这是如何“改变 ORM”的? AutoMapper 是一个对象-对象映射器。
  • 我很抱歉。快速扫描,快速评论。
【解决方案4】:

如果您最终需要从无法访问 EF 或 DBContext 的项目中执行此操作,并且您不知道您引用的类型是否是代理,您可以执行以下操作:

    public Type GetType
    {
        get
        {
            var thisType = _baseObject.GetType();

            if (thisType.Namespace == "System.Data.Entity.DynamicProxies")
                return thisType.BaseType;

            return thisType;
        }
    }

【讨论】:

  • Ajay 的解决方案仍然是唯一好的:使用 ObjectContext.GetObjectType()。这是一个静态方法,因此您实际上不需要对 DbContext 的引用。
  • @codetuner 如果您的项目中没有对实体框架的引用,那么您将根本无法访问 DbContext,这是我的情况。
  • @codetuner 另外,我的 API 需要传递 object 类型的变量。所以我不能在事先不知道类型的情况下使用.Entity() 方法,这违背了我需要解决方案的目的。
  • 与其他解决方案相比,这可能是最干净的解决方案。不需要对 DbEntity 的引用,我可以在静态方法中实现它
  • 另一种情况:if (thisType.Namespace == "Castle.Proxies")
【解决方案5】:

建议的答案存在许多问题 - 例如,它不保留在部分类中为生成的 POCO 类定义的属性,并且它从 DB 重新加载实体(这也会影响性能)。

您可以在请求更改之前尝试关闭代理,但如果之前已经加载过实体,这可能无济于事 - 它们已经是代理类型(可能取决于 EF 版本,但它只成功了一次,并且根据我的经验,另一次没有工作)。 您还需要在返回代理之前实现它 - 这并不明显,但它只是必须实现的延迟查询:

context.Configuration.ProxyCreationEnabled = false;
var changes = context.ChangeTracker.Entries().ToArray();

【讨论】:

  • 我现在碰巧看到了这个,但我不明白这是如何回答这个问题的。 context.ChangeTracker.Entries() 不返回基类型,而是返回代理。这些关于延迟执行的东西是关于什么的?至于批评,我的方法不会重新加载任何东西。您唯一的一点是不保留未映射的属性。
  • 更改了我的答案以明确延迟执行。好吧,我从 DB 重新加载数据可能是错误的。显然我将它与 OriginalValues(我碰巧在我的应用程序中需要)混合在一起。
【解决方案6】:

首先我应该说没有底层对象。代理不包装实体对象(装饰器模式),它派生它(继承)。所以我们不能解开实体,我们只能转换一个代理到一个基础对象。转换(与强制转换相反)总是创建一个新对象。

对于这种转换,我们可以利用这样一个事实,即在大多数情况下,通过 EF 返回代理的方式,代理的编译时类型是基本类型。也就是说,如果将代理作为泛型方法的参数输入,则泛型参数将被推断为基本类型。这个特性允许我们创建一个方法来做你想做的事:

T UnProxy<T>(DbContext context, T proxyObject) where T : class
{
    var proxyCreationEnabled = context.Configuration.ProxyCreationEnabled;
    try
    {
        context.Configuration.ProxyCreationEnabled = false;
        T poco = context.Entry(proxyObject).CurrentValues.ToObject() as T;
        return poco;
    }
    finally
    {
        context.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

说明

代理对象进入方法。它的类型被推断为基本 POCO 类型。现在我们可以暂时关闭上下文中的ProxyCreationEnabled,并将代理对象复制到其基本 POCO 类型的对象中。此复制操作使用了一些 EF 功能。

【讨论】:

  • 如果在其他线程中使用了上下文,这可能会让你绊倒……只是一个警告。
  • 另外,你必须处理一个被跟踪的实体,所以不能是 .AsNoTracking() 查询的结果。
  • @IDisposable 在多个线程中使用上下文无论如何都是不健康的。是的,它必须是被跟踪的实体。
  • 如果存在同样是代理类型的链接实体的情况呢?即代理实体公司,具有员工类型的代理实体的关系列表。您的函数只是将任何关系变为 null。
  • @DFTR 这就是我们所要求的。要填充导航属性,应该构建一个递归循环。随意:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-28
相关资源
最近更新 更多