简体   繁体   中英

IQueryable two tables from Code First Entity Framework

I have a seemingly simple Post table with multiple Votes like this:

Post:

namespace Api.Models
{
    public class Post
    {
        public int id { get; set; }
        public bool deleted { get; set; }
        public virtual User creator { get; set; }

        (...)

        public virtual ICollection<Vote> votes { get; set; }
    }
}

Vote:

namespace Api.Models
{
    public class Vote
    {
        public int id { get; set; }    
        public bool seen { get; set; } = false;

        (...)

        [JsonIgnore]
        [IgnoreDataMember]
        public virtual ICollection<Post> post { get; set; }    
    }
}

This has generated a VotePost table in the SQL database with Vote_id and Post_id as foreign keys.

I am trying to only get the Votes that seen == false that are connected to the post with p.creator.user_id, if the post has 0 votes or all votes are seen. Then I don't need to return the post element

The posts has multiple votes objects and I don't know how to filter out all the vote.seen == false

        IQueryable<Post> postQuery = (from p in db.Posts where 
                                      p.deleted == false && 
                                      p.creator.user_id == user_id &&

                                      // all p.votes object seen == false ?

                                      select p);

Thanks in advance for any help

You can try like that;

IQueryable<Post> postQuery = (from p in db.Posts
                  where 
                  p.deleted == false && 
                  p.creator.user_id == user_id &&
                  (p.votes == null || !p.votes.Any(v => v.seen == true))
                  select p);

So you have Users, Posts and Votes.

There is a one-to-many relation between a Creator and a Post: Every Creator has zero or more Posts, every Post belongs to exactly one Creator.

There seems to be a many-to-many relation between Posts and Votes: Every Post has zero or more Votes, and every Votes is for to zero or more Posts.

I'm not sure why you chose to deviate from standard entity framework conventions and left out the foreign keys of your class definitions. This only makes your query more difficult. But we'll have to do with what you've designed.

You wrote:

I am trying to only get the Votes that seen == false that are connected to the post with p.creator.user_id, if the post has 0 votes or all votes are seen. Then I don't need to return the post element

I gues this is a difficult and a bit ambiguous way to say:

Given a variable user_id, which is the primary key of a user. Give me the query that results in all Votes, that are not Seen, that have any Post of the Creator with primary key user_Id.

var result = dBContext.Votes
    .Where(vote => !vote.Seen 
        && vote.Posts.Any(post => post.Creator.user_id == user_id);

If you'd included the foreign keys in your classes it would even have been simpler:

class User
{
    public int Id {get; set;}    // primary key
    // a User has zero or more Posts:
    public virtual ICollectioins<Post> Posts {get; set;}
    ...
}
class Post
{
    public int Id {get; set;}    // primary key

    // Every Post belongs to exactly one User via foreign key
    public User User {get; set;}
    public int UserId {get; set;}

    // a Post has zero or more Votes (many-to-many)
    public virtual ICollection<Vote> Votes{get; set;}
    ...
}

class Vote
{
    public int Id {get; set;}    // primary key
    // A vote is for zero or more Posts (many-to-many)
    public virtual ICollection<Post> Posts{get; set;}

    public bool Seen {get; set;}
}

It might be that you named some of the properties differently, but you get the gist.

Once you've included the foreign key to the User of the Post, your query is even simpler:

 var result = dBContext.Votes
    .Where(vote => !vote.Seen 
        && vote.Posts.Any(post => post.userid == user_id);

Advice: whenever you think of deviating from entity framework code first conventions , like using non-conventional names for primary keys and foerign keys, using non-conventional names for ICollection properties etc. think twice: is this deviation really needed?

Deviating means the need of fluent Api, or Attributes. Maintenance: others won't immediately see what you mean. And your queries might become more difficult

You can just use All() linq method:

IQueryable<Post> postQuery = (from p in Posts
                              where p.deleted == false 
                              && p.creator.user_id == user_id 
                              && p.votes.All(v => !v.seen)
                              select p).AsQueryable();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM