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

Posted

技术标签:

【中文标题】用于从实体类及其导航属性中选择多个列的 Linq Lambda 表达式【英文标题】:Linq Lamda expression to select multiple columns from entity class and its navigation property 【发布时间】:2018-08-22 02:39:30 【问题描述】:

我正在使用存储库模式并具有以下实体类和 DTO 类。在我的服务中,我正在实现一个接口来获取所有类别(类型)GetAlbumsInAllCategories 中的专辑(专辑名称)。我只需要在函数GetAlbumsInAllCategories 中的linq lamda 中按这些列分组返回这些字段-abbumcategory.type、song.album、song.albumcover。下面 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

专辑分类

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

                
            

【问题讨论】:

【参考方案1】:

听起来您正试图从初始查询中选择几个字段,然后只返回其中的一些。如果你不想创建一个特殊的类(我们称之为模型),那么你就会被匿名类型困住。如果您计划将结果传递给其他函数或进一步查询它,这很难使用。这是一个 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();

【讨论】:

【参考方案2】:

您尝试执行的操作有点令人困惑,但您的选择似乎缺少函数运算符 (s =>),并且可能缺少用于创建返回对象的新关键字。

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

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

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() .Select(s => new EmployeeDTO(s))

如果您想从 DTO 回到实体,请执行此操作...

EmployeeDTO.MapToEntity(Employee)

【讨论】:

我得到的错误很简单,没有与我在下面的代码中选择的少数字段匹配的类。不想为查询返回的少数字段创建新类。有没有使用现有类只返回几列的方法? 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 )); 我发布了第二个答案,希望能解决您的问题。

以上是关于用于从实体类及其导航属性中选择多个列的 Linq Lambda 表达式的主要内容,如果未能解决你的问题,请参考以下文章

选择特定实体 LINQ 的属性

sql 用于选择Magento实体EAV属性及其值的一系列查询。这里有很多联盟......

在LINQ to Entities中不支持指定的类型成员'x'。只支持初始化器实体成员和实体导航属性。

Linq 到实体 Skip() 和 Take()

指定类型的成员XX”不支持实体LINQ。只有初始化,成员单位,和实体导航性能的支持。

如何使用实体框架的导航属性来构建视图模型