简体   繁体   English

根据运行时已知的属性列表动态创建对象

[英]Dynamically create an object from list of properties known at run-time

I have results (xml format, <Prop0> , <Prop1> , ...) coming from an API that I am saving into my database table. 我的结果(xml格式, <Prop0><Prop1> ,...)来自要保存到数据库表中的API。 I have a mapping of the Props to my table columns. 我将道具映射到表列。 I would like to create an object on the fly with property names coming from the mapping and values coming from the API results. 我想动态创建一个对象,其属性名称来自映射,值来自API结果。

I could use the mapping dictionary and the API results to directly save the values in the database, but all the insert functions (remote to my application) are designed to use List of objects. 我可以使用映射字典和API结果直接将值保存在数据库中,但是所有插入功能(远程到我的应用程序)都设计为使用对象列表。

I have explored System.Dynamic.ExpandoObject but one cannot add properties on the fly to it (from, say, a dictionary). 我已经探索了System.Dynamic.ExpandoObject,但是无法动态添加属性(例如从字典中)。 Currently, I am using the following generic but strongly typed solution, for which all the attributes need to be present in the class. 当前,我正在使用以下通用但强类型的解决方案,该类的所有属性都必须存在。

internal T FetchPopulatedObject<T>(Dictionary<string, string> apiToTableMapping, Dictionary<string, string> apiResultsKeyVal, T objectToPopulate) where T : new()
{
  if (objectToPopulate == null)
    objectToPopulate = new T();

  PropertyInfo currProp;
  Type currActualType;
  string valToSet = string.Empty;
  int valToSetNumeric = 0;
  object finalValToSet = new object();

  foreach (KeyValuePair<string, string> currMap in apiToTableMapping)
  {
    valToSet = string.Empty;
    currProp = (typeof(T).GetProperty(currMap.Value));
    if (currProp == null)
    {
      log.Info("Could not find property for table column " + currMap.Value + ". Moving on.");
      continue;
    }
    bool valResult = apiResultsKeyVal.TryGetValue(currMap.Key, out valToSet);
    if (!valResult)
    {
      log.Info("Could not find value in API Results for property: " + currMap.Key + ".");
      continue;
    }

    currActualType = Nullable.GetUnderlyingType(currProp.PropertyType) ?? currProp.PropertyType;

    if (string.IsNullOrEmpty(valToSet))
    {
      log.Info("Property " + currProp.Name + " is of " + currProp.PropertyType.Name + " type but API attribute " +
        currMap.Key + " returned an incompatible or empty value " + valToSet + ". Skipping this field.");
      continue;
    }
    else
    {
      finalValToSet = Int32.TryParse(valToSet, out valToSetNumeric) ? Convert.ChangeType(valToSetNumeric, currActualType) : Convert.ChangeType(valToSet, currActualType);
    }

    currProp.SetValue(objectToPopulate, finalValToSet, null);
  }

  return objectToPopulate;

}

However, this is infeasible, since the API results keep changing and adding new attributes. 但是,这是不可行的,因为API结果不断变化并添加新属性。

Can somebody suggest how to accomplish the same without having to have all the properties already in class T? 有人可以建议如何完成相同的任务,而不必拥有T类中已经存在的所有属性吗?

Other than Emit, I can only think of a dynamic compiled type, example: 除了Emit之外,我只能想到动态编译类型,例如:

       static Type GetMeAType( string myTypeName, string myStringPropertyName ) {

            string codeToCompile = "using System; public class " + myTypeName + " {public string " +
                                   myStringPropertyName + " { get; set; } }";
            string[] references = { "System.dll", "System.Core.dll" };

            CompilerParameters compilerParams = new CompilerParameters();

            compilerParams.GenerateInMemory = true;
            compilerParams.TreatWarningsAsErrors = false;
            compilerParams.GenerateExecutable = false;
            compilerParams.CompilerOptions = "/optimize";
            compilerParams.ReferencedAssemblies.AddRange( references );

            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerResults compile = provider.CompileAssemblyFromSource( compilerParams, codeToCompile );
            Module dynamicModule = compile.CompiledAssembly.GetModules()[ 0 ];
            var type = dynamicModule.GetType( myTypeName );

            return type;

        }

The only way to dynamically create a type in C# is by using the System.Reflection.Emit namespace. 在C#中动态创建类型的唯一方法是使用System.Reflection.Emit命名空间。 You cannot use a generic in this case, because even though the type T is not specified, it still implies type-safety, which requires a type to be created during compile-time. 在这种情况下,您不能使用泛型,因为即使未指定类型T ,它仍然暗含类型安全性,这要求在编译时创建类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM