1

I am running in to a problem where I have made the following query in my controller:

var query = from pmt in db.ProjectHasTags
            join project in db.Projects on pmt.ProjectId equals project.ID
            join tag in db.ProjectTags
                 on pmt.TagId equals tag.ID
                 group pmt by pmt.Project into pmtGroup
                    select new
                    {
                        Project = pmtGroup.Key,
                        Tags = pmtGroup.Select(project => project.ProjectTag)
                    };

I want to return this query to a view using:

return View(query.ToList());

In the view file I have the following code:

@model IEnumerable<portfolio.Models.ProjectHasTag>

@foreach (var p in Model)
{
    @p.Project.Title

    foreach (var tag in p.Tags)
    {
        @tag.title
    }       
}

I get the following error:

The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType62[portfolio.Models.Project,System.Collections.Generic.IEnumerable1[portfolio.Models.ProjectTag]]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[portfolio.Models.ProjectHasTag]'.

The ProjectHasTag model code:

public class ProjectHasTag
{
    public int ID { get; set; }

    public int? ProjectId { get; set; }

    [ForeignKey("ProjectId")]
    [DisplayName("Project")]
    public virtual Project Project { get; set; }

    public int? TagId { get; set; }

    [ForeignKey("TagId")]
    [DisplayName("Tag")]
    public virtual ProjectTag ProjectTag { get; set; }

    public virtual ICollection<ProjectTag> Tags { get; set; }

}

This is what I want to achieve: https://i.stack.imgur.com/DAZ5n.png (I cant post images yet)

thanks for taking the time, English is not my first language.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
JorisP
  • 46
  • 1
  • 6
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Nov 03 '13 at 23:41

3 Answers3

4

The problem is this part of your query:

select new
{
    Project = pmtGroup.Key,
    Tags = pmtGroup.Select(project => project.ProjectTag)
};

You're not specifying the type that should be instantiated, so it's creating an anonymous type, which is then being passed to your view. You probably want something like this:

select new ProjectHasTag
{
    Project = pmtGroup.Key,
    Tags = pmtGroup.Select(project => project.ProjectTag)
};

Update

Just as the error is telling you, ProjectHasTag does not have a Tags property. It looks like what you want is really this:

select new ProjectHasTag
{
    Project = pmtGroup.Key,
    ProjectTag = pmtGroup.Select(project => project.ProjectTag)
};

However, it's a little unclear what you're trying to do because in your view it looks as though you have multiple tags for each project, in which case it should really be a collection. Something like:

public virtual ICollection<ProjectTag> Tags { get; set; }

Update Two

I forgot the Entity Framework (EF) is picky when it comes to directly instantiating entity types. For a quick fix, you should be able to map from an anonymous type to the entity type (as described here).

However, EF doing this is actually a good thing because it's forcing you to adopt a design strategy that will allow you to leverage more power from MVC. In particular, this would be a good time to learn about ViewModels (see: ASP.NET MVC - How exactly to use View Models and http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx).

Community
  • 1
  • 1
John H
  • 14,422
  • 4
  • 41
  • 74
  • I get the following error: 'portfolio.Models.ProjectHasTag' does not contain a definition for 'Tags', I my model ProjectHasTag missing something? – JorisP Nov 03 '13 at 16:54
  • It would be helpful if you could edit your question to include the code for `ProjectHasTag`. – John H Nov 03 '13 at 16:56
  • public class ProjectHasTag { public int ID { get; set; } public int? ProjectId { get; set; } [ForeignKey("ProjectId")] [DisplayName("Project")] public virtual Project Project { get; set; } public int? TagId { get; set; } [ForeignKey("TagId")] [DisplayName("Tag")] public virtual ProjectTag ProjectTag { get; set; } } I added this code to the question. – JorisP Nov 03 '13 at 16:58
  • I added the last line ( public virtual ICollection Tags { get; set; } ) to the Model: ProjectHasTag and it gives me the an error when I try to run it: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'portfolio.Models.ProjectTag'. An explicit conversion exists (are you missing a cast?) – JorisP Nov 03 '13 at 17:18
  • @JorisP So you do have multiple tags for each project or just one? Your model now has properties for both. It's not clear which is the one you want. – John H Nov 03 '13 at 17:28
  • 1
    It seems like projects can have multiple tags, since he is narrowing ProjectHasTags by ProjectId, and then finding the actual Tags by joining with Projects for every TagId in ProjectHasTags. I was thinking that he might be wanting to bind the Tags to dropdownlist, and then ProjectTag is the selected value from the list, but that does seem to be the case :) – tranceporter Nov 03 '13 at 17:32
  • I have multiple tags for each project (like in this image: http://i.stack.imgur.com/DAZ5n.png) – JorisP Nov 03 '13 at 17:33
  • @JorisP In that case, the first `select` query I stated should be fine: `select new ProjectHasTag { Project = pmtGroup.Key, Tags = pmtGroup.Select(project => project.ProjectTag) };` – John H Nov 03 '13 at 17:42
  • I got the following bug: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.ICollection'. An explicit conversion exists (are you missing a cast?) But I fixed it by changing the last model ProjectHasTags line to public virtual IEnumerable Tags { get; set; } This was the result http://i.imgur.com/aeSHtSg.png – JorisP Nov 03 '13 at 18:12
  • @JorisP This should do it: `select new ProjectHasTag { Project = pmtGroup.Key, Tags = pmtGroup.Select(project => project.ProjectTag).ToList() };` – John H Nov 03 '13 at 18:15
  • @JorisP That works too. I assume you're using Entity Framework, so if ever you're not sure of which collection type to use, [this question](http://stackoverflow.com/questions/10113244/why-use-icollection-and-not-ienumerable-or-listt-on-many-many-one-many-relatio) has answers that will help. Is everything working now? – John H Nov 03 '13 at 18:19
  • Unfortunately I got a new error: The entity or complex type 'portfolio.Models.ProjectHasTag' cannot be constructed in a LINQ to Entities query. – JorisP Nov 03 '13 at 18:43
  • @JorisP Updated again. – John H Nov 03 '13 at 23:27
0

In the select part of your query, you create an anonymous object, and your model requires objects form type ProjectHasTag. So this should look like (look at the select new ProjectHasTag):

var query = from pmt in db.ProjectHasTags
            join project in db.Projects on pmt.ProjectId equals project.ID
            join tag in db.ProjectTags
                 on pmt.TagId equals tag.ID
                 group pmt by pmt.Project into pmtGroup
                    select new ProjectHasTag
                    {
                        Project = pmtGroup.Key,
                        Tags = pmtGroup.Select(project => project.ProjectTag)
                    };
Kevin Brechbühl
  • 4,717
  • 3
  • 24
  • 47
0

Since you have an ICollection , you are telling Linq, that you want your ProjectTags to be lazy loaded. Like @John H said above, since you have added the Tags property to your ProjectHasTag class, you can populate it such:

select new ProjectHasTag
{
    Project = pmtGroup.Key,
    Tags = pmtGroup.Select(project => project.ProjectTag)
};
tranceporter
  • 2,241
  • 1
  • 21
  • 23