Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用LINQ动态映射(或构造投影)_Linq_Mapping_Map Projections - Fatal编程技术网

使用LINQ动态映射(或构造投影)

使用LINQ动态映射(或构造投影),linq,mapping,map-projections,Linq,Mapping,Map Projections,我知道我可以使用LINQ使用投影映射两种对象类型,如下所示: var destModel = from m in sourceModel select new DestModelType {A = m.A, C = m.C, E = m.E} 在哪里 但是如果我想做一些类似于泛型的东西来做这件事,我不知道我要处理的两种类型。因此,它将遍历“Dest”类型并与匹配的“Source”类型匹配。。这可能吗?另外,为了实现延迟执行,我希望它只返回一个IQueryable

我知道我可以使用LINQ使用投影映射两种对象类型,如下所示:

var destModel = from m in sourceModel
               select new DestModelType {A = m.A, C = m.C, E = m.E}
在哪里

但是如果我想做一些类似于泛型的东西来做这件事,我不知道我要处理的两种类型。因此,它将遍历“Dest”类型并与匹配的“Source”类型匹配。。这可能吗?另外,为了实现延迟执行,我希望它只返回一个IQueryable

例如:

public IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
{
   // dynamically build the LINQ projection based on the properties in TDest

   // return the IQueryable containing the constructed projection
}
public IQueryable ProjectionMap(IQueryable sourceModel)
{
//基于TDest中的属性动态构建LINQ投影
//返回包含构造投影的IQueryable
}

我知道这很有挑战性,但我希望这不是不可能的,因为这将为我节省大量模型和viewmodels之间的显式映射工作。

您必须生成一个表达式树,但这是一个简单的表达式树,所以这并不难

void Main()
{
    var source = new[]
    {
        new SourceModelType { A = "hello", B = "world", C = "foo", D = "bar", E = "Baz" },
        new SourceModelType { A = "The", B = "answer", C = "is", D = "42", E = "!" }
    };

    var dest = ProjectionMap<SourceModelType, DestModelType>(source.AsQueryable());
    dest.Dump();
}

public static IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
    where TDest : new()
{
    var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
    var destProperties =   typeof(TDest).GetProperties().Where(p => p.CanWrite);
    var propertyMap = from d in destProperties
                      join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
                      select new { Source = s, Dest = d };
    var itemParam = Expression.Parameter(typeof(TSource), "item");
    var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source)));
    var newExpression = Expression.New(typeof(TDest));
    var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
    var projection = Expression.Lambda<Func<TSource, TDest>>(memberInitExpression, itemParam);
    projection.Dump();
    return sourceModel.Select(projection);
}

感谢您提供此解决方案。我深入了解它是如何工作的。如果我想让它钻入复杂对象,我必须修改propertyMap,对吗?如果你想了解表达式是如何构造的,我建议你使用LinqPad;它允许您轻松地检查表达式的每个节点。至于你的问题,我不太明白你的意思。。。如果您只知道源和目标类型,那么除了复制具有相同名称的属性之外,您真的无法执行任何更复杂的操作。如果您希望合并复杂对象,以便item=>new DestModelType(){A=item.A.X,C=item.C,E=item.E},该怎么办。这可以通过一个属性来指定映射到的对象。我认为这会更加困难,但仍然可行。。。您必须根据自己的逻辑更改
属性映射
成员绑定
void Main()
{
    var source = new[]
    {
        new SourceModelType { A = "hello", B = "world", C = "foo", D = "bar", E = "Baz" },
        new SourceModelType { A = "The", B = "answer", C = "is", D = "42", E = "!" }
    };

    var dest = ProjectionMap<SourceModelType, DestModelType>(source.AsQueryable());
    dest.Dump();
}

public static IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
    where TDest : new()
{
    var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
    var destProperties =   typeof(TDest).GetProperties().Where(p => p.CanWrite);
    var propertyMap = from d in destProperties
                      join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
                      select new { Source = s, Dest = d };
    var itemParam = Expression.Parameter(typeof(TSource), "item");
    var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source)));
    var newExpression = Expression.New(typeof(TDest));
    var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
    var projection = Expression.Lambda<Func<TSource, TDest>>(memberInitExpression, itemParam);
    projection.Dump();
    return sourceModel.Select(projection);
}
item => new DestModelType() {A = item.A, C = item.C, E = item.E}