Base solution for your next web application
Open Closed

Mapping sub list with select #621


User avatar
0
sayram created

I'm trying to build Category part for my application back-end. In Index action ill list Categories and its sub categories.

Here is my Category entity

public class Category : Entity<int>
    {
        public virtual int? ParentId { get; set; }
        public virtual string Title { get; set; }
        public virtual string Description { get; set; }
        public virtual string MetaDescription { get; set; }
        public virtual string MetaKeys { get; set; }
        public virtual int Order { get; set; }
        public virtual Category ParentCategory { get; set; }
        public virtual ICollection<Category> SubCategories { get; set; }
        public virtual ContentState ContentState { get; set; }
    }

    public enum ContentState
    {
        Published = 1,
        UnPublished = 0,
        Featured = 2,
        Trashed = -1
    }

I created Dtos like below

public class GetAllCategorySummariesInput : IInputDto
    {
        public ContentState? ContentState { get; set; }
    }

    public class GetAllCategorySummariesOutput : IOutputDto
    {
        public List<CategorySummaryDto> AllCategorySummaries { get; set; }
    }

    [AutoMap(typeof(Category))]
    public class CategorySummaryDto : EntityDto
    {
        public string Title { get; set; }
        public int Order { get; set; }
        public List<CategorySummaryDto> SubCategories { get; set; }
    }

in service class im using this code

public GetAllCategorySummariesOutput GetAllCategorySummaries(GetAllCategorySummariesInput input)
        {

            var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).OrderBy(x => x.Id).ToList();

            return new GetAllCategorySummariesOutput
            {
                AllCategorySummaries = catList.MapTo<List<CategorySummaryDto>>()
            };
        }

This one is seems works fine. But, i only want Id, Title, Order and SubCategories

so i tried something like this:

var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new { x.Id, x.Title, x.SubCategories, x.Order }).OrderBy(x => x.Id).ToList();

