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

C# 实体框架数据传输对象最佳实践,c#,entity-framework,linq,dto,C#,Entity Framework,Linq,Dto,我们必须对许多表使用数据传输对象,因为它们非常大,而且许多列对于我正在处理的上下文没有用处 为了获得最佳性能,我无法读取完整的数据库实体,然后将其转换为DTO。因此,我创建了一个linq扩展方法,在执行查询之前将其转换为DTO 扩展方法调用: Func<MyTable, MyTableDTO> selectExp=x => new MyTableDTO{ ID =

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

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

扩展方法调用:

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);
}
 public static IQueryable<T> ToDto<TSource,T>(this IQueryable<TSource> query, Func<TSource, T> selectExpr)
 {
    return query.Select(selectExpr);
 }
db.MyTable.Select(…).ToDto().ToList();
我的扩展方法:

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);
}
 public static IQueryable<T> ToDto<TSource,T>(this IQueryable<TSource> query, Func<TSource, T> selectExpr)
 {
    return query.Select(selectExpr);
 }
公共静态IQueryable ToDto(此IQueryable查询)
{
返回查询。选择(x=>newmytabledto
{
ID=x.ID,
Name=x.Name
});
}
这是一个可行的解决方案,还是有更好的做法

第二个问题:不仅需要将IQueryable对象转换为DTO,还必须转换MyTable对象。我为MyTable类创建了一个扩展方法:

公共静态MyTabledToTodTo(此MyTable x)
{
返回新的MyTableDto
{
ID=x.ID,
Name=x.Name
};
}
为什么我不能在我的第一个ToDto函数中使用此函数? 比如:

公共静态IQueryable ToDto(此IQueryable查询)
{
返回query.Select(x=>x.ToDto());
}
更新 由于下面的研究,还有一个问题。也有一些情况下,我们只想返回高性能问题的最少字段

可以创建一个repository类,在该类中可以定义一个参数来传递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,
        ...
    };
}
公共类MyTableRepository
{
公共列表GetMyTable(字符串搜索1、字符串搜索2、Func selectExp)
{
使用(var db=new Context())
{
返回db.MyTable.Where(x=>x.A==search1…).Select(selectExp.ToList();
}
}
}
公共类MyServiceClass
{
公共列表GetMyTableEntitySimple(字符串搜索1…)
{
MyTableRepository=新建。。。
返回rep.GetMyTable(search1,ToMyTableEntitySimple);
}
公共列表GetMyTableEntity(字符串搜索1…)
{
MyTableRepository=新建。。。
返回rep.GetMyTable(search1,ToMyTableEntity);
}
Func新MyTableEntitySimple
{
ID=x.ID,
Name=x.Name
};
Func新MyTableEntitySimple
{
ID=x.ID,
Name=x.Name,
字段3=x.Field3,
字段4=x.Field4,
...
};
}

因为您的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);
}
 public static IQueryable<T> ToDto<TSource,T>(this IQueryable<TSource> query, Func<TSource, T> selectExpr)
 {
    return query.Select(selectExpr);
 }
Func selectExp=x=>new MyTableDTO{
ID=x.ID,
Name=x.Name
});
//将lambda表达式作为参数传递
公共静态IQueryable ToDto(此IQueryable查询,Func selectExpr)
{
返回查询。选择(selectExpr);
}
或者正如@Timothy在他的评论中建议的那样,你也可以使用。将实体类与其DTO映射后,可以执行以下操作:

using AutoMapper.QueryableExtensions;

public static IQueryable<MyTableDTO> ToDto(this IQueryable<MyTable> query)
{
    return query.ProjectTo<MyTableDTO>();
}
使用AutoMapper.QueryableExtensions;
公共静态IQueryable ToDto(此IQueryable查询)
{
返回query.ProjectTo();
}
你可以在这里找到更多信息

更新 对于我的第一个解决方案,您可以创建一个通用扩展方法:

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);
}
 public static IQueryable<T> ToDto<TSource,T>(this IQueryable<TSource> query, Func<TSource, T> selectExpr)
 {
    return query.Select(selectExpr);
 }
public static IQueryable ToDto(此IQueryable查询,Func selectExpr)
{
返回查询。选择(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));
//配置自动映射器
Mapper.CreateMap()
.FormMember(dest=>dest.YourNewName1,opt=>opt.MapFrom(src=>src.YourGermanName1))
.ForMember(dest=>dest.YourNewName2,opt=>opt.MapFrom(src=>src.YourGermanName2));

你可以在这篇文章中找到一篇关于这个主题的好文章。

我还想补充一点,如果你只想将它们用作DTO,那么你应该在从SQL源枚举之前使用

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


1.这是可行的,有很多可用的库,您可以利用它们来避免手动映射属性。看看这两个。不太清楚为什么不能,您看到了什么错误?您可以避免所有这些,只需使用。选择(x=>new MyTableDto{ID=x.ID,Name=x.Name}),直到您使用类似于ToList()的东西执行查询,您仍然只处理查询并对这些字段进行选择,而不是对所有字段进行选择。蒂莫西:谢谢,请参阅下面的答案。。。Stephen:问题是,我不想在每个返回实体的方法中都这样做。我希望每个实体只进行一次转换。谢谢,这非常有效。(我必须向query.Select(selectExpr)添加一个AsQueryable()。是否可以为我的所有实体创建一个通用ToDto?(与lamda表达式一样,映射已经定义)我看过Automapper。问题是我们的数据库列大多使用德语名称,我不想在DTO中使用德语字段名称。因此,我仍然需要单独映射每个字段。谢谢,我将进一步了解Automapper,以及我们如何以最佳方式使用它。我在我的问题中添加了一个更新,因为它仍然适合我和他需要为我们的问题找到最好的解决方案。这非常有用。我做到了,但我的团队问我:如果我的实体有两个大的varchar字段