Linq Lamda表达式从实体类及其导航属性中选择多个列

时间:2018-03-13 18:05:35

标签: c# entity-framework linq linq-to-entities entity-framework-5

我正在使用存储库模式并拥有以下实体类和DTO类。在我的服务中,我实现了一个接口来获取所有类别(类型)GetAlbumsInAllCategories中的专辑(专辑名称)。我需要通过这些列分组只返回这些字段-abbumcategory.type,song.album,song.albumcover,在函数GetAlbumsInAllCategories中的linq lamda中。下面的GetAlbumsInAllCategories中的linq lamda表达式在下面一行的Select关键字中给出了此错误。

return albums.ToList().Select(Mapper.Map<AlbumsByCategory, AlbumsByCategoryDTO>);

Error at select keyword

错误是因为我从存储库类AlbumRepository和导航属性歌曲中只选择了几列。而且我不想为少数选定的列创建一个新的DTO类,有没有办法在没有为少数选定的列创建新的DTO但使用现有的AlbumsByCategory和导航歌曲的情况下执行此操作?

T-SQL翻译

            select  
        ab.[type],s.Album,s.[AlbumCover]
        from
        [dbo].[AlbumsByCategory] ab
        join [dbo].[Songs] s on s.Id=ab.SongId
        where ab.Archived=0 and ab.ShowByAlbums=1
        group by 
        ab.[type],s.Album,s.[AlbumCover]

结果 Result of SQL query above

AlbumsByCategory

         public partial class AlbumsByCategory
        {
            public int Album_Song_Id { get; set; }
            public string Type { get; set; }
            public int SongId { get; set; }
            public bool ShowByAlbums { get; set; }
            public Nullable<System.DateTimeOffset> FromDate { get; set; }
            public Nullable<System.DateTimeOffset> ToDate { get; set; }
            public bool Archived { get; set; }
            public virtual Song Song { get; set; }
        }

歌曲

            public partial class Song
            {
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
                public Song()
                {
                    this.Favorites = new HashSet<Favorite>();
                    this.HitMiscSongs = new HashSet<HitMiscSong>();
                    this.PlayListSongs = new HashSet<PlayListSong>();
                    this.AlbumsByCategories = new HashSet<AlbumsByCategory>();
                }

                public int Id { get; set; }
                public string Title { get; set; }
                public string Artist { get; set; }
                public string Genre { get; set; }
                public string AlbumCover { get; set; }
                public string Album { get; set; }
                public string ContentType { get; set; }
                public string FilePath { get; set; }

                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
                public virtual ICollection<Favorite> Favorites { get; set; }
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
                public virtual ICollection<HitMiscSong> HitMiscSongs { get; set; }
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
                public virtual ICollection<PlayListSong> PlayListSongs { get; set; }
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
                public virtual ICollection<AlbumsByCategory> AlbumsByCategories { get; set; }
            }

DTO签约

 public class AlbumsByCategoryDTO
{
    public int Album_Song_Id { get; set; }
    public string Type { get; set; }
    public int SongId { get; set; }
    public bool ShowByAlbums { get; set; }
    public Nullable<System.DateTimeOffset> FromDate { get; set; }
    public Nullable<System.DateTimeOffset> ToDate { get; set; }
    public bool Archived { get; set; }
    public virtual SongDTO Song { get; set; }
}
public class SongEntity
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Artist { get; set; }
    public string Genre { get; set; }
    public string AlbumCover { get; set; }
    public string Album { get; set; }
    public string ContentType { get; set; }
    public string FilePath { get; set; }
    public virtual ICollection<FavoriteEntity> Favorites { get; set; }
    public virtual ICollection<PlayListSongEntity> PlayListSongs { get; set; }

}

