如何在C#中为动态不同数量的Data对象动态创建属性(数据对象实例)? [英] how to dynamically create properties (Data object instances) in C# for a dynamically varying number of Data objects?

查看:81
本文介绍了如何在C#中为动态不同数量的Data对象动态创建属性(数据对象实例)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在C#中为动态不同数量的Data对象动态创建属性(数据对象实例)?



我希望这通过Linq到SQL实现存储过程,其返回类型无法识别,因为我的SP根据输入返回不同数量的列。 div class =h2_lin>解决方案

所以这不是太难,但你会在反射命名空间中深入研究。第一个问题是弄清楚从服务器返回的动态对象可能是什么样子。您可以使用反射来获取给定对象上的属性,然后使用PropInfo来查找该属性的类型。从那里,您可以创建一个具有所需属性的新类型,并使用一些generic-method-foo,将数据对象中的值转换为新建的动态对象。



我有一个处理来自Web服务的返回对象的方法,该方法可能存在或不存在某些字段。这允许我消除对服务代理类的依赖性并且可以灵活地返回可能返回的内容。该方法检查给定属性的对象,并尝试将值分配给对象上的字段。该类有一个构造函数,它接受从Web服务返回的对象(作为类型对象),并调用此方法将值映射到类中的字段。



该方法如下所示:

  public   void  MapDtoToFields( object  serviceDto)
{
try
{
// 直接映射到基本类型的常规属性
AcknowledgeDeadline = PortableDtoHelpers.CastDtoToField< ; DateTime?> (serviceDto, acknowledgeDeadline< /跨度>);
AdmitDate = PortableDtoHelpers.CastDtoToField< DateTime?> (serviceDto, admitDate);
AssociatedRequest = PortableDtoHelpers.CastDtoToField< string>(serviceDto, request);
AssociatedRequestUnivId = PortableDtoHelpers.CastDtoToField< string>(serviceDto, requestUniversalId);
ClosedDate = PortableDtoHelpers.CastDtoToField< DateTime?> (serviceDto, closedDate);
ClosingComments = PortableDtoHelpers.CastDtoToField< string>(serviceDto, closingComments);

// 复杂属性映射到另一个内部平台类型
RoutingRecipient = new CMPayerObject(PortableDtoHelpers.CastDtoToField< object>(serviceDto, routingRecipientDto));

// 映射到集合的数组属性
object [] serviceDtoAttachedDiagnoses = PortableDtoHelpers.CastDtoToField< object [] > (serviceDto, umAttachedDiagnosisDtos);
if (serviceDtoAttachedDiagnoses!= null
foreach object item in serviceDtoAttachedDiagnoses)
Diagnoses.AttachedDiagnoses。添加( new CMDiagnosisObject(item));
}
catch (例外情况)
{
logger.Log(CommonLogLevel.Error, string .Format( 将服务器Dto映射到字段时出现异常。例外是{0},ex.ToString()));
}
}





它的要点是每一行都使用通用方法CastDtoToField< t>检查指定属性的服务对象,然后尝试返回值的强类型实例。 CastDtoToFields方法存在于PCL中的静态类中,因此我们可以从使用该PCL(可移植类库)的任何地方访问它。



方法如下:

  ///   <  摘要 >  
