使用 EF4 读取引用实体的 PropertyInfo 的值

发布于 2024-12-29 08:19:21 字数 3911 浏览 2 评论 0原文

我想动态读取在循环父实体的 PropertyInfo 时遇到的 EntityObjects 的 PropertyInfo 值(连接到当前 Image 实例的 ImageType 实例的列值,fi)。

主要实体的类型仅在运行时已知,因此我正在寻找一种读取任何引用实体对象的 PropertyInfo 值的通用方法。

我可以循环访问子实体的 PropertyInfos,但是当我尝试获取值时,我得到一个 TargetException:对象与目标类型不匹配。

// loop through the main entity's properties
foreach (PropertyInfo pi in entityType.GetProperties())
{
    // if the main entity's property is an entity
    if (pi.PropertyType.BaseType == typeof(System.Data.Objects.DataClasses.EntityObject))
    {
        // loop through the sub entity's properties
        foreach(PropertyInfo mychildren in pi.PropertyType.GetProperties())     
        {   
            // the loop works fine but when i try to get a value I get a
            // TargetException: Object does not match target type.
            object test = mychildren.GetValue(pi, null);
        }
    }
}

我该怎么做?

编辑

Entity Framework 4.0 似乎不允许您动态检索实体的相关实体的实例。但在 EF 4.1 及更高版本中,您可以通过使用类名作为字符串标识符来实现。所以我升级到 EF 4.2 并让它正常工作。

我想要这段代码的原因是在我的 DTO 翻译例程中使用它。我的 DTO 可以具有与相关实体的名称属性相对应的字符串属性,这样我就可以访问这些属性,而无需对相关实体的类型进行硬编码。

在 EF 4.1 及更高版本中,ObjectContext 由名为 DbContext 的类包装,该类提供导航属性,用于使用字符串获取相关实体的实例。要动态检索单个相关实体,您可以使用:

dynamic refObject = Activator.CreateInstance(refObjectType);

refObject = context.Entry(currentObject).Reference(refObjectType.Name).CurrentValue;

对于从 4.0 升级的用户:使用 DbContext 的推荐方法不是使用 EntityObject,而是使用 POCO。这些可以手动制作,也可以通过 edmx 上下文菜单生成。

我当前的实现如下:

