LinQ to Entities: Query with five tables JOIN - c#

I have five tables all with a primary key of ID
User
User_Role_Relation with Foreign Keys User_ID and Role_ID
Role
Role_Right_Relation with Foreign Keys Role_ID and Right_ID
Right
I am currently getting the Rights for the selected User with the following query in a stored procedure
SELECT DISTINCT
tbl_Right.ID, tbl_Right.Name
FROM
tbl_User_Role_Relation
INNER JOIN
tbl_Role_Right_Relation ON tbl_User_Role_Relation.Role_ID = tbl_Role_Right_Relation.Role_ID
INNER JOIN
tbl_Right ON tbl_Role_Right_Relation.Right_ID = tbl_Right.ID
WHERE
tbl_User_Role_Relation.User_ID = #User_ID
I am trying to covert this to LINQ to Entity with this code
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
where urr.User_ID == userid
select r;
but I get the following error
An expression of type 'Models.Role' is not allowed in a subsequent from clause in a query expression with source type 'System.Linq.IQueryable' Type inference failed in the call to 'SelectMany'
Any advise would be helpful.
Thanks.

First of all that linq query is doing a cross join, not inner join like your sql. You should check out this
Second of all, you would probably be best correctly defining the relationships between entities in the edmx and you probably won't have to join at all, instead you can use navigation properites to access parent/children and filter on those properties directly
The idea of the entity framework is you don't have to flatten the hierarchy

Edit 3
If you posted your code verbatim, then you are missing a line
from u in urr.Users
So:
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
from u in urr.Users
where u.User_ID == userid
select r;
Or at least there's a typo and the where should read:
where urr.User_ID == userid
resulting in:
var query = from r in context.Rights
from rrr in r.Role_Right_Relation
from rl in rrr.Role
from urr in rl.User_Role_Relation
where urr.User_ID == userid
select r;

If you use method syntax you should be able to do something like:
var user = context.Users.FirstOrDefault(u => u.Id == userId);
var rights =user.Roles.SelectMany(role => role.Rights).Distinct();
Be sure to check user is not null before getting rights

Related

Using 'OR' inside a LinQ join query (adapting SQL into LinQ)

I have a SQL query that basically joins the MyWords table to the MyTranslations table on two keys. Here are my tables for better understanding.
MyWords table:
Id
WordString
LangType
MyTranslations table:
Id
WordColumnAId
WordColumnBId
Please note that WordColumnAIdand WordColumnBId columns are FKs that represents the MyWords.Id.
My SQL query:
SELECT MyTranslations.Id
,WordColumnAId
,WordColumnBId
,MyWords.WordString
,MyWords.LangType
FROM MyTranslations
join MyWords on (MyTranslations.WordColumnAId = MyWords.Id or MyTranslations.WordColumnBId = MyWords.Id)
where MyWords.LangType !=#currentLangType
I know it doesn't make sense to use join on with multiple keys at first glance but I'm trying to a cross-query that would join two tables whether the key is on WordColumnAId or WordColumnBId.
Problem
I'm trying to adapt the above T-SQL query into a LinQ query. The problem is that I can't find my way around LinQ to use two keys in a single join query.
Here's what I've got so far:
from translation in queryableTranslation
join word in _myWordRepository on translation.WordColumnAId equals word.Id // This is where I want to add `or translation.WordColumnBId equals word.Id` but get errors.
where word.LangType != currentLangType
select new QueryResultDto {
MyTranslationId = translation.Id,
WordString = word.WordString,
LanguageType = word.LangType,
WordColumnAId = translation.WordColumnAId,
WordColumnbId=translation.WordColumnbId,
};
I'm very new to the LinQ and trying to learn.
So my question: is there a way to achieve this in LinQ or am I trying the impossible? I'm also open to better approaches.
EF and other LINQ providers should translate query to INNER JOIN when using this syntax:
var query =
from translation in queryableTranslation
from word in _myWordRepository.Where(word => translation.WordColumnAId == word.Id
|| translation.WordColumnBId = word.Id)
where word.LangType != currentLangType
select new QueryResultDto
{
MyTranslationId = translation.Id,
WordString = word.WordString,
LanguageType = word.LangType,
WordColumnAId = translation.WordColumnAId,
WordColumnbId=translation.WordColumnbId,
};

How to convert C# Linq to query in SQL Server?