But im getting "Missing type map configuration or unsupported mapping." error at runtime detailed below

Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\n<>f__AnonymousType04 -> CategorySummaryDto\r\n<>f__AnonymousType04[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Collections.Generic.ICollection1[[YouScene.Categories.Category, YouScene.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] -> YouScene.Categories.Dtos.CategorySummaryDto\r\n\r\nDestination path:\r\nList1[0]\r\n\r\nSource value:\r\n{ Id = 2, Title = Michael Fassbender, SubCategories = System.Collections.Generic.HashSet`1[YouScene.Categories.Category], Order = 0 }"}

Well, i said "if door is not open for me, ill look for another one. "

Here.

var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new CategorySummaryDto { Id = x.Id, Title =  x.Title, SubCategories = x.SubCategories, Order = x.Order }).OrderBy(x => x.Id).ToList();

when i change the code like above 'SubCategories = x.SubCategories' shows error

Cannot implicitly convert type 'System.Collections.Generic.ICollection<YouScene.Categories.Category>' to 'System.Collections.Generic.List<YouScene.Categories.Dtos.CategorySummaryDto>'. An explicit conversion exists (are you missing a cast?)

She is right. Category is not CategorySummaryDto.

So i changed the code like below followed by a final hope

var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new CategorySummaryDto { Id = x.Id, Title =  x.Title, SubCategories = x.SubCategories.MapTo&lt;List&lt;CategorySummaryDto&gt;>(), Order = x.Order }).OrderBy(x => x.Id).ToList();

in design time everything was seems good.

But when i run the code she throwed an error again.

Additional information: LINQ to Entities does not recognize the method 'System.Collections.Generic.List1[YouScene.Categories.Dtos.CategorySummaryDto] MapTo[List1](System.Object)' method, and this method cannot be translated into a store expression.


3 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    This is completely out of scope of ABP, and related to automapper and LINQ, but I will try to help you if I can.

    1. See this code:
    var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new { x.Id, x.Title, x.SubCategories, x.Order }).OrderBy(x => x.Id).ToList();
    

    catList is a List of an anonymous type! Since anonymous types are dynamic, you can not define a mapping (like [AutoMap(typeof(Category))]). So, you can use AutoMapper's dynamic mapping. It should be something like that, but search on web:

    return new GetAllCategorySummariesOutput
                {
                    AllCategorySummaries = Mapper.DynamicMap<List<CategorySummaryDto>>(catList)
                };
    
    1. Alternatively, you tried:
    var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new CategorySummaryDto { Id = x.Id, Title =  x.Title, SubCategories = x.SubCategories.MapTo<List<CategorySummaryDto>>(), Order = x.Order }).OrderBy(x => x.Id).ToList();
    

    This can not work since LINQ does not supports it. I don't know why :)

    Eventually, your working case is good for me. But if you want, you can use DynamicMap of AutoMapper.

  • User Avatar
    0
    sayram created

    <cite>hikalkan: </cite>

    1. See this code:
    var catList = _categoryRepository.GetAll().Where(x => x.ContentState == input.ContentState).Select(x => new { x.Id, x.Title, x.SubCategories, x.Order }).OrderBy(x => x.Id).ToList();
    

    catList is a List of an anonymous type! Since anonymous types are dynamic, you can not define a mapping (like [AutoMap(typeof(Category))]). So, you can use AutoMapper's dynamic mapping. It should be something like that, but search on web:

    return new GetAllCategorySummariesOutput
               {
                   AllCategorySummaries = Mapper.DynamicMap<List<CategorySummaryDto>>(catList)
               };
    

    That dynamic things worked fine. But!

    Sql Server Profiler reported that query when _categoryRepository run.

    SELECT 
        [Project1].[Id] AS [Id], 
        [Project1].[Title] AS [Title], 
        [Project1].[Order] AS [Order], 
        [Project1].[ContentState] AS [ContentState], 
        [Project1].[C1] AS [C1], 
        [Project1].[Id1] AS [Id1], 
        [Project1].[ParentId] AS [ParentId], 
        [Project1].[Title1] AS [Title1], 
        [Project1].[Description] AS [Description], 
        [Project1].[MetaDescription] AS [MetaDescription], 
        [Project1].[MetaKeys] AS [MetaKeys], 
        [Project1].[Order1] AS [Order1], 
        [Project1].[ContentState1] AS [ContentState1]
        FROM ( SELECT 
            [Extent1].[Id] AS [Id], 
            [Extent1].[Title] AS [Title], 
            [Extent1].[Order] AS [Order], 
            [Extent1].[ContentState] AS [ContentState], 
            [Extent2].[Id] AS [Id1], 
            [Extent2].[ParentId] AS [ParentId], 
            [Extent2].[Title] AS [Title1], 
            [Extent2].[Description] AS [Description], 
            [Extent2].[MetaDescription] AS [MetaDescription], 
            [Extent2].[MetaKeys] AS [MetaKeys], 
            [Extent2].[Order] AS [Order1], 
            [Extent2].[ContentState] AS [ContentState1], 
            CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
            FROM  [dbo].[Category] AS [Extent1]
            LEFT OUTER JOIN [dbo].[Category] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ParentId]
        )  AS [Project1]
        ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
    
    exec sp_executesql N'SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[ParentId] AS [ParentId], 
        [Extent1].[Title] AS [Title], 
        [Extent1].[Description] AS [Description], 
        [Extent1].[MetaDescription] AS [MetaDescription], 
        [Extent1].[MetaKeys] AS [MetaKeys], 
        [Extent1].[Order] AS [Order], 
        [Extent1].[ContentState] AS [ContentState]
        FROM [dbo].[Category] AS [Extent1]
        WHERE [Extent1].[ParentId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=9
    

    As you can see all fields selected. I put breakpoint after the _categoryRepository, only selected fields returned, not all of them. I need some research about this i gues.

    Thank you this tip.

    <cite>hikalkan: </cite>

    Eventually, your working case is good for me.

    I need more samples. About ViewModels, Dtos.

    İddiayı tutturayım alıcam zeroyu görmem lazım senin kodları. :) Teşekkürler yardımların için.

  • User Avatar
    0
    hikalkan created
    Support Team

    :D