I'm creating a forum and I have recently implemented Simple Membership. What I'm trying to do now is display the actual name of who wrote the post. As of now I'm only able to display the UserId of the user.
I have two models: The Simplemembership model AccountModels, and my ForumModels.
My Forummodel Posts contains a field for UserId.
I've tried adding some of the tables from AccountModels to my ForumModels, but this just caused an error (Since I was trying to create the same table twice)
I've tried creating a ViewModel that contained Posts and UserProfile, but couldn't populate it correctly with data.
Lastly I tried performing a join on the two tables Post and Userprofile
var posts = (from p in db.Posts
join u in udb.UserProfiles
on p.UserId equals u.UserId
where p.TopicId == id
select p);
return View(posts);
This created the error:
The specified LINQ expression contains references to queries that are associated with different contexts.
Any ideas on what I should do?
It seems you're trying to perform a Join between two different contexts. You can either try to:
1) Make a call to the first context and persist the ID collection on a list like this:
var userIds = udb.UserProfiles.UserId.ToList();
var posts = from p in db.Posts
where p.TopicId == id && userIds.Contains(p.UserId)
select p;
2) Add the Posts to the same context as the one used by simple membership and you'll be able to use the join.
Update to Example code
//This will retrieve the posts from the database.
//ToList() will translate and execute the SQL statement,
//so you can manipulate the colleciton in memory
var posts = (from p in db.Posts
where p.TopicId == id
select p).ToList();
//Get all the user IDs on the post collection.
var userIds = posts.Select(p => p.UserId).Distinct();
//Then we will select the users in the posts
var users = ubd.UserProfiles.Where(u => userIds.Contains(u.UserId)).ToList();
//Now you have both collections in memory and you can perform the join
var joined = from p in posts
join u in users
on p.UserId equals u.UserId
select new {Title = p.Title, UserName = u.UserName};
Related
may anybody help me with this task...
persistence provider is eclipselink 2.6.
i want to retrieve a list of users that may have 0 or n documents. because both tables have a few columns i want to use SELECT NEW Entity (userId, amountDocuments), i only need the user-id and the amount of documents for this task. if the user hasn't any documents yet, "0" should be shown, e.g.:
UserId: 1 2 3 4
AmountDocs: 0 1 0 3
Mapping for Documents in Entity User is as follows:
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL,mappedBy = "user", targetEntity = UserDocument.class)
#OrderBy("sortOrder ASC")
#JoinFetch(JoinFetchType.OUTER)
protected List<UserDocument>documents;
Mapping for User in Entity UserDocument is as follows:
#ManyToOne(cascade=CascadeType.ALL)
protected User user;
and here is the jpa-query:
SELECT DISTINCT
NEW user.entity.User(u.id,count(doc.user)) FROM User u
LEFT JOIN u.documents doc ON doc.user = u
AND doc.active = 't'
GROUP BY u.id
Problem is, that i only retrieve those two users who have documents that match doc.active='t'.
I also tried it with SIZE(u.documents) which also just returns two users and additionally wrong document-count values.
What is wrong here?
Thanks in advance!
finally after spending hours with that simple stuff, the right solution came with:
SELECT DISTINCT
NEW user.entity.User(u.id,count(doc)) FROM User u
LEFT JOIN u.documents doc ON doc.user = u AND doc.active = 't'
GROUP BY u.id
i have to count the left joined documents itself not the users.
I have a Users table, a Roles table, and a cross reference table Users_Roles which has the following columns:
User_ID Role_ID Beamline_ID Facility_ID Laboratory_ID
The Beamline_ID, Facility_ID, Laboratory_ID are only filled in depending on the Role_ID. If someone has the Role_ID of 2 ("Lab Admin") then they will have an entry in Laboratory_ID.
I am trying to figure out how to get the Laboratory_ID for a specific row in this table. For example, I know I have User_ID = 1. I want to get the Laboratory_ID for User_ID = 1 and Role_ID = 2 ("Lab Admin").
This is obviously simple when dealing with SQL but I am new to Entity Framework and I am trying to do this with Entities and I'm having some trouble. I am using MVC so in my controller I have done this:
User user = new User();
user.GetUser(User.Identity.Name);
var labID = user.Users_Roles.Where(r => r.Role_ID == 2);
That should get me the "row" of that user when the Role = Lab Admin but I don't know how to grab the Labortory_ID column now. I thought maybe it would be:
var labID = user.Users_Roles.Where(r => r.Role_ID == 2).Select(l => l.Laboratory_ID);
But that is not correct. Any help would be greatly appreciated.
EDIT: I am using the database first approach and I am using DBContext. So typically I would access the context like this:
var context = new PASSEntities();
As for why the code you've already tried doesn't work, at first glance I think you just need to tack on a .Single() to the end:
var labID = user.Users_Roles.Where(r => r.Role_ID == 2)
.Select(l => l.Laboratory_ID)
.Single();
Select() returns a sequence, and it looks like you want a single value, so I think that might be the cause of the problem you were having. Single() will throw an exception if there's not exactly one result, which sounds appropriate for your table structure, but there's also First() if you don't care about enforcing that.
But for this kind of thing, you might also think about just querying manually using your DbContext, instead of trying to load entities individually and then traversing down through their navigation properties -- that results in more local searching than you probably need, and, depending on the circumstances, might be performing more/larger queries against your context and database than necessary too.
Here's an example LINQ query you could use (you might have to adjust the names of your tables and so on):
var labID = (from ur in context.Users_Roles
where ur.User_ID == 1 && ur.Role_ID == 2
select ur.Laboratory_ID).Single();
When you execute that statement, it will get translated into SQL equivalent to this:
SELECT TOP 1 Laboratory_ID FROM Users_Roles WHERE User_ID = 1 AND Role_ID = 2
...so it's pretty efficient. (Actually, if User_ID and Role_ID form the primary key for the Users_Roles table, and that record has already been loaded previously, I think it'll just return the cached copy, and won't need to query the database at all.)
That's a very bare-bones query, though; more than likely, you're eventually going to want to query based on properties of the user or role, instead of just searching for hard-coded IDs you know in advance. In that case, you can adjust your query to something like this:
var labID = (from u in context.Users
join ur in context.Users_Roles on u.User_ID equals ur.User_ID
join r in context.Roles on ur.Role_ID equals r.Role_ID
where u.UserName == "John Smith" && r.RoleName == "Lab Admin"
select ur.Laboratory_ID).Single();
That would, as you can probably tell, return the Lab ID for the user John Smith under the role Lab Admin.
Does that make any sense? LINQ syntax can take a little getting used to.
I have table structure as follows
table user: iduser, firstName, lastName, username, email, dateJoined, dateOfBirth, password
table tag : idtag, tagName
table post: idpost, title, content, iduser, date, dateCreated
table post_tag : idpost, idtag :-join table for many to many relationship for tag and post
table user_tag : iduser, idtag :-join table for many to many relationship for user and tag
what i want is the names of the tags for a specific user using JPA query.
I also wanted to retrieve the post for the user and I could retrieve that using the following NamedQuery
SELECT p FROM Post p WHERE p.user = :user ORDER BY p.dateCreated DESC
using this, I was successfully able to retrieve posts for the user as Post and user are both entities and is a valid JPA query
But when I try to retrieve the tags for a specific just like I retrieved posts for user,
so I tried to write a join query something like this
SELECT t.tagName FROM tag t JOIN user_tag ut ON ut.idtag = t.idtag JOIN user u ON ut.iduser = u.iduser WHERE u.username = :username
but eclipse complaints for this query as user_tag is not the entity type. I am not able to formulate a join query using JPA QL which can retrieve tags for a user
Any help is highly appreciated.
I was able to formulate the query as follows
SELECT tag.tagName FROM Tag tag WHERE tag.users = :users
This was just like the query I used for Post and User
but I have another doubt that if we don't need to write a join query in this case, then in which scenarios JPA QL join queries are used?
Lets say we have 3 tables, Users, Products, Purchases.
There is a view that needs to display the purchases made by a user.
I could lookup the data required by doing:
from p in DBSet<Purchases>.Include("User").Include("Product") select p;
However, I am concern that this may have a performance impact because it will retrieve the full objects.
Alternatively, I could select only the fields i need:
from p in DBSet<Purchases>.Include("User").Include("Product") select new SimplePurchaseInfo() { UserName = p.User.name, Userid = p.User.Id, ProductName = p.Product.Name ... etc };
So my question is:
Whats the best practice in doing this?
== EDIT
Thanks for all the replies.
[QUESTION 1]: I want to know whether all views should work with flat ViewModels with very specific data for that view, or should the ViewModels contain the entity objects.
Real example: User reviews Products
var query = from dr in productRepository.FindAllReviews()
where dr.User.UserId = 'userid'
select dr;
string sql = ((ObjectQuery)query).ToTraceString();
SELECT [Extent1].[ProductId] AS [ProductId],
[Extent1].[Comment] AS [Comment],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Id] AS [Id],
[Extent1].[Rating] AS [Rating],
[Extent1].[UserId] AS [UserId],
[Extent3].[CreatedTime] AS [CreatedTime1],
[Extent3].[CreatorId] AS [CreatorId],
[Extent3].[Description] AS [Description],
[Extent3].[Id] AS [Id1],
[Extent3].[Name] AS [Name],
[Extent3].[Price] AS [Price],
[Extent3].[Rating] AS [Rating1],
[Extent3].[ShopId] AS [ShopId],
[Extent3].[Thumbnail] AS [Thumbnail],
[Extent3].[Creator_UserId] AS [Creator_UserId],
[Extent4].[Comment] AS [Comment1],
[Extent4].[DateCreated] AS [DateCreated],
[Extent4].[DateLastActivity] AS [DateLastActivity],
[Extent4].[DateLastLogin] AS [DateLastLogin],
[Extent4].[DateLastPasswordChange] AS [DateLastPasswordChange],
[Extent4].[Email] AS [Email],
[Extent4].[Enabled] AS [Enabled],
[Extent4].[PasswordHash] AS [PasswordHash],
[Extent4].[PasswordSalt] AS [PasswordSalt],
[Extent4].[ScreenName] AS [ScreenName],
[Extent4].[Thumbnail] AS [Thumbnail1],
[Extent4].[UserId] AS [UserId1],
[Extent4].[UserName] AS [UserName]
FROM [ProductReviews] AS [Extent1]
INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]
LEFT OUTER JOIN [Products] AS [Extent3] ON [Extent1].[ProductId] = [Extent3].[Id]
LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent1].[UserId] = [Extent4].[UserId]
WHERE N'615005822' = [Extent2].[UserId]
or
from d in productRepository.FindAllProducts()
from dr in d.ProductReviews
where dr.User.UserId == 'userid'
orderby dr.CreatedTime
select new ProductReviewInfo()
{
product = new SimpleProductInfo() { Id = d.Id, Name = d.Name, Thumbnail = d.Thumbnail, Rating = d.Rating },
Rating = dr.Rating,
Comment = dr.Comment,
UserId = dr.UserId,
UserScreenName = dr.User.ScreenName,
UserThumbnail = dr.User.Thumbnail,
CreateTime = dr.CreatedTime
};
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Thumbnail] AS [Thumbnail],
[Extent1].[Rating] AS [Rating],
[Extent2].[Rating] AS [Rating1],
[Extent2].[Comment] AS [Comment],
[Extent2].[UserId] AS [UserId],
[Extent4].[ScreenName] AS [ScreenName],
[Extent4].[Thumbnail] AS [Thumbnail1],
[Extent2].[CreatedTime] AS [CreatedTime]
FROM [Products] AS [Extent1]
INNER JOIN [ProductReviews] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId]
INNER JOIN [Users] AS [Extent3] ON [Extent2].[UserId] = [Extent3].[UserId]
LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent2].[UserId] = [Extent4].[UserId]
WHERE N'userid' = [Extent3].[UserId]
ORDER BY [Extent2].[CreatedTime] ASC
[QUESTION 2]: Whats with the ugly outer joins?
In general, only retrieve what you need, but keep in mind to retrieve enough information so your application is not too chatty, so if you can batch a bunch of things together, do so, otherwise you'll pay network traffic cost everytime you need to go back to the database and retrieve some more stuffs.
In this case, assuming you will only need those info, I would go with the second approach (if that's what you really need).
Eager loading with .Include doesn't really play nice when you want filtering (or ordering for that matter).
That first query is basically this:
select p.*, u.*, p2.*
from products p
left outer join users u on p.userid = u.userid
left outer join purchases p2 on p.productid = p2.productid
where u.userid == #p1
Is that really what you want?
There is a view that needs to display the purchases made by a user.
Well then why are you including "Product"?
Shouldn't it just be:
from p in DBSet<Purchases>.Include("User") select p;
Your second query will error. You must project to an entity on the model, or an anonymous type - not a random class/DTO.
To be honest, the easiest and most well performing option in your current scenario is to query on the FK itself:
var purchasesForUser = DBSet<Purchases>.Where(x => x.UserId == userId);
That should produce:
select p.*
from products p
where p.UserId == #p1
The above query of course requires you to include the foreign keys in the model.
If you don't have the FK's in your model, then you'll need more LINQ-Entities trickery in the form of anonymous type projection.
Overall, don't go out looking to optimize. Create queries which align with the scenario/business requirement, then optimize if necessary - or look for alternatives to LINQ-Entities, such as stored procedures, views or compiled queries.
Remember: premature optimization is the root of all evil.
*EDIT - In response to Question Update *
[QUESTION 1]: I want to know whether all views should work with flat ViewModels with very specific data for that view, or should the ViewModels contain the entity objects.
Yes - ViewModel's should only contain what is required for that View. Otherwise why have the ViewModel? You may as well bind straight to the EF model. So, setup the ViewModel which only the fields it needs for the view.
[QUESTION 2]: What's with the ugly outer joins?
That is default behaviour for .Include. .Include always produces a left outer join.
I think the second query will throw exception because you can't map result to unmapped .NET type in Linq-to-entities. You have to return annonymous type and map it to your object in Linq-to-objects or you have to use some advanced concepts for projections - QueryView (projections in ESQL) or DefiningQuery (custom SQL query mapped to new readonly entity).
Generally it is more about design of your entities. If you select single small entity it is not a big difference to load it all instead of projection. If you are selecting list of entities you should consider projections - expecially if tables contains columns like nvarchar(max) or varbinar(max) which are not needed in your result!
Both create almost the same query: select from one table, with two inner joins. The only thing that changes from a database perspective is the amount of fields returned, but that shouldn't really matter that much.
I think here DRY wins from a performance hit (if it even exists): so my call is go for the first option.
I am new to Entity Framework and LINQ and have run into a rather odd scenario.
I have been using the following query to return account information:
var account = ((from acct in _entities.Account
join m in _entities.Item on acct.Id equals m.Account.Id
where acct.Id == accountId && m.ItemNumber.EndsWith(itemNumber)
select acct) as ObjectQuery<Account>).Include("Item.ItemDetails");
We recently made some changes to the database and generated a new edmx file. Following the change the above query still returns the account and associated Item but the ItemDetails is no longer being included.
I have validated the SQL returned by the query and there doesn't seem to be anything wrong as the correct data is being returned.
Furthermore I don't see anthing different in the edmx file between the Item and ItemDetails objects as these were not changed and the navigation property is there.
Has anyone seen this before?
Thanks
In Include(...) is used the name of the navigation property so it will be good to check the exact name of the property from the .edmx (especially if it is singular or plural).
Also you can try to change the query like this:
var account = from acct in _entities.Account.Include("Item.ItemDetails")
join m in _entities.Item
on acct.Id equals m.Account.Id
where acct.Id == accountId && m.ItemNumber.EndsWith(itemNumber)
select acct;
You have one of two possible scenarios:
Item has a relationship to Account (expressed in your Entity Model as a EntityAssociation and in DB as a foreign key):
There is no relationship between Item set and Account set, hence, you must specify a join in LINQ as you have done.
Case 1: if this is the case, then you don't need a join statement... by selecting Acount.Item will naturally give you all items where Item.AccountID is equal to Account.ID
So your join statement: join m in _entities.Item on acct.Id equals m.Account.Id
has basically told Item to loop back onto Account to check the ID. If they were not already connected, then you could not have gotten m.Account.ID
Case 2: If there is no relationship between Account and Item, then the .Include() will definitely not work because the navigational property DOES NOT exist in your model.
Conclusion: Check your new model to see if a relationship exists between Account and Item. If yes, then remove the Join. If no relationship, then you've done something wrong.
Here is a select statement assuming scenario 1 and that Account.Item is not a collection:
var account = from acct in _entities.Account.Include("Item.ItemDetails")
where acct.Id == accountId && acct.Item.ItemNumber.EndsWith(itemNumber)
select acct;