0

垂直キー値ペア ストア システムから従来の水平列ストア システムにデータを変換するシステムを構築しようとしています。

元データはこんな感じ

public class Container
{
  public string Type { get; set; }
  public IEnumerable<Attribute> Attributes { get; set; }
  public IEnumerable<Container> RelatedContainers { get; set; }
}

public class Attributes
{
  public string Name{ get; set; }
  public string Value { get; set; }
}

次のようなデータが生成されます

public class Person
{
  public string Name { get; set; }
  public IEnumerable<Address> Addresses { get; set; }
}


public class Address
{
  public string Line1 { get; set; }
  public string City { get; set; }
  public string State { get; set; }
  public string Zip { get; set; }
}

この状況にはいくつかの落とし穴があります。まず第一に、実行時までターゲット型のすべてのフィールドを把握しているわけではありません。そのための大まかな解決策があり、ソース データの構造に基づいて実行時に新しいクラスを生成できます。

ただし、データ自体を新しいクラスにマップする良い方法がわかりません。問題を解決するためのより簡単な方法を指摘したり、私が進んでいる道の次のステップで助けを求めたい.

4

4 に答える 4

1

ここに、最初に何かを提供すると思われるコードをいくつか示します。ネストされたオブジェクトは処理しませんが、ギャップを埋めるには十分なはずです。

質問のクラスを使用し、 Address オブジェクトを設定します。メソッド「CreateObjectFromContainer」は、実際に作業が行われる場所です。

using System;
using System.Collections.Generic;
using System.Linq;

namespace PopulateFromAttributes
{
class Program
{
    static void Main(string[] args)
    {
        // Set up some test data - an address in a Container
        var attributeData = new List<Attributes> 
        {
            new Attributes { Name = "Line1", Value = "123 Something Avenue" },
            new Attributes { Name = "City", Value = "Newville" },
            new Attributes { Name = "State", Value = "New York" },
            new Attributes { Name = "Zip", Value = "12345" },
        };
        Container container = new Container { Type = "Address", Attributes = attributeData };

        // Instantiate and Populate the object
        object populatedObject = CreateObjectFromContainer("PopulateFromAttributes", container);
        Address address = populatedObject as Address;

        // Output values
        Console.WriteLine(address.Line1);
        Console.WriteLine(address.City);
        Console.WriteLine(address.State);
        Console.WriteLine(address.Zip);
        Console.ReadKey();
    }

    /// <summary>
    /// Creates the object from container.
    /// </summary>
    /// <param name="objectNamespace">The namespace of the Type of the new object.</param>
    /// <param name="container">The container containing the object's data.</param>
    /// <returns>Returns a newly instantiated populated object.</returns>
    private static object CreateObjectFromContainer(string objectNamespace, Container container)
    {
        // Get the Type that we need to populate and instantiate an object of that type
        Type newType = Type.GetType(string.Format("{0}.{1}", objectNamespace, container.Type));
        object newObject = Activator.CreateInstance(newType);

        // Pass each attribute and populate the values
        var properties = newType.GetProperties();
        foreach (var property in properties)
        {
            var singleAttribute = container.Attributes.Where(a => a.Name == property.Name).FirstOrDefault();
            if (singleAttribute != null)
            {
                property.SetValue(newObject, singleAttribute.Value, null);
            }
        }

        return newObject;
    }
}

public class Container
{
    public string Type { get; set; }
    public IEnumerable<Attributes> Attributes { get; set; }
    public IEnumerable<Container> RelatedContainers { get; set; }
}

public class Attributes
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class Address
{
    public string Line1 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}
}
于 2013-01-08T22:04:14.633 に答える
0

私が提供できるアドバイスの 1 つは、System.Convert.ChangeType(...) メソッドを使用して、可能な場合は宛先の型に値を強制的に変換し、宛先の型で静的な Parse(...) メソッドを探すことです。 '文字列値から開始しています(上記のコードが示すように)。

于 2013-01-08T22:55:42.573 に答える
0

これはうまくいくようです:

object CreateObjectFromNVPair(Container c)
{
    Type t = Type.GetType(this.GetType().Namespace + "." + c.Type);
    object o = Activator.CreateInstance(t);
    if (c.Attributes != null)
    {
        foreach (Attribute a in c.Attributes)
        {
            PropertyInfo pi = o.GetType().GetProperty(a.Name);
            pi.SetValue(o, a.Value, null);
        }
    }
    if (c.RelatedContainers != null)
    {
        foreach (Container c2 in c.RelatedContainers)
        {
            Type lt = typeof(List<>);
            Type t2 = Type.GetType(this.GetType().Namespace + "." + c2.Type);
            PropertyInfo pi = o.GetType().GetProperty(c2.Type + "List");
            object l = pi.GetValue(o, null);
            if (l == null)
            {
                l = Activator.CreateInstance(lt.MakeGenericType(new Type[] { t2 }));
                pi.SetValue(o, l, null);
            }
            object o2 = CreateObjectFromNVPair(c2);
            MethodInfo mi = l.GetType().GetMethod("Add");
            mi.Invoke(l, new object[] { o2 });
        }
    }
    return o;
}

名前空間と、CreateInstance に使用されるアクティベーターまたはアセンブリにいくつかの変更が必要になる場合があります。

注: 一貫性を保つために、複数形のリストから末尾に「リスト」を追加するように名前を変更しました。

于 2013-01-09T15:40:33.260 に答える
0

宛先クラスをバインドするために .NET Reflection を使用するのはどうですか? 私はあなたが望むことをする方法を与えると信じている1つのサンプルを見つけました:

http://www.codeproject.com/Articles/55710/Reflection-in-NET

于 2013-01-08T20:38:21.473 に答える