GetAlbumsInAllCategories中上述TSQL的Linq Lamda

            public class AlbumServices : IAlbumServices
            {
                private readonly UnitOfWork _unitOfWork;
                /// <summary>
                /// Public constructor.
                /// </summary>
                public AlbumServices(UnitOfWork unitOfWork)
                {
                    _unitOfWork = unitOfWork;

                }

                public IEnumerable<AlbumsByCategoryEntity> GetAlbumsInAllCategories()
                {
                    var albums = _unitOfWork.AlbumRepository.GetAll()
                        .Where(y => y.Archived == false && y.ShowByAlbums == true)
                        .GroupBy(y => new { y.Type, y.Song.Album, y.Song.AlbumCover })
                        .ToList()
                        .SelectMany(x => x.Select(y => new { y.Type, y.Song.Album, y.Song.AlbumCover }));

                    if (albums.Any())
                    {

                            return albums.ToList().Select(Mapper.Map<AlbumsByCategory, AlbumsByCategoryDTO>);

                    }
                    return Enumerable.Empty<AlbumsByCategoryDTO>();

                }
            }

2 个答案:

答案 0 :(得分:0)

它对你要做的事情有点困惑,但看起来你的选择缺少函数运算符(s =&gt;),而且可能是new关键字来创建返回对象。

Select(s => new Mapper.Map<AlbumsByCategory, AlbumsByCategoryDTO>)

其次,我从不使用使用DTO工具时创建的映射器对象。我为所有DTO对象创建了一个基类,并有一个构造函数,它接受一个实体并为我复制一切。我还有一个功能,回到DTO所以它是一条双向的街道。它只会复制存在的属性,因此你可以拥有一个部分对象,如果某些属性不存在,它就不会失败。

public class DTOBase
{
    public DTOBase() { }
    public DTOBase(Object Entity)
    {
        if (Entity == null) return;
        Type tObjFrom = Entity.GetType();
        Type tObjTo = this.GetType();

        var listPropObj1 = tObjFrom.GetProperties().Where(p => p.GetValue(Entity) != null).ToList();

        foreach (var item in listPropObj1)
        {
            if (tObjTo.GetProperty(item.Name) != null)
            {
                tObjTo.GetProperty(item.Name).SetValue(this, item.GetValue(Entity));
            }
        }
    }
    public void MapToEntity(object Entity)
    {
        if (Entity == null) return;
        Type tObjTo = Entity.GetType();
        Type tObjFrom = this.GetType();

        var listPropObj1 = tObjFrom.GetProperties().ToList();

        foreach (var item in listPropObj1)
        {
            if (tObjTo.GetProperty(item.Name) != null)
            {
                //if (item.GetValue(this) != null)
                tObjTo.GetProperty(item.Name).SetValue(Entity, item.GetValue(this));
            }
        }
    }
}

所以当我从数据库运行我的Linq时,我只是动态创建DTO。

.ToList()    。选择(s =&gt;新员工DTO(s))

如果您想从DTO回到实体,那么就这样做......

EmployeeDTO.MapToEntity(Employee)

答案 1 :(得分:0)

听起来您正在尝试从初始查询中选择一些字段并仅返回其中一些字段。如果你不想创建一个特殊的类(我们称之为这些模型),那么你就会陷入匿名类型。如果您计划将结果传递给其他函数或进一步查询它,则很难使用。这是一个linq查询的示例,其中特定字段从多个表中选择为匿名类型。

var seg = context.TaskSegments
                        .Where(s => s.SegmentID == ID)
                        .Select(s => new
                        {
                            SegmentID = s.SegmentID,
                            TaskID = s.TaskID,
                            TaskType = s.Task.Type,
                            DisplayAs = s.Task.DisplayAs,
                            Value = s.Value,
                            CompletedOn = s.CompletedOn,
                            ModifiedBy = s.ModifiedBy,
                            ModifiedOn = s.ModifiedOn,
                            RequiresCompleteDate = s.Task.RequiresCompleteDate,
                            AllowAttachments = s.Task.AllowAttachments,
                            UseYesNoCompletion = s.Task.UseYesNoCompletion,
                            DisplayXWhenNotCompleted = s.Task.DisplayXWhenNotCompleted,
                            Responsible = s.Task.Responsible,
                            Secondary = s.Task.ResponsibleSecondary,
                            Confirmation = s.Task.Confirmation
                        })
                        .FirstOrDefault();