【问题标题】:c# How can I create a collection of custom objects without going through each fieldc#如何在不遍历每个字段的情况下创建自定义对象的集合
【发布时间】:2016-07-21 18:11:59
【问题描述】:

我创建了一个名为 DataResponse 的类,它有 40 多个公共字段。 DataResponse 类具有与我的数据库 DataRepoes 中相同数量的字段和类型(假设是这样)。

有没有办法在下面创建对象列表并自动将字段分配给DataResponse 从数据库中的内容中执行类似 linq 的操作?否则我必须拼出每一个这 40 个字段,并在我新建 DataResponse 类时手动分配它们。谢谢

List<Classes.DataResponse> res = (from rx in con.DataRepoes
  where iaccess.Contains(rx.STOREID)
  select new Classes.DataResponse).ToList<Classes.DataResponse>();

【问题讨论】:

标签: c# linq collections


【解决方案1】:

您可以为此使用 Automapper Queryable Extensions

假设Classes.DataResponse 中的字段与DataRepoes 中的字段名称相同,那么您可以这样做:

// During your application bootstrap, configure AutoMapper to create a map between the two types
Mapper.Initialize(cfg => cfg.CreateMap<DataRepoes, Classes.DataResponse);


// Then you can ask AutoMapper to project the IQueryable directly to your DTO
List<Classes.DataResponse> res = con.DataRepoes
                                    .Where(rx => iaccess.Contains(rx.STOREID))
                                    .ProjectTo<Classes.DataResponse>()
                                    .ToList();                                    

【讨论】:

    【解决方案2】:

    如果您不需要 AutoMapper 提供的灵活性或不想使用第三方库,您可以使用以下简化的自定义扩展方法:

    public static class QueryableExtensions
    {
        public static IQueryable<TResult> SelectTo<TResult>(this IQueryable source)
        {
            var sourceType = source.ElementType;
            var resultType = typeof(TResult);
            var parameter = Expression.Parameter(sourceType, "x");
            var bindings =
                from rm in resultType.GetProperties().Concat<MemberInfo>(resultType.GetFields())
                join sm in sourceType.GetProperties().Concat<MemberInfo>(sourceType.GetFields())
                    on rm.Name equals sm.Name
                select Expression.Bind(rm, Expression.MakeMemberAccess(parameter, sm));
            var body = Expression.MemberInit(Expression.New(resultType), bindings);
            return source.Provider.CreateQuery<TResult>(Expression.Call(
                typeof(Queryable), "Select", new[] { sourceType, resultType },
                source.Expression, Expression.Quote(Expression.Lambda(body, parameter))));
        }
    }
    

    它将尝试选择按名称匹配的所有属性/字段。如果匹配的属性/字段类型不同,则会失败。

    示例用法:

    方法语法

    var res = con.DataRepoes
        .Where(rx => iaccess.Contains(rx.STOREID))
        .SelectTo<Classes.DataResponse>()
        .ToList();
    

    查询语法

    var res =
        (from rx in con.DataRepoes
         where iaccess.Contains(rx.STOREID)
         select rx)
        .SelectTo<Classes.DataResponse>()
        .ToList();
    

    【讨论】:

    • 谢谢。是的,我不喜欢使用第三方库,因为它必须经过安全审查。我会试试看。
    • 谢谢伊万。就像一个哭泣的婴儿的摇篮曲。如果你能仔细阅读你的代码并向我们展示如何钓鱼,那对于下一个傻瓜(像我一样)来说会很棒。我一直理解它,直到 rm.Name 上的 ` 等于 sm.Name` 行。
    • 大声笑,这是一种按名称将源属性/字段映射到目标属性/字段的 LINQ 方法 - 请参阅 join。很高兴有帮助,编码愉快:)
    • 不错。与 AutoMapper 所做的相比,我在这段代码中看到的一个问题是,每次调用该方法时,您都必须在运行时执行反射和映射,这会变得非常昂贵。 AutoMapper 会预先构建一次地图,并在 AppDomain 的整个生命周期内将其缓存。您可能想在这里做类似的事情以避免一直做反射工作。
    • 我认为与执行数据库查询时发生的情况相比,这可以忽略不计 :)
    【解决方案3】:

    这在使用 LINQ 时是不可能的。 但是,您可以使用“AutoMapper”来实现这一点。 只需为这两个类创建映射,然后映射它们

    Mapper.CreateMap<DataResponse,DataRepo>();//Assuming DataRepoes is collection of DataRepo types
    List<Classes.DataResponse> res = (from rx in con.DataRepoes
                                      where iaccess.Contains(rx.STOREID)
                                      select Mapper.Map<Classes.DataResponse>(rx)).
                                      ToList<Classes.DataResponse>();
    

    希望有帮助!!

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多