I'm a junior developer and trying to convert the following linq statement to T-SQL:
var items = from u in DataContext.Users_SearchUsers(searchPara.UserFirstName,
searchPara.UserLastName,
searchPara.UserEmailAddress,
fetchOptions.page,
fetchOptions.rp,
fetchOptions.sortname,
fetchOptions.sortorder)
.ToList()
join a in DataContext.UserAccesses
.Where(x => x.Access.AccessTypeId == 4).ToList() on u.UserID equals a.UserId into accessGroup
select new {};
Can one please help me ? into accessGroup ---> (very important)
First of all you need to understand where your data is coming from. You are loading information from Users_SearchUsers on the one hand and UserAccesses on the other hand. The first query looks like
select <somecolumns>
from users
where <somefilters>;
(you need to use your actual columns and criteria, but Users_SearchUsers is not specified in the question at all). I have ignored paging here for the sake of simplicity
The second query looks like this:
select *
from user_accesses
where access_type_id = 4;
Let's join the two:
select <someoutercolumns>
from
(
select <someinnercolumns>
from users
where <somefilters>
) t1
join
(
select <someotherinnercolumns>
from user_accesses
where access_type_id = 4
) t2
on t1.user_id = t2.user_id;
These queries are probably not the exact solutions you need, but you want the answers to improve, then improve your question.
The requirement makes sense if the LINQ query is very slow. In that case you will need to refactor it in the following manner:
select <somecolumns>
from users
join user_accesses
on users.user_id = user_accesses.user_id and user_accesses.access_type_id = 4
where <somefilters>;
you can use this code
select *(you can put your columns instead *)
from Users
join UserAccesses
on Users.userid = UserAccesses.userid
where UserAccesses.typeid = 4;

EF (LINQ to SQL) - How to query?

I have the concept of a document that has keyword/s. EF abstracted out the document-keyword joining table to an association.
The structure looks like this
Document: ID (PK)
Document_Keyword: DocumentID (PK), Keyword (PK)
Keyword: Keyword (PK)
I have the requirement to return a list of documents where they contain ALL keywords in a string[]
If I was doing this in SQL it would be similar to below
with t as (
select 'keyword1' KEYWORD union
select 'keyword2'
)
select DocumentID,count(*) from [dbo].[Document_Keyword] p
inner join t on p.KEYWORD = t.KEYWORD
group by DocumentID
having count(*) = (select count(*) from t)
Im struggling to form a linq query that will give me the same result.
I have tried the following LINQ statement however it does returns documents that contain 1 or more of the keywords in the array. I require that documents are only returned if ALL keywords match.
var query = (from k in db.KEYWORD
from b in k.DOCUMENT
join q in arrKeywords //array of string[]
on k.KEYWORD equals q
select new Document()
{
Filename = b.FILENAME,
Description = b.TITLE
});
Any ideas?
Cheers
Jeremy
If I get you well you want entries of which all keywords match exactly, i.e. it doesn't have any other keywords. A way too achieve this is
var kwc = arrKeywords.Count();
var query = from k in db.KEYWORD
let kw = k.DOCUMENT.Select(d => d.KEYWORD)
where kw.All(kw1 => arrKeywords.Contains(kw1))
&& kw.Count() == kwc;
The generated query is still much longer than a hand-coded one would be, but I think the database's query optimizer should be able to handle this.

securing query authorization in linq-to-sql

