实体框架数据传输对象最佳实践

时间:2015-11-19 14:07:46

标签: c# entity-framework linq dto

我们必须为我们的许多表使用数据传输对象,因为它们非常大,而且许多列对我正在使用的上下文没用。

为了获得最佳性能,我无法阅读完整的数据库实体,然后将其转换为dtos。因此,我创建了一个linq扩展方法,在执行查询之前将其转换为dtos。

调用扩展方法:

db.MyTable.Select(...).ToDto().ToList();

我的扩展方法:

public static IQueryable<MyTableDTO> ToDto(this IQueryable<MyTable> query)
{
     return query.Select(x => new MyTableDTO
     {
         ID = x.ID,
         Name = x.Name
     });
}

这是一个可行的解决方案还是有更好的做法呢?

第二个问题:不仅有IQueryable&lt; MyTable&gt;需要转换为dtos的对象,还必须转换MyTable对象。我为MyTable类创建了一个扩展方法:

public static MyTableDto ToDto (this MyTable x)
{
    return new MyTableDto
    {
        ID = x.ID,
        Name = x.Name
    };
}

为什么我不能在我的第一个ToDto功能中使用此功能? 像:

public static IQueryable<MyTableDTO> ToDto(this IQueryable<MyTable> query)
{
    return query.Select(x => x.ToDto());
}

更新

由于下面的研究,还有一个问题。在某些情况下,我们希望仅为高性能问题返回最少的字段。

可以创建一个存储库类,您可以在其中定义一个参数,以传递一个Func,其中包含应由查询返回的字段(如下所述)。然后可以创建一个类(下面示例中的MyServiceClass),您可以在其中为不同的返回实体调用相同的存储库方法。但这是一个好的做法,还是一个更好的解决方案呢?

public class MyTableRepository<T>
{
    public List<T> GetMyTable(String search1, String search2, Func<MyTable, T> selectExp)
    {
        using(var db = new Context())
        {
            return db.MyTable.Where(x => x.A == search1 ...).Select(selectExp).ToList();
        }
    }
}

public class MyServiceClass
{
    public List<MyTableEntitySimple> GetMyTableEntitySimple(String  search1...)
    {
        MyTableRepository<MyTableEntitySimple> rep = new ...
        return rep.GetMyTable(search1, ToMyTableEntitySimple);
    }

    public List<MyTableEntity> GetMyTableEntity(String search1...)
    {
        MyTableRepository<MyTableEntity> rep = new ...
        return rep.GetMyTable(search1, ToMyTableEntity);
    }

    Func<MyTable, MyTableEntitySimple) ToMyTableEntitySimple = x => new MyTableEntitySimple
    {
        ID = x.ID,
        Name = x.Name
    };

    Func<MyTable, MyTableEntity) ToMyTableEntity = x => new MyTableEntitySimple
    {
        ID = x.ID,
        Name = x.Name,
        Field3 = x.Field3,
        Field4 = x.Field4,
        ...
    };
}

2 个答案:

答案 0 :(得分:6)

因为您的Linq to Entities提供程序不知道如何将方法调用转换为SQL语句。作为您的问题的解决方案,您可以使用lambda表达式而不是扩展方法:

Func<MyTable, MyTableDTO> selectExp=x => new MyTableDTO{
                                                         ID = x.ID,
                                                         Name = x.Name
                                                        });

//Pass the lambda expression as a paremter
public static IQueryable<MyTableDTO> ToDto(this IQueryable<MyTable> query, Func<MyTable, MyTableDTO> selectExpr)
{
    return query.Select(selectExpr);
}

或者正如@Timothy在评论中所建议的那样,你也可以使用Automapper。一旦将实体类与其DTO映射,就可以执行以下操作:

using AutoMapper.QueryableExtensions;

public static IQueryable<MyTableDTO> ToDto(this IQueryable<MyTable> query)
{
    return query.ProjectTo<MyTableDTO>();
}

您可以在此page找到更多信息。

更新

对于我的拳头解决方案,也许你可以创建一个通用的扩展方法:

 public static IQueryable<T> ToDto<TSource,T>(this IQueryable<TSource> query, Func<TSource, T> selectExpr)
 {
    return query.Select(selectExpr);
 }

关于第二个,我仍然认为恕我直言的更好,你可以配置你的映射:

// Configure AutoMapper
Mapper.CreateMap<MyTable, MyTableDTO>()
    .ForMember(dest => dest.YourNewName1, opt => opt.MapFrom(src => src.YourGermanName1))
    .ForMember(dest => dest.YourNewName2, opt => opt.MapFrom(src => src.YourGermanName2));

您可以在此link找到关于此主题的精彩文章。

答案 1 :(得分:0)

我还要补充一点,如果您打算只将它们用作DTO,那么在从SQL源枚举之前,您应该使用.AsNoTracking()

  

返回一个新查询,其中返回的实体不会缓存在DbContext或ObjectContext中。