Problems initializing a type inside a query with LINQ

5

I'm going through the following problem, I am putting an API into a forum application and this API needs an endpoint to return a collection of Forum that have the% property of% null. Then I put the following endpoint :

[AllowAnonymous]
[HttpGet]
[Route("toplevel")]
public async Task<IHttpActionResult> GetAllTopLevel(int page = 1, int pageSize = 15)
{
    var data = Db.Forums
        .Where(f => !f.ParentId.HasValue)
        .OrderByDescending(f => f.Name)
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .Select(f => new ForumDTO
        {
            Id = f.Id,
            Name = f.Name,
            SubForums = f.SubForums.Select(sf => new ForumDTO
            {
                Id = sf.Id,
                Name = sf.Name
            }).ToList()
        }).ToList();

    return Ok(data);
}

But I'm having problems with ParentId of this LINQ, which is causing a Select with the following error message:

  

The type 'ZigForum.Models.DTOs.ForumDTO' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.

Class NotSupportedException :

public class Forum
{
    public Forum()
    {
        SubForums = new List<Forum>();
        Posts = new List<Post>();
    }

    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }

    public virtual Forum Parent { get; set; }
    public virtual ICollection<Forum> SubForums { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

Class Forum :

public class ForumDTO
{
    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("parent_id")]
    public int? ParentId { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("created")]
    public DateTime? Created { get; set; }

    [JsonProperty("parent")]
    public ForumDTO Parent { get; set; }

    [JsonProperty("subforums")]
    public List<ForumDTO> SubForums { get; set; }

    [JsonProperty("posts")]
    public List<PostDTO> Posts { get; set; }
}

I have tried a few things too, such as initializing the ForumDTO property with SubForums , since in the previous error message it says that a type can only be initialized in two places if both places are assigned values to same properties and in the same order. Doing this seemed to compress me with the demands:

[AllowAnonymous]
[HttpGet]
[Route("toplevel")]
public async Task<IHttpActionResult> GetAllTopLevel(int page = 1, int pageSize = 15)
{
    var data = Db.Forums
        .Where(f => !f.ParentId.HasValue)
        .OrderByDescending(f => f.Name)
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .Select(f => new ForumDTO
        {
            Id = f.Id,
            Name = f.Name,
            SubForums = f.SubForums.Select(sf => new ForumDTO
            {
                Id = sf.Id,
                Name = sf.Name,
                SubForums = null // Note que fiz a alteração aqui
            }).ToList()
        }).ToList();

    return Ok(data);
}

But it resulted in another error:

  

Unable to create a null constant value of type 'System.Collections.Generic.List'1 [[ZigForum.Models.ViewModels.ForumDTO, ZigForum, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null]]' . Only entity types, enumeration types or primitive types are supported in this context.

Could anyone help me please? I do not know what else to do.

    
asked by anonymous 06.01.2016 / 11:43

4 answers

1

You can not initialize an object within a Linq expression. Simple as that.

But there's a way around it:

public class ForumDTO
{
    // Propriedades

    public static ToForumDTO(Forum f)
    {
        return new ForumDTO 
        {
            Id = f.Id,
            Name = f.Name,
            SubForums = f.SubForums
        }
    }
}

And it consumes in your Linq like this:

 .Select(ForumDTO.ToSubForum).ToList();
    
06.01.2016 / 12:47
1

I see that your Entity and your DTO have the same properties with compatible types and the same name.

In this case, you can try using AutoMapper to do the mapping for you.

Below is an example using LINQ to Objects, but should work smoothly with LINQ to Entities.

Entity

public class Forum
{
    public Forum()
    {
        this.SubForums = new List<Forum>();
    }

    public Guid ID { get; set; }        
    public string Nome { get; set; }
    public Guid? ParentID { get; set; }

    public virtual Forum Parent { get; set; }
    public virtual ICollection<Forum> SubForums { get; set; }
}

DTO

[DataContract]
public class ForumDTO
{
    [DataMember]
    public Guid ID { get; set; }
    [DataMember]
    public string Nome { get; set; }
    [DataMember]
    public Guid? ParentID { get; set; }

    [DataMember]
    public ForumDTO Parent { get; set; }
    [DataMember]
    public IList<ForumDTO> SubForums { get; set; }
}

Global.asax - Registering Maps

protected void Application_Start()
{
    Mapper.CreateMap<Forum, ForumDTO>();
}

Inquiry

var forums = new List<Forum>();
PopularForums(ref forums);

var data = (
    from forum in forums
    where !forum.ParentID.HasValue
    select Mapper.Map<ForumDTO>(forum)
).ToList();

If it still does not work, you can ToList() before doing the map.

var forums = new List<Forum>();
PopularForums(ref forums);

var lista = (
    from forum in forums
    where !forum.ParentID.HasValue
    select forum
).ToList();

var data = Mapper.Map<List<ForumDTO>>(lista);

You can see the full example in the DotNetFiddle , note that it serializes the full tree of forums, regardless of the number of relatives and sub-trees.

    
06.01.2016 / 14:17
1

Create an explicit static converter in your ForumDTO class that converts the Forum to the ForumDTO and in your select use it statement. It would also be interesting for you to replace many of the methods with LINQ statements that mimic SQL.

    
24.02.2017 / 22:11
-1

You can instead initialize an object within the select. Take a look at this code, it works perfectly.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        const int page = 1;
        const int pageSize = 15;

        var forums = new List<Forum>
        {
            new Forum
            {
                Id = 1,
                Name = "Forum 1",
                Created = DateTime.Now,
                SubForums = new List<Forum>
                {
                    new Forum {Id = 2, ParentId = 1, Name = "Forum 2"},
                        new Forum {Id = 3, ParentId = 1, Name = "Forum 3"}
                }
            }
        };

        var data = forums
            .Where(f => !f.ParentId.HasValue)
            .OrderByDescending(f => f.Name)
            .Skip((page - 1)*pageSize)
            .Take(pageSize)
            .Select(f => new ForumDTO
                    {
                        Id = f.Id,
                        Name = f.Name,
                        SubForums = f.SubForums.Select(sf => new ForumDTO
                                                       {
                                                           Id = sf.Id,
                                                           Name = sf.Name,
                                                           SubForums = null
                                                       }).ToList()
                    }).ToList();

        foreach (var d in data)
        {
            Console.WriteLine(d.Name);

            if (d.SubForums.Count <= 0) continue;

            foreach (var subForum in d.SubForums)
            {
                Console.WriteLine("\t"+ subForum.Name);
            }
        }       
    }
}

public class Forum
{
    public Forum()
    {
        SubForums = new List<Forum>();
    }

    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
    public virtual Forum Parent { get; set; }
    public virtual ICollection<Forum> SubForums { get; set; }
}

public class ForumDTO
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Name { get; set; }
    public DateTime? Created { get; set; }
    public ForumDTO Parent { get; set; }
    public List<ForumDTO> SubForums { get; set; }
}

link

    
06.01.2016 / 13:43