简体   繁体   English

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

[英]Entity Framework Data Transfer Objects Best Practice

We have to use data transfer objects for many of our tables as they are very big and many columns aren't useful for the context I'm working. 我们必须为我们的许多表使用数据传输对象,因为它们非常大,并且许多列对我正在使用的上下文没用。

To get the best performance I cannot read the complete database entities and covert it to dtos afterwards. 为了获得最佳性能,我无法读取完整的数据库实体,并在之后将其转换为dtos。 Therefore I created a linq extension method to convert it to dtos before executing the query. 因此,我创建了一个linq扩展方法,在执行查询之前将其转换为dtos。

Call of extension method: 延期召唤方法:

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

My extension method: 我的扩展方法:

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

Is this a feasable solution or are there better practises to do that? 这是一个可行的解决方案还是有更好的做法?

Second question: There aren't only IQueryable< MyTable > objects which needs to be transformed to dtos, also MyTable objects have to be transformed. 第二个问题:不仅需要将IQueryable <MyTable>对象转换为dtos,还必须转换MyTable对象。 I created an extension method for MyTable class: 我为MyTable类创建了一个扩展方法:

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

Why can't I use this function in my first ToDto function? 为什么我不能在我的第一个ToDto功能中使用此功能? Like: 喜欢:

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

UPDATE UPDATE

A further question because of the research below. 由于下面的研究,还有一个问题。 There are also cases where we want to return only a minimum of fields for high performance issues. 在某些情况下,我们希望仅为高性能问题返回最少的字段。

It is possible to create a repository class where you can define a parameter to pass a Func with the fields which should be returned by the query (as described below). 可以创建一个存储库类,您可以在其中定义一个参数,以传递一个Func,其中包含应由查询返回的字段(如下所述)。 Then it is possible to create a class (MyServiceClass in the example below) where you can call the same repository method with for different return entities. 然后可以创建一个类(下面示例中的MyServiceClass),您可以在其中为不同的返回实体调用相同的存储库方法。 But is that a good practise or what would be a better solution for it? 但这是一个好的做法还是什么是更好的解决方案呢?

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,
        ...
    };
}

Because your Linq to Entities provider doesn't know how to translate your method call into a SQL statement. 因为您的Linq to Entities提供程序不知道如何将方法调用转换为SQL语句。 As a solution to your issue, you can use a lambda expression instead of an extension method: 作为您的问题的解决方案,您可以使用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);
}

Or as suggested @Timothy in his comment you can also use Automapper . 或者正如@Timothy在评论中所建议的那样,你也可以使用Automapper Once you have mapped your entity class with its DTO, you can do something like this: 使用DTO映射实体类后,可以执行以下操作:

using AutoMapper.QueryableExtensions;

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

You can find more info in this page . 您可以在此页面中找到更多信息。

Update 更新

Well for my fist solution maybe you can create a generic extension method: 对于我的拳头解决方案,也许您可​​以创建一个通用的扩展方法:

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

About the second one, which IMHO I'm still thinking is better for you, you can configure your mapping: 关于第二个,我仍然认为恕我直言,你可以配置你的映射:

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

You can find an great article about this subject in this link . 您可以在此链接中找到关于此主题的精彩文章。

I'd also add that if you intend to only use these as DTO, then you should be using the .AsNoTracking() before you enumerate from the SQL source. 我还要补充一点,如果你打算只将它们用作DTO,那么你应该在从SQL源枚举之前使用.AsNoTracking()

Returns a new query where the entities returned will not be cached in the DbContext or ObjectContext. 返回一个新查询,其中返回的实体不会缓存在DbContext或ObjectContext中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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