简体   繁体   中英

Iteration error in Entity Framework

I'm creating a simple Messaging application with C# Winforms. I'm connecting to a SQLEXPRESS server running on my computer and storing everything there.

Here is my schema:

public class UserContext : DbContext {
    public UserContext() : base("name=BuddyDatabase") {

    }
    public DbSet<User> Users { get; set; }
    public DbSet<Message> Messages { get; set; }
}

public class User {
    [Key]
    public string username { get; set; }
    public string password { get; set; }

    public static implicit operator User(bool v) {
        throw new NotImplementedException();
    }

    public virtual List<User> friends { get; set; }
}

public class Message {
    [Key]
    public int ID { get; set; }

    public virtual User sender { get; set; }
    public virtual User recipient { get; set; }

    public string content { get; set; }

    public virtual List<User> group { get; set; }
}

Pretty simple

Sending messages to a single recipient works but group messaging doesn't, whenever I open the "Messages Screen" I get this error:

Unable to create a constant value of type 'WindowsFormsApp1.User'. Only primitive types or enumeration types are supported in this context.

And this is the method I have run when the Message Screen loads up:

var x = db.Messages.Where(b => b.recipient.username == currentuser.username);
foreach (var y in x) {
    MainMessagesBox.Text += y.content;
}
x = null;

var z = db.Messages.Where(b => b.group.Contains(currentuser));
foreach (var y in z) {
    MainMessagesBox.Text += y.content;
}

(Visual Studio highlights the 'in' in this line as the cause of the error.)

foreach (var y in z) {

Thought this might be a problem with me using the Where method and involving non-primitives (as the error message suggests) so I tried changing my schema so that group is a list of strings that contain the usernames of the intended recipients and modified the methods accordingly but that didn't work either. Will provide code and errors for that trial on request.

Here is the actual "Send Message" code:

if (!textBox1.Text.Contains(',')) {
    db.Messages.Add(new Message { sender = currentuser, recipient = db.Users.Find(textBox1.Text), content = currentuser.username + ": " + ContentBox.Text + "\n" });
    db.SaveChanges();
} else {
     List<User> recips = new List<User>();
     string[] poop = textBox1.Text.Split(',');

     foreach (var x in poop) {
         recips.Add(db.Users.Find(x));
     }

     db.Messages.Add(new Message { sender = currentuser, group = recips, content = ContentBox.Text });
     db.SaveChanges();
}

poop is an array of the intended recipients separated by comma from a textbox, this is temporary.

Sorry if anything is misformatted or I'm unclear, this is my first question.

Thank you in advance.

The problem is the Select query on message table where you check for the current user to be in the group list. This won't work, because the query has to be translated to SQL to be send to the database.

SQL does not understand what a ´User´ type is and can't compare references. And actually, neither does C# compare this properly. Two objects of type ´User´ with the same username would not be equal in your case. You need to compare the username of those objects.

Regardless, as Bitmask explained in a comment, you have to properly define the foreign key for the messages.

It's quite easy for the sender of the messages. You have a public virtual ICollection<Message> Messages { get; set; } public virtual ICollection<Message> Messages { get; set; } public virtual ICollection<Message> Messages { get; set; } in your User class.

But for the recipients, it's a many-to-many relation. So both, the User class and the Message class have a collection to Message and User respectively.

Something like this should work:

public class User {
    [Key]
    public string username { get; set; }
    public string password { get; set; }

    public static implicit operator User(bool v) {
        throw new NotImplementedException();
    }

    public virtual ICollection<Message> SentMessages { get; set; }
    public virtual ICollection<Message> ReceivedMessages { get; set; }

    public virtual List<User> friends { get; set; }
}

public class Message {
    [Key]
    public int ID { get; set; }

    public virtual User Sender { get; set; }
    public virtual ICollection<User> Recipients { get; set; }

    public string content { get; set; }
}

Check this link for an example on how to define a many-to-many relation either using data annotations or fluent API. This assumes you're using EF6 though. If you're using an older version of EF, you might have to define the joining table yourself to get the many-to-many relation.

And as for the query. You can use the following in your message screen OnLoad method:

MainMessagesBox.Text = string.Join(System.Environment.NewLine, currentuser.ReceivedMessages.Select(m => m.content))

This concatenates all messages, separated with a new line.

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