通过将EF实体与LINQ中的另一个对象连接来创建DTO

时间:2015-10-15 07:02:55

标签: c# entity-framework linq

我有一个实体框架实体

public class Entiy 
{
    public string EntityProperty1 { get; set; }
    public string EntityProperty2 { get; set; }
    public string EntityProperty3 { get; set; }
    public Guid? SomeId { get; set; }
}

还有一个OtherObject和一个DTO,它包含的属性与其他类相同。

public class OtherObject
{
    public string OtherObjectProperty1 { get; set; }
    public string OtherObjectProperty2 { get; set; }
    public string OtherObjectProperty3 { get; set; }
    public Guid SomeId { get; set; }
}

public class DTO
{
    public string EntityProperty1 { get; set; }
    public string EntityProperty2 { get; set; }
    public string EntityProperty3 { get; set; }
    public string OtherObjectProperty1 { get; set; }
    public string OtherObjectProperty2 { get; set; }
    public string OtherObjectProperty3 { get; set; }
}

在我的服务类中,我获得了我的实体的IQueryable,并且我发送了一个包含可枚举的OtherObject的参数。当我只想使用实体属性创建DTO时,它工作正常。

public IEnumerable<DTO> GetDtos(IEnumerable<OtherObject> otherObjects) 
{
    return _someRepository.GetAll()
        .Where(a => otherObjects.Select(b => b.SomeId).Contains(a.SomeId))
        .Select(d => new DTO()
            {
                EntityProperty1 = d.EntityProperty1
                EntityProperty2 = d.EntityProperty2 
                EntityProperty3 = d.EntityProperty3
            })
        .ToList();
}

但是,我还想加入我的OtherObject类的一些属性。如果我做以下

public IEnumerable<DTO> GetDtos(IEnumerable<OtherObject> otherObjects) 
{
    return _someRepository.GetAll()
        .Where(a => otherObjects.Select(b => b.SomeId).Contains(a.SomeId))
        .Select(d => new DTO()
            {
                EntityProperty1 = d.EntityProperty1
                EntityProperty2 = d.EntityProperty2 
                EntityProperty3 = d.EntityProperty3
                OtherObjectEntity1 = otherObjects.FirstOrDefault(a => a.SomeId == d.SomeId).OtherObjectEntity1
            })
        .ToList();
} 

我收到以下错误:&#34;无法创建XXX类型的常量值。在此上下文中仅支持原始类型或枚举类型&#34;

3 个答案:

答案 0 :(得分:0)

您是否尝试使用Join而不是Where? 它看起来像这样:

_someRepository.GetAll()
        .Join(otherObjects, a => a.SomeId, b => b.SomeId, (d, e) => new DTO
            {
                EntityProperty1 = d.EntityProperty1
                EntityProperty2 = d.EntityProperty2 
                EntityProperty3 = d.EntityProperty3
                OtherObjectEntity1 = e.OtherObjectEntity1
            })
        .ToList();

在我看来,它比你的实施更有效率。

修改

感谢您的评论,我使用数据库检查了查询,但它无效。 我决定修改查询,如果你想使用Join:)

        var ids = otherObjects.Select(i => i.Id);

        // When
        var results = repository.GetAll()
            .Where(a => ids.Contains(a.Id))
            .ToList()
            .Join(otherObjects, a => a.Id, b => b.Id, (a, b) => new SimpleDto
            {
                Id = a.Id,
                Name = a.Name,
                Attribute = b.Attribute
            })
            .ToList();

答案 1 :(得分:0)

最简单的方法是在Where子句之后做一个ToList,然后再进行DTO投影。做一个早期的ToList会过度选择存储库的所有列,这对性能不利。

另一个选项,首先实现前三列..

var result = 
    someRepository.GetAll()
        .Where(a => otherObjects.Select(b => b.SomeId).Contains(a.SomeId))
        .Select(d => new DTO()
            {
                EntityProperty1 = d.EntityProperty1
                EntityProperty2 = d.EntityProperty2 
                EntityProperty3 = d.EntityProperty3
            }).
 .ToList()

..然后重新投影DTO:

 result = result(d => 
          new DTO() {
              EntityProperty1 = d.EntityProperty1,
              EntityProperty2 = d.EntityProperty2,
              EntityProperty3 = d.EntityProperty3,
              OtherObjectEntity1 = otherObjects.FirstOrDefault(a => a.SomeId == e.SomeId).OtherObjectEntity1
          }

另一种方法是在IQueryable上使用IQueryable。这将在SELECT语句中生成子查询。

public IEnumerable<DTO> GetDtos(IQueryable<OtherObject> otherObjects) 
{
    return _someRepository.GetAll()
        .Where(a => otherObjects.Select(b => b.SomeId).Contains(a.SomeId))
        .Select(d => new DTO()
            {
                EntityProperty1 = d.EntityProperty1
                EntityProperty2 = d.EntityProperty2 
                EntityProperty3 = d.EntityProperty3,
                OtherObjectEntity1 = otherObjects.FirstOrDefault(a => a.SomeId == e.SomeId).OtherObjectEntity1
            })
        .ToList();
}

或者使用join(Milosz Wieczorek&#39; s answer),它比子查询更有效。

答案 2 :(得分:0)

您可以通过向第一个查询添加另一个选择来添加其他对象的属性

public IEnumerable<DTO> GetDtos(IQueryable<OtherObject> otherObjects) 
{
   var ids = otherObjects.Select(x => x.SomeId);
   return _someRepository.GetAll()
          .Where(x => ids.Contains(x.SomeId)
          .ToList()
          .Select(x =>
                 new DTO()
                 {
                    EntityProperty1 = x.EntityProperty1,
                    EntityProperty2 = x.EntityProperty2, 
                    EntityProperty3 = x.EntityProperty3,
                    OtherObjectEntity1 = otherObjects.FirstOrDefault(p => p.SomeId == x.SomeId).OtherObjectEntity1
                 };
}

我也提取了其他对象&#39; ids到局部变量以优化查询一点点。