I have 3 tables: the User table, the Records table and the UserRecords table.
The columns are so:
UserTable
UserID | OtherUserDataFields
RecordsTable
RecordID | OtherRecordDataFields
UserRecords
UserID | RecordID
The UserRecords table tells me which users have authorization on which record. I have a function that updates the RecordsTable by receiving 2 paramaters: a record TheRecord (which contains a RecordID field) and a UserID.
I'm writing a query in the data context MyDC to fetch the record with the RecordID supplied in the parameter and test if the user is authorized on that record like this:
var RecordToUpdate = (
from r in MyDC.RecordsTable
from u in MyDC.UserRecords
where r.RecordID == TheRecord.RecordID && TheRecord.RecordID == u.RecordID
where u.UserID == TheUserID
select r).SingleOrDefault();
Will this ensure me that only records the user is authorized will be fetched? I want to avoid cases where users maliciously send a record they're not authorized on and make changes to these unauthorized records.
Thanks for your advice.
I agree with Anand, you will need a linq query:
var filterUserRecord = from u in MyDC.UserRecords
where u.UserID == TheUserID
select u;
var q1 = from r in MyDC.RecordsTable
where r.RecordID = TheRecordID
where filterUserRecord.Any(f => f.RecordID == r.RecordID)
select r;
This will be converted to the SQL query like the following:
SELECT * FROM RecordsTable rt WHERE rt.RecordID = TheRecordID AND EXISTS
(SELECT recordId FROM UserRecords ur WHERE ur.userId = TheUserID AND ur.recordID = rt.recordID)
Note that those are IQueryable<T>s and linq queries on it will produce another IQueryable<T> which will contain expressions to be wholly translated to SQL (or whatever the backend is) instead of naively evaluating it at client side.
Well I think your problem could be solved by subquery
sql:
select * from RecordsTable where recordId in
(select recordId from UserRecords where userId = #someUserId)
It could be represented in Linq as following
var filterUserRecord = from u in MyDC.UserRecords
where u.UserID == TheUserID
select u
var q1 = from r in MyDC.RecordsTable
where filterUserRecord.Any(f => f.RecordID == r.RecordID)
Details for subquery in Linq - Read from here
Perhaps read the article here which discusses how SQL Injection (which I assume should be the main security concern here) is handled in LINQ scenarios.
There is also a nice article here regarding Microsoft's security considerations for EF. It's worth a read for anyone developing with these tools!
[Edit] With regards to your last comment, you can use queries similar to those already on this page. To condense a little: If your database is normalised, to the extent that the RecordId is a unique primary key, you can bypass joins to make a query which reads a little better:
var targetRecords =
from userRecords in MyDC.UserRecords
where userRecords.UserTable.UserID == TheUserID
&& userRecords.RecordsTable.RecordID == TheRecord.RecordID
select userRecords;
var targetRecordsResult = targetRecords.SingleOrDefault();
I have separated the query from it's result 'var' here to point out that 'targetRecords' does NOT get evaluated until you call SingleOrDefault on it in order to assign to targetRecordsResult. You can of course wrap this into one statement if you wish.
If, as mentioned, your RecordID is a Unique Primary Key, you will either get the matching Record back, or null. Note that if this isn't the case, i.e. more than on Record could have the same ID, then the SingleOrDefault call can fail. If your database is designed like that, you would have to use a query more like the one which Anand has specified. That is slightly more verbose, but will return you ANY Record with a matching id for that specific user.
In terms of security, note that your SQL statement will get compiled containing the UserID, making it very difficult to tamper with. Hence my point that in this case, the scope and exposure of UserID is your main concern. If, like you have stated, the user (and any potential malitious user) has no access to the variable (via property exposure etc) then this should be more than suitable for your needs.
Try this:
var RecordToUpdate = (from u in MyDC.UserRecords
where u.UserTable.UserID == TheUserID
and u.RecordsTable.RecordID == TheRecord.RecordID).SingleOrDefault();
This will return the result for query which has specified UserID and RecordID.

NHibernate: hql to criteria query - help needed

I have this hql query, which works perfect:
select m
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
group by m.Uid
But now it needs to be parameterized/made dynamic, not only the parameters, but also the joins (it is possible to select only from Media, without any joins, and so no *.Uid in will be required in this case).
I dont want to mess around with a StringBuilder instance and build the hql query that way, I would rather like to use the Criteria API, but I cant get a
SELECT m.*
....
GROUP BY m.Uid
query to work with Criteria.
If I add a
Projections.GroupProperty("Uid")
to my query, nhibernate selects
SELECT m.Uid
....
GROUP BY m.Uid
which is of course wrong.
After that, I also need to count the unique rows the query returned, as the result is paged.
So, my other query is quite similiar, but I cant find a Criteria equivalent for
SELECT COUNT(DISTINCT m.Uid)
Here is the HQL:
select count(distinct m.Uid)
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
How can this be done with Criteria API?
Please, (N)Hibernate experts - help me with this, I cant find a working solution. Any help is greatly appreciated!
Group columns are implicitly returned as result, but you can add more columns. AFAIK, you can return full entities:
var query = session.CreateCriteria(typeof(Media), "m")
.Add(Projections.GroupProperty("m"))
.Add(Restrictions.NotEq("m.Uid", 0));
// dynamically add filters
if (filterProductLines)
{
query
.CreateCriteria("m.Productlines", "p")
.Add(Restrictions.Eq("p.Uid", productLines));
}
// more dynamic filters of this kind follow here...
IList<Media> results = query.List<Media>();
To count the full number of results you can just build up the same query with different projection:
var query = session.CreateCriteria(typeof(Media), "m")
.SetProjection(Projections.CountDistinct("m.Uid"));
// rest of the query the same way as above
long totalNumberOfResults = query.UniqueResult<long>();
I'm getting unsure about the Projections.GroupProperty("m"), you need to try this. If it doesn't work, you could make it an DetachedQuery that only returns ids:
var subquery = DetachedCriteria.For(typeof(Media), "m")
.Add(Projections.GroupProperty("m.Uid"))
.Add(Restrictions.NotEq("m.Uid", 0));
// add filtering
var query = session.CreateCriteria(typeof(Media), "outer")
.Add(Subqueries.PropertyIn("outer.Uid", subquery));
IList<Media> results = query.List<Media>();
This creates a sql query like this:
select outer.* // all properties of Media to create an instance
from Media outer
where outer.Uid in (
select Uid
from media m
where // filter
)
var count = session.CreateCriteria(typeof(Media))
// Add other criterias...
.SetProjection(Projections.CountDistinct("Id")) // or whatever the id property of Media class is called
.UniqueResult<long>();
As to your GROUP BY question, the query:
SELECT m.*
....
GROUP BY m.Uid
makes no sense because you need to select only columns that appear in the group by clause or aggregate functions. Could you elaborate a little more as to what exactly are you trying to achieve?

Categories

Resources