/// 投放指定的字段在提供的服务Dto到正确的类型并将其返回给调用者。
/// < / summary >
/// < typeparam < span class =code-summarycomment> name =T > 类型目标数据字段< / typeparam >
/// < param name =serviceDto > 从Web服务返回的dto < / param >
/// < param name =dtoFieldName > 要在Dto上映射的字段的名称。< / param >
/// < 返回 > 类型值< / returns >
public static T CastDtoToField< T>( object serviceDto, string dtoFieldName)
{
try
{
logger.Log(CommonLogLevel.Trace, 输入CastDtoToField< T>方法。);
// 防御第一。
if (serviceDto == null | string .IsNullOrWhiteSpace(dtoFieldName))
{
logger.Log(CommonLogLevel.Warn, 返回默认值值,因为服务dto为null或字段名称为空。);
return 默认(T);
}

// 测试到查看目标属性是否存在于Dto
if (!PortableClassHelpers.HasProperty(serviceDto,dtoFieldName))
{
// Property doesn'存在!
logger.Log(CommonLogLevel.Warn, string .Format( 提供的服务dto(类型{0})上没有属性{1}。
serviceDto.GetType()。ToString (),dtoFieldName));
return 默认(T);
}

// 服务dto上存在属性。现在看看是否有指定属性。
bool hasSpecified = false ;
string specifiedProperty = dtoFieldName + 指定的 ;
hasSpecified = PortableClassHelpers.HasProperty(serviceDto,specifiedProperty);

// 如果存在Specified属性,则源类型不可为空。
if (hasSpecified)
{
// 源属性不可为空,我们必须查看...
如果 (PortableClassHelpers.CastProperty< bool>(serviceDto,specifiedProperty))
{
// 指定的property设置为TRUE,因此附加属性有一个值,我们应该
// 做一个直接演员。
// 试一试这个并抛回如果出错则默认。
// (是的...我们有一个外部的try-catch,但这样做可以让我们记录特定错误。
尝试
{
logger.Log(CommonLogLevel.Trace, string .Format( 属性{0}附加了指定属性设置为true。返回目标属性的直接强制转换。,dtoFieldName));
return PortableClassHelpers.CastProperty< T>(serviceDto,dtoFieldName);
}
catch (例外情况)
{
logger.LogException(CommonLogLevel.Fatal, string .Format( 尝试转换属性{0时出现异常检测到'指定'属性并将其设置为true但直播失败。,dtoFieldName),ex);
return 默认(T);
}
}
其他
{
// 指定的属性为FALSE,因此附加属性没有有效值...即使它有值。
// < span class =code-comment>所以只返回默认值。

logger.Log(CommonLogLevel.Trace, string .Format( 属性{0}附加了Specified属性,该属性设置为false。返回目标属性的默认值。,dtoFieldName));
return default (T);
}
}
else
{
// 没有指定的字段,所以我们尝试直接演员。
// 如果出现问题,请尝试将其包装并退回默认值。
// (是的......我们有一个外部的try-catch,但这样做可以让我们记录特定的错误。
try
{
logger.Log(CommonLogLevel.Trace, string .Format( 属性{0}没有有一个'指定'属性。返回目标属性的直接强制转换。,dtoFieldName));
return PortableClassHelpers.CastProperty< T>(serviceDto,dtoFieldName);
}
catch (例外情况)
{
logger.LogException(CommonLogLevel.Fatal, string .Format( 尝试转换属性{0时出现异常没有检测到指定属性并且直播失败。,dtoFieldName),ex);
return 默认(T);
}
}
}
catch (例外oex)
{
logger.LogException(CommonLogLevel.Fatal, string .Format( 尝试转换属性{0}时发生异常。有关详细信息,请参阅异常。,dtoFieldName),oex);
return default (T);
}
}





哇!所以这段代码看看是否定义的属性出现在源对象上并返回该属性的强类型值。请注意,该代码没有保证。在过去的一年中,这里出现了一个错误,并且出现了一个错误,它将无法正确转换所以,如果你使用它,请注意它可能无法正确地转换所有内容。数组并且不实现IConvertable的类型似乎特别麻烦。



I不要在这里进行下一步,但你应该可以从中扩展它。要实现你想要的,你需要一个基类,你将动态扩展以保持你的可变大小的响应。我建议你定义一个该基类的接口,并在所有代码中使用该接口帽子方式,你可以传递实例,没有各种蹩脚的类型铸造。重要的是要知道你不能将物业添加到一个不受欢迎的物体。因此,您必须创建一个从基类型扩展的新对象,并在创建它的实例之前向其添加其他属性。 System.Reflection.Emit命名空间具有您要使用的TypeBuilder类。有一个动态构建类型,创建实例,以及在TypeBuilder的MSDN页面上设置属性值的示例。你可以在这里看到它: https://msdn.microsoft.com/en-us/library/2sd82fz7。 aspx [ ^ ]那个应该给你你需要的另一半。



所以你现在应该可以将它们组合在一起。



祝你好运!


how to dynamically create properties (Data object instances) in C# for a dynamically varying number of Data objects?

I want this for implementing a stored procedure through Linq to SQL whose return type can't be recognized as my SP returns varying number of columns based on the input.

解决方案

So this isn't too hard but you will be neck deep in the reflection namespaces. The first problem is figuring out what a dynamic object returned from the server may look like. You can use reflection to get the properties that are on a given object then use PropInfo to find out the type of that property. From there, you can create a new type that has the properties you want and using some generic-method-foo, cast the values from your data object to your newly minted dynamic object.

I have a method for processing return object from a web service that may or may not have certain fields present. This allows me to eliminate a dependency on the service proxy class and to be flexible about what may be returned. That method inspects the object for a given property and attempts to assign the value to a field on the object. The class has a constructor which accepts the object returned from the web service (as type object) and calls this method to map the values onto the fields in the class.

That method looks like this:

public void MapDtoToFields(object serviceDto)
{
    try
    {
        // General properties mapped straight to base types
        AcknowledgeDeadline = PortableDtoHelpers.CastDtoToField<DateTime?>(serviceDto, "acknowledgeDeadline");
        AdmitDate = PortableDtoHelpers.CastDtoToField<DateTime?>(serviceDto, "admitDate");
        AssociatedRequest = PortableDtoHelpers.CastDtoToField<string>(serviceDto, "request");
        AssociatedRequestUnivId = PortableDtoHelpers.CastDtoToField<string>(serviceDto, "requestUniversalId");
        ClosedDate = PortableDtoHelpers.CastDtoToField<DateTime?>(serviceDto, "closedDate");
        ClosingComments = PortableDtoHelpers.CastDtoToField<string>(serviceDto, "closingComments");

        // Complex property mapped to another internal platform type
        RoutingRecipient = new CMPayerObject(PortableDtoHelpers.CastDtoToField<object>(serviceDto, "routingRecipientDto"));

        // Array property mapped to a collection
        object[] serviceDtoAttachedDiagnoses = PortableDtoHelpers.CastDtoToField<object[]>(serviceDto, "umAttachedDiagnosisDtos");
        if(serviceDtoAttachedDiagnoses != null)
            foreach (object item in serviceDtoAttachedDiagnoses)
                Diagnoses.AttachedDiagnoses.Add(new CMDiagnosisObject(item));
    }
    catch (Exception ex)
    {
        logger.Log(CommonLogLevel.Error, string.Format("There was an exception while mapping the server Dto to the fields. The exception is {0}", ex.ToString()));
    }
}



The gist of it is that each line utilizes the generic method CastDtoToField<t> to inspect the service object for the specified property then attempt to return a strongly-typed instance of the value. The CastDtoToFields method lives in a static class in a PCL so we have access to it from anywhere that uses that PCL (Portable Class Library).

The method looks like:

/// <summary>
/// Casts the field specified on the provided service Dto to the proper type and returns that to the caller.
/// </summary>
/// <typeparam name="T">The Type of the target data field</typeparam>
/// <param name="serviceDto">The dto returned from the web service</param>
/// <param name="dtoFieldName">The name of the field to map on the Dto.</param>
/// <returns>A typed value</returns>
public static T CastDtoToField<T>(object serviceDto, string dtoFieldName)
{
    try
    {
        logger.Log(CommonLogLevel.Trace, "Entering CastDtoToField<T> method.");
        // Defense first.
        if(serviceDto == null | string.IsNullOrWhiteSpace(dtoFieldName))
        {
            logger.Log(CommonLogLevel.Warn, "Returning default value because the service dto is null or the field name was empty.");
            return default(T);
        }

        // Test to see if the target property exists on the Dto
        if(!PortableClassHelpers.HasProperty(serviceDto, dtoFieldName))
        {
            // Property doesn't exist!
            logger.Log(CommonLogLevel.Warn, string.Format("The provided service dto (type {0}) does not have the property {1} on it.",
                serviceDto.GetType().ToString(), dtoFieldName));
            return default(T);
        }

        // Property exists on the service dto. Now see if there is a "specified" property.
        bool hasSpecified = false;
        string specifiedProperty = dtoFieldName + "Specified";
        hasSpecified = PortableClassHelpers.HasProperty(serviceDto, specifiedProperty);

        // If the "Specified" property exists, then the source type is not-nullable.
        if(hasSpecified)
        {
            // Source property is not nullable and we have to look to see...
            if(PortableClassHelpers.CastProperty<bool>(serviceDto, specifiedProperty))
            {
                // The specified property is set to TRUE so the attached property has a value and we should be able to
                // do a straight cast.
                // Wrap this in a try and toss back the default if it goes wrong.
                // (Yes... we have an outer try-catch but doing this here lets us log the specific error.
                try
                {
                    logger.Log(CommonLogLevel.Trace, string.Format("The property {0} has an attached 'Specified' property which is set to true. Returning the straight cast of the target property.", dtoFieldName));
                    return PortableClassHelpers.CastProperty<T>(serviceDto, dtoFieldName);
                }
                catch (Exception ex)
                {
                    logger.LogException(CommonLogLevel.Fatal, string.Format("There was an exception while trying to cast the property {0}. A 'specified' property was detected and set to true but the straight-cast failed.", dtoFieldName),ex);
                    return default(T);
                }
            }
            else
            {
                // The specified property is FALSE so the attached property doesn't have a valid value... even if it has a value.
                // So just return the default value.
                logger.Log(CommonLogLevel.Trace, string.Format("The property {0} has an attached 'Specified' property which is set to false. Returning the default of the target property.", dtoFieldName));
                return default(T);
            }
        }
        else
        {
            // No specified field so we try a straight cast.
            // Wrap this in a try and toss back the default if it goes wrong.
            // (Yes... we have an outer try-catch but doing this here lets us log the specific error.
            try
            {
                logger.Log(CommonLogLevel.Trace, string.Format("The property {0} does not have a 'Specified' property. Returning the straight cast of the target property.", dtoFieldName));
                return PortableClassHelpers.CastProperty<T>(serviceDto, dtoFieldName);
            }
            catch (Exception ex)
            {
                logger.LogException(CommonLogLevel.Fatal, string.Format("There was an exception while trying to cast the property {0}. No 'specified' property was detected and the straight-cast failed.", dtoFieldName),ex);
                return default(T);
            }
        }
    }
    catch (Exception oex)
    {
        logger.LogException(CommonLogLevel.Fatal, string.Format("There was an exception while trying to cast the property {0}. See the exception for details.", dtoFieldName), oex);
        return default(T);
    }
}



Whew! So that block of code looks to see if the defined property is present on the source object and returns a strongly typed value of that property. Note that there is no warranty on that code. Over the last year a bug here and a bug there has popped up where it won't properly cast something so if you use it, be aware that it may not correctly cast everything. Types that are arrays and don't implement IConvertable seem to be especially troublesome.

I don't go to the next step here but you should be able to extend it from this. To achieve what you want, you will need a base class that you will dynamically extend to hold your variable-sized response. I suggest also defining an interface for that base class and using the interface in all of your code. That way you can pass around the instance without all kinds of crappy type-casting. The important thing to know is that you CAN'T ADD PROPERTIES TO AN INSTANTIATED OBJECT. So you have to create a new object extending from your base type and add the additional properties to it before you create an instance of it. The System.Reflection.Emit namespace has the TypeBuilder class which is what you want to use. There is an example of building a type dynamically, creating an instance, and setting the value of a property on the MSDN page for TypeBuilder. You can see it here: https://msdn.microsoft.com/en-us/library/2sd82fz7.aspx[^] That should give you the other half of what you need.

So you should be able to tie it all together now.

Good luck!


这篇关于如何在C#中为动态不同数量的Data对象动态创建属性(数据对象实例)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