// Loop through the propertyinfos of the dto's type
foreach (PropertyInfo pf in dtoType.GetProperties().Where(p => p.CanWrite))
{
    // Use the name of the dto property to get the corresponding property from the POCO's type. If it doesn't exist, pi will be null
    PropertyInfo pi = pocoType.GetProperty(pf.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

    if (pi != null)
    {
        // Check if the current propertyinfo of the POCO has a subproperty named Id
        // If this is the case we treat the propertyinfo as a referenced POCO
        if (pi.PropertyType.GetProperty("Id") != null)
        {
            // skip referenced POCOs if their data is not needed
            if (!includeRelated) continue;

            // Get the type of the referenced POCO
            Type refObjectType = pi.PropertyType;
            // Create an instance of the referenced POCO
            dynamic refObject = Activator.CreateInstance(refObjectType);

            // Create a type of GenericRepository<objectType>
            Type refObjectRepositoryType = typeof(GenericRepository<>).MakeGenericType(refObjectType);
            // Create an instance of GenericRepository<objectType>
            dynamic refObjectRepository = Activator.CreateInstance(refObjectRepositoryType);

            // Fill the dynamic POCO instance with the values of the referenced POCO instance
            refObject = refObjectRepository._context.Entry(poco).Reference(refObjectType.Name).CurrentValue;

            try
            {
                // Set the dto property with the name value of the referenced POCO instance
                // (i.e. dtoImage.ImageType = pocImage.ImageType.Name)
                pf.SetValue(dto, refObject.Name, null);
            }
            catch (RuntimeBinderException)
            {
                // this happens when the related entity is null, ie. in one to zero-or-one relationships
                continue;
            }

            continue;
        }

        // If the propertyinfo's propertytype does not have an Id property, just set the value of  
        // the dto property to that of the POCO's propertyinfo directly
        pf.SetValue(dto, pi.GetValue(poco, null), null);

    }
}

目前,此代码仅适用于同时具有 Id 和 Name 属性的引用实体。此外,这种方法可能会降低性能,因此我实现了标志 includeRelated 来切换是否查询相关对象。

I would like to dynamically read the values of the PropertyInfos of EntityObjects that I come across when looping through the PropertyInfos of a parent entity (the column values of the instance of ImageType that is connected to the current instance of Image, f.i.).

The main entity's type is only known at runtime, so I'm looking for a generic way of reading the PropertyInfo values of any referenced entity object.

I can loop through the PropertyInfos of the sub entity, but when I try to get a value I get a TargetException: Object does not match target type.

// loop through the main entity's properties
foreach (PropertyInfo pi in entityType.GetProperties())
{
    // if the main entity's property is an entity
    if (pi.PropertyType.BaseType == typeof(System.Data.Objects.DataClasses.EntityObject))
    {
        // loop through the sub entity's properties
        foreach(PropertyInfo mychildren in pi.PropertyType.GetProperties())     
        {   
            // the loop works fine but when i try to get a value I get a
            // TargetException: Object does not match target type.
            object test = mychildren.GetValue(pi, null);
        }
    }
}

How can I do this?

Edit:

Entity Framework 4.0 doesn't seem to allow you to dynamically retrieve the instances of an entity's related entities. But with EF 4.1 and up you can, by using their class name as a string identifier. So I upgraded to EF 4.2 and got it working.

The reason I wanted this code is to use it in my DTO translation routine. My DTO's can have string properties that correspond to the name properties of related entities and this way I can access those without having to hard code the related entities' types.

In EF 4.1 and up, the ObjectContext is wrapped by a class named DbContext, which provides navigational properties with which to get instances of related entities using strings. To dynamically retrieve a singular related entity, you can use:

dynamic refObject = Activator.CreateInstance(refObjectType);

refObject = context.Entry(currentObject).Reference(refObjectType.Name).CurrentValue;

For those upgrading from 4.0: The recommended way to work with DbContext is not with EntityObjects but with POCOs. These can be made manually or they can be generated via the edmx context menu.

My current implementation is as follows:

// Loop through the propertyinfos of the dto's type
foreach (PropertyInfo pf in dtoType.GetProperties().Where(p => p.CanWrite))
{
    // Use the name of the dto property to get the corresponding property from the POCO's type. If it doesn't exist, pi will be null
    PropertyInfo pi = pocoType.GetProperty(pf.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

    if (pi != null)
    {
        // Check if the current propertyinfo of the POCO has a subproperty named Id
        // If this is the case we treat the propertyinfo as a referenced POCO
        if (pi.PropertyType.GetProperty("Id") != null)
        {
            // skip referenced POCOs if their data is not needed
            if (!includeRelated) continue;

            // Get the type of the referenced POCO
            Type refObjectType = pi.PropertyType;
            // Create an instance of the referenced POCO
            dynamic refObject = Activator.CreateInstance(refObjectType);

            // Create a type of GenericRepository<objectType>
            Type refObjectRepositoryType = typeof(GenericRepository<>).MakeGenericType(refObjectType);
            // Create an instance of GenericRepository<objectType>
            dynamic refObjectRepository = Activator.CreateInstance(refObjectRepositoryType);

            // Fill the dynamic POCO instance with the values of the referenced POCO instance
            refObject = refObjectRepository._context.Entry(poco).Reference(refObjectType.Name).CurrentValue;

            try
            {
                // Set the dto property with the name value of the referenced POCO instance
                // (i.e. dtoImage.ImageType = pocImage.ImageType.Name)
                pf.SetValue(dto, refObject.Name, null);
            }
            catch (RuntimeBinderException)
            {
                // this happens when the related entity is null, ie. in one to zero-or-one relationships
                continue;
            }

            continue;
        }

        // If the propertyinfo's propertytype does not have an Id property, just set the value of  
        // the dto property to that of the POCO's propertyinfo directly
        pf.SetValue(dto, pi.GetValue(poco, null), null);

    }
}

For now this code will only work for referenced entities that have both an Id and a Name property. Also, there is likely to be a performance penalty for this approach so I have implemented the flag includeRelated to toggle whether to query the related objects or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

庆幸我还是我 2025-01-05 08:19:21

您正在尝试从父级的 PropertyInfo 获取值,但 GetValue 需要 pi.PropertyType 类型的对象。你应该使用这样的东西:

using (var context = new MyContext())
{
    var cust = context.Customer.First();
    var custType = cust.CustomerType;

    var pi = typeof (Customer).GetProperty("CustomerType");
    var child = pi.PropertyType.GetProperty("CustomerTypeID");

    var res = child.GetValue(custType, null);
    // this returns value of Customer.CustomerTypeID
}

You're trying to get value from parent's PropertyInfo, but GetValue expected object of pi.PropertyType type. You should use something like this:

using (var context = new MyContext())
{
    var cust = context.Customer.First();
    var custType = cust.CustomerType;

    var pi = typeof (Customer).GetProperty("CustomerType");
    var child = pi.PropertyType.GetProperty("CustomerTypeID");

    var res = child.GetValue(custType, null);
    // this returns value of Customer.CustomerTypeID
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文