views:

230

answers:

1

I'm trying to understand the Entity Framework, and I have a table "Users" and a table "Pages". These are related in a many-to-many relationship with a junction table "UserPages". First of all I'd like to know if I'm designing this relationship correctly using many-to-many: One user can visit multiple pages, and each page can be visited by multiple users..., so am I right in using many2many?

Secondly, and more importantly, as I have understood m2m relationships, the User and Page tables should not repeat information. I.e. there should be only one record for each user and each page. But then in the entity framework, how am I able to add new visits to the same page for the same user? That is, I was thinking I could simply use the Count() method on the IEnumerable returned by a LINQ query to get the number of times a user has visited a certain page.

But I see no way of doing that. In Linq to Sql I could access the junction table and add records there to reflect added visits to a certain page by a certain user, as many times as necessary. But in the EF I can't access the junction table. I can only go from User to a Pages collection and vice versa.

I'm sure I'm misunderstanding relationships or something, but I just can't figure out how to model this. I could always have a Count column in the Page table, but as far as I have understood you're not supposed to design database tables like that, those values should be collected by queries...

Please help me understand what I'm doing wrong...

+1  A: 

You are doing it right.

In the Entity Data Model (EDM) Many-To-Many relationships can be represented with or without a join table, depending on whether it contains some additional fields. See the article below for more details.


In your case, the User entity will directly reference a collection of Page entities and vice versa, since your model doesn't include a mapping for the User_Page join table.

In order to add visits to a certain page on a user you could for example do something like:

using (var context = new YourEntityModelObjectContext())
{
     var page = context.Pages.FirstOrDefault(p => p.Url == "http://someurl");
     var user = context.Users.FirstOrDefault(u => u.Username == "someuser");

     user.Pages.Add(page);

     context.SaveChanges();
}

Or you could do it from the other side of the relation:

using (var context = new YourEntityModelObjectContext())
{
     var page = context.Pages.FirstOrDefault(p => p.Url == "http://someurl");
     var user = context.Users.FirstOrDefault(u => u.Username == "someuser");

     page.Users.Add(user);

     context.SaveChanges();
}

In both cases a new record will be added to the User_Page join table.

If you need to retrieve the number of pages visited by a particular user you could simply do:

using (var context = new YourEntityModelObjectContext())
{
     var user = context.Users.FirstOrDefault(u => u.Username == "someuser");
     var visitCount = user.Pages.Count;
}

Related resources:

Enrico Campidoglio
Thanks, but unless I have misunderstood you somehow, this doesn't work, I had already tried that. I get this exception: "Unable to update the EntitySet 'UserPages' because it has a DefiningQuery and no <InsertFunction> element exists in the <ModificationFunctionMapping> element to support the current operation." I'm not quite sure what this means, since I'm rather new to the EF...
Anders Svensson
It sounds like the "UserPages" entity maps to the join table between the "User" and "Page" tables. You shouldn't need to include that entity in your model and instead create a direct Many-to-Many relation between the "User" and "Page" entities. Check out this article for more details on how to do that: http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
Enrico Campidoglio
Well no, there isn't a join table in the model, only the User and Page classes. So the UserPages table only seems to be used in the background somehow. I did choose all tables when I created the Entity Data Model in the dialog box, but I guessed that if I didn't do that the relationship wouldn't be included in the model? But again, even though I chose all tables, the join table was not included in the generated model...
Anders Svensson
I see. The join table is not part for the conceptual model (CSDL), but is included in the storage model (SSDL). Both are part of the EDM model. The exception you described before is caused by the fact that the join table doesn't have a primary key defined in the database. In that case Visual Studio will generate a "view" in the storage model (a "DefiningQuery"), which by default cannot be updated. My suggestion is that you define a primary key for the join table in the database and then update your model (in the EDM designer, right-click and choose "Update model from database")
Enrico Campidoglio