2

しばらく頭を悩ませてきましたが、何かが足りないと思いますので、誰か助けてくれるかもしれません。

次のマッパークラスがあるとしましょう。

public class Mapping<TSource, TResult>
{
    private readonly Action<TSource, TResult> setter;

    public Mapping(Expression<Func<TSource, TResult>> expression)
    {
        var newValue = Expression.Parameter(expression.Body.Type);
        var body = Expression.Assign(expression.Body, newValue);
        var assign = Expression.Lambda<Action<TSource, TResult>>(body, expression.Parameters[0], newValue);

        setter = assign.Compile();
    }

    public void Assign(TSource instance, TResult value)
    {
        setter(instance, value);
    }
}

そしてそれはうまく機能しています:

    [Test]
    public void ShouldMapProperty()
    {
        var testClass = new TestClass();

        var nameMapping = new Mapping<TestClass, string>(x => x.Name);
        var ageMapping = new Mapping<TestClass, int>(x => x.Age);

        nameMapping.Assign(testClass, "name");
        ageMapping.Assign(testClass, 10);

        Assert.AreEqual("name", testClass.Name);
        Assert.AreEqual(10, testClass.Age);       
    }

つまり、単一のオブジェクトタイプのマッピングをいくつかのコレクションに保持したいのですが、プロパティごとにタイプが異なる限り、TResultが邪魔になります。TResultをうまく取り除く方法は?

更新: 私は十分に明確ではなかったように見えるので、これは私がそれをどのように使用するかについてのサンプルになります:

 public class Mapping<TSource, TResult>
{
    private readonly Action<TSource, TResult> setter;
    private readonly string columnName;

    public Mapping(Expression<Func<TSource, TResult>> expression, string columnName)
    {
        this.columnName = columnName;            

        var newValue = Expression.Parameter(expression.Body.Type);
        var body = Expression.Assign(expression.Body, newValue);
        var assign = Expression.Lambda<Action<TSource, TResult>>(body, expression.Parameters[0], newValue);

        setter = assign.Compile();
    }

    public void Assign(TSource instance, DataRow row)
    {
        setter(instance, row[columnName]);
    }
}

そして、MappingConfigurationクラスがあり、これを実行できます。

MappingConfiguration.For<TestClass>()
  .Map(x => x.Name, "FirstName")
  .Map(x => x.Age, "Age");

そして最後に、DataTableとMappingConfigurationを入力として受け取り、IEnumerable<TestClass>出力として生成するMappingEngineクラスをいくつか作成します。

アップデート2: 初期バージョンを次のように変更しました:

public class Mapping2<TSource>
{
    private readonly Delegate setter;

    public Mapping2(Expression<Func<TSource, object>> expression)
    {
        var newValue = Expression.Parameter(expression.Body.Type);
        var body = Expression.Assign(expression.Body, newValue);
        var assign = Expression.Lambda(body, expression.Parameters[0], newValue);            

        setter = assign.Compile();
    }

    public void Assign(TSource instance, object value)
    {
        setter.DynamicInvoke(instance, value);
    }
}

そして、それはほとんど機能します。
ほぼ私はそれが参照型のプロパティで動作し、値型のプロパティで動作することを意味します:

System.ArgumentException:式は書き込み可能である必要があります
パラメーター名:左

4

2 に答える 2

2

私はなんとかそれをすることができました、以下のソースコード。Automapperよりもいくらか高速に実行され(Automapper構成がこのタスクで最速かどうかはわかりません)、ベンチマークは防弾ではありませんが、私のマシンで500万行をマップするには、作成したマッパーを使用して20.16秒、Automapperを使用して39.90秒かかりました。 Automapperがこのタスクに使用するメモリが少なくなっています(測定していませんが、1,000万行の場合、Automapperは結果を出し、私のマッパーはOutOfMemoryで失敗します)。

public class MappingParameter<TSource>
{
    private readonly Delegate setter;

    private MappingParameter(Delegate compiledSetter)
    {
        setter = compiledSetter;
    }

    public static MappingParameter<TSource> Create<TResult>(Expression<Func<TSource, TResult>> expression)
    {
        var newValue = Expression.Parameter(expression.Body.Type);
        var body = Expression.Assign(expression.Body, newValue);
        var assign = Expression.Lambda(body, expression.Parameters[0], newValue);

        var compiledSetter = assign.Compile();

        return new MappingParameter<TSource>(compiledSetter);
    }

    public void Assign(TSource instance, object value)
    {
        object convertedValue;
        if (!setter.Method.ReturnType.IsAssignableFrom(typeof(string)))
        {
            convertedValue = Convert.ChangeType(value, setter.Method.ReturnType);
        }
        else
        {
            convertedValue = value;
        }

        setter.DynamicInvoke(instance, convertedValue);
    }
}

public class DataRowMappingConfiguration<TSource>
{
    private readonly Dictionary<string, MappingParameter<TSource>> mappings =
        new Dictionary<string, MappingParameter<TSource>>();

    public DataRowMappingConfiguration<TSource> Add<TResult>(string columnName,
                                                             Expression<Func<TSource, TResult>> expression)
    {
        mappings.Add(columnName, MappingParameter<TSource>.Create(expression));
        return this;
    }

    public Dictionary<string, MappingParameter<TSource>> Mappings
    {
        get
        {
            return mappings;
        }
    }
}

public class DataRowMapper<TSource>
{
    private readonly DataRowMappingConfiguration<TSource> configuration;

    public DataRowMapper(DataRowMappingConfiguration<TSource> configuration)
    {
        this.configuration = configuration;
    }

    public IEnumerable<TSource> Map(DataTable table)
    {
        var list = new List<TSource>(table.Rows.Count);

        foreach (DataRow dataRow in table.Rows)
        {
            var obj = (TSource)Activator.CreateInstance(typeof(TSource));

            foreach (var mapping in configuration.Mappings)
            {
                mapping.Value.Assign(obj, dataRow[mapping.Key]);
            }

            list.Add(obj);
        }

        return list;
    }
}

public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

[TestFixture]
public class DataRowMappingTests
{      
    [Test]
    public void ShouldMapPropertiesUsingOwnMapper()
    {            
        var mappingConfiguration = new DataRowMappingConfiguration<TestClass>()
            .Add("firstName", x => x.Name)
            .Add("age", x => x.Age);

        var mapper = new DataRowMapper<TestClass>(mappingConfiguration);                      

        var dataTable = new DataTable();
        dataTable.Columns.Add("firstName");
        dataTable.Columns.Add("age");

        for (int i = 0; i < 5000000; i++)
        {
            var row = dataTable.NewRow();
            row["firstName"] = "John";
            row["age"] = 15;

            dataTable.Rows.Add(row);                
        }

        var start = DateTime.Now;

        var result = mapper.Map(dataTable).ToList();

        Console.WriteLine((DateTime.Now - start).TotalSeconds);

        Assert.AreEqual("John", result.First().Name);
        Assert.AreEqual(15, result.First().Age);
    }

    [Test]
    public void ShouldMapPropertyUsingAutoMapper()
    {
        Mapper.CreateMap<DataRow, TestClass>()
            .ForMember(x => x.Name, x => x.MapFrom(y => y["firstName"]))
            .ForMember(x => x.Age, x => x.MapFrom(y => y["age"]));

        var dataTable = new DataTable();
        dataTable.Columns.Add("firstName");
        dataTable.Columns.Add("age");

        for (int i = 0; i < 5000000; i++)
        {
            var row = dataTable.NewRow();
            row["firstName"] = "John";
            row["age"] = 15;

            dataTable.Rows.Add(row);
        }

        var start = DateTime.Now;

        var result = dataTable.Rows.OfType<DataRow>().Select(Mapper.Map<DataRow, TestClass>).ToList();         

        Console.WriteLine((DateTime.Now - start).TotalSeconds);

        Assert.AreEqual("John", result.First().Name);
        Assert.AreEqual(15, result.First().Age);
    }
}
于 2012-07-18T15:02:19.627 に答える
1

多分thidのような何か:

public class Mapping<TSource>
{
    public void Assign<TResult>(TSource instance, TResult value)
    {
        var property = typeof(TSource).GetProperties().FirstOrDefault(p => p.PropertyType == typeof(TResult)));
        if (property != null)
        {
            property.SetValue(instance, value, new object[0]);
        }

    }
}

ただし、これを正確に行うには、オブジェクトに各タイプのプロパティが1つ必要です。

より一般的にすることもできますが、より危険です:

    public void Assign<TResult>(TSource instance, TResult value)
    {
        var property = typeof(TSource).GetProperties().FirstOrDefault(p => p.PropertyType.IsAssignableFrom(typeof(TResult)));
        if (property != null)
        {
            property.SetValue(instance, value, new object[0]);
        }

    }

(同じ基本クラスから継承する2つのプロパティがある場合、これは機能しません)...

于 2012-07-18T08:47:19.187 に答える