简体   繁体   中英

c# How can I create a collection of custom objects without going through each field

I have created a class called DataResponse that has more than 40 public fields. The class DataResponse has the same number of fields and type as what's in my database DataRepoes (let's assume that).

Is there a way at all to do something like linq below where I create list of objects and automatically the fields are assigned to DataResponse from what's in the DB? Otherwise I have to spell out each and every those 40 fields and assign them manually when I new up DataResponse class. Thanks

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

You can use Automapper Queryable Extensions for that.

Assuming the fields in Classes.DataResponse have the same names as the ones in DataRepoes you can then, do:

// 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();                                    

This is not possible using LINQ. However you can use " AutoMapper " to acheive this. Just CreateMap for these two class and then Map them

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>();

Hope that helps!!

If you don't need the flexibility provided by AutoMapper or don't want using a third party library, you can use the following simplified custom extension method:

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))));
    }
}

It will try to select all the properties/fields that match by name. Will fail if matched property/field types differ.

Sample usage:

Method syntax

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

Query syntax

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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