Inner Join Between Many to Many Relationship Models - c#

I have two model like below that are configured as many to many relationship:
public class Permission
{
public int PermissionId { get; set; }
public string PermissionName { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public string Name { get; set; }
public string ID { get; set; }
public virtual List<Permission> Permissions { get; set; }
}
I want to do an Inner Join with Linq. I can do that easily in SQL since the join table is present there. But how can I do this with linq? Below is what I could do so far:
from pr in Permissions
join role in Roles on pr.Roles.Select(s => s.RoleId).FirstOrDefault() equals role.RoleId
select new { pr.PermissionName, role.RoleId }
As you can see above the FirstOrDefault will ruin the result but other than that I cannot get to compile the query without errors.
Below is the query I am trying to write in Linq:
SELECT P.PermissionName, R.RoleId
FROM Permissions AS P
INNER JOIN PermissionRoles AS PR ON P.PermissionId = PR.Permission_PermissionId
INNER JOIN Roles AS R ON PR.Role_RoleId = R.RoleId
As you can see, an inner join is made with the join table thus query works as expected
Any help is appreciated.

The easiest syntax is
from p in context.Permissions
from r in p.Roles // notice the p !
select new { p.PermissionName, r.RoleId, Role = r.Name, etc... }
EF will produce SQL with the required inner joins.
The fluent equivalent is
Products.SelectMany(p => p.Roles,
(p, r) => new
{
p.PermissionName,
r.RoleId,
...
})
You'll probably agree that the first form, "comprehensive syntax", wins.

var x = from pr in Permissions
from role in Roles
where pr.Roles.Exists(r => r.RoleId == role.RoleId)
select new { pr.PermissionName, role.RoleId };

Related

LINQ to SQL and LINQ to Entity Join with static AND condition

I'm trying to convert the following Join statement into LINQ TO SQL or LINQ to Entity. I know how to join tables in either implementation; but, i'm struggling with the AND clause in the Join statement.
SELECT DISTINCT
p.LastName,
p.FirstName
FROM
dbo.Patient p INNER JOIN dbo.FormPat fp ON p.PatientID = fp.PatientID
INNER JOIN dbo.TxCyclePhase tcp ON fp.TxCyclePhase = tcp.TxCyclePhaseID AND tcp.Type = 2
As far as LINQ to SQL is concerned, I have the followings:
var query = (from p in Context.Set<Patient>().AsNoTracking()
join fp in Context.Set<PatientForm>().AsNoTracking() on p.Id equals fp.PatientId
join tcp in Context.Set<TxCyclePhase>().AsNoTracking() on new { fp.TxCyclePhaseId, seconProperty = true } equals new { tcp.Id, seconProperty = tcp.Type == 2 }
select new
{
p.FirstName,
p.LastName,
}).Distinct();
However, I'm getting an ArgumentNullException on the second join statement.
For the LINQ to Entity, I have the followings, however, this is giving me a distinct IQueryable of FormPat, instead of Patient.
var patients = Context.Set<Patient>().AsNoTracking()
.SelectMany(p => p.Forms)
.Where(fp => fp.Phase.Type == 2)
.Distinct();
As far as the LINQ to Entity is concerned, I was able to figure it out. I'd still like to know how to do it in LINQ to SQL tho.
I'm using the EF fluent API. My Patient object looks like:
public Patient()
{
Programs = new HashSet<Program>();
}
public virtual ICollection<PatientForm> Forms { get; set; }
My PatientForm object looks like:
public class PatientForm
{
public int FormId { get; set; }
public Patient CurrentPatient { get; set; }
public TxCyclePhase Phase { get; set; }
}
And the CyclePhase object looks like:
public TxCyclePhase()
{
this.FormPats = new HashSet<PatientForm>();
}
public int Id { get; set; }
public virtual ICollection<PatientForm> FormPats { get; set; }
In the entity configurations, I have the relationships set. So, in the repository, all I have to do is to use the Any() function when selecting the Patient forms.
var patients = Context.Set<Patient>().AsNoTracking()
.Where(p => p.Forms.Any(f => f.Phase.Type == 2))
.Distinct();

Linq query with first or default join

I have the following data model:
public class Course
{
public int CourseId { get; set; }
public int StateId { get; set; }
}
public class CompletedCourse
{
public int CompletedCourseId { get; set; }
public int UserId { get; set; }
public Course Course { get; set; }
public string LicenseNumber { get; set; }
}
public class License
{
public int LicenseId { get; set; }
public int UserId { get; set; }
public int StateId { get; set; }
public string LicenseNumber { get; set; }
}
I'm trying to come up with an IQueryable for CompletedCourses and I would like to populate CompletedCourse.LicenseNumber with the LicenseNumber property of the FirstOrDefault() selection from my Licenses table where UserId and StateId match the completed course records.
Here is my query, but I don't think this will handle duplicate licenses correctly:
var entries =
(from course in context.CompletedCourses
join license in context.Licenses on course.UserId equals license.UserId
where license.StateId == course.Course.StateId
select course)
.Include(x => x.Agent)
.Include(x => x.Course.State);
Is this something that can be done in a single query? Thanks in advance.
Here is how you can do that:
var entries =
(from course in context.CompletedCourses
join license in context.Licenses
on new { course.UserId, course.Course.StateId }
equals new { license.UserId, license.StateId }
into licenses
let licenseNumber = licenses.Select(license => license.LicenseNumber).FirstOrDefault()
select new { course, licenseNumber });
But please note that with this type of projection you cannot have Includes in your query (you can, but they will not be in effect).
The EF generated query I'm getting from the above is:
SELECT
[Extent1].[CompletedCourseId] AS [CompletedCourseId],
[Extent1].[UserId] AS [UserId],
[Extent1].[LicenseNumber] AS [LicenseNumber],
[Extent1].[Course_CourseId] AS [Course_CourseId],
(SELECT TOP (1)
[Extent2].[LicenseNumber] AS [LicenseNumber]
FROM [dbo].[Licenses] AS [Extent2]
INNER JOIN [dbo].[Courses] AS [Extent3] ON [Extent3].[StateId] = [Extent2].[StateId]
WHERE ([Extent1].[Course_CourseId] = [Extent3].[CourseId]) AND ([Extent1].[UserId] = [Extent2].[UserId])) AS [C1]
FROM [dbo].[CompletedCourses] AS [Extent1]
It can be noticed that EF effectively ignores the join, so the same result can be obtained by simple natural query:
var entries =
(from course in db.CompletedCourses
let licenseNumber =
(from license in db.Licenses
where license.UserId == course.UserId && license.StateId == course.Course.StateId
select license.LicenseNumber).FirstOrDefault()
select new { course, licenseNumber });
#IvanStoev's answer was very helpful in joining on anonymous types, but ultimately I couldn't use it because I needed Includes. Here is the solution I went with that results in two DB queries instead of one which is fine for my situation.
var entries = context.CompletedCourses
.Include(x => x.Agent)
.Include(x => x.Course);
var courses = entries.ToList();
var courseIds = entries.Select(x => x.CompletedCourseId);
var licenses =
(from course in entries
join license in context.Licenses
on new { course.AgentId, course.Course.StateId }
equals new { AgentId = license.UserId, license.StateId }
where courseIds.Contains(course.CompletedCourseId)
select license);
foreach (var course in courses)
{
var license = agentLicenses.FirstOrDefault(x => x.UserId == course.AgentId &&
x.StateId == course.Course.StateId);
if (license != null)
{
course.LicenseNumber = license.LicenseNumber;
}
}
return courses;

convert Sql with inner select to Linq

How to convert this Sql query to LinQ
SELECT *
FROM
users
JOIN products
ON users.id = products.user_id
WHERE users.id IN (
SELECT id
FROM users
WHERE users.status = 'Online'
)
Well, your sql is too complicated (the subquery is useless), so simplify it, and you'll get an easy linq...
from u in users
join p in products on u.id equals p.user_id
where u.status == "Online"
select new{u, p};
your sql could just be
select *
from users u
join products p on u.id = p.user_id
where u.status = 'Online'
You don't need an inner SELECT at all
SELECT *
FROM
users
JOIN products
ON users.id = products.user_id
WHERE
users.status = 'Online'
Now, converting this to a LINQ query is a piece of cake.
Note: You would need an IN-clause with a nested SELECT if you were querying another table that, when joined, would produce more output rows, because it has an 1 : n relation to one of the tables already included in the query.
I assume you have these entities:
class User {
public int Id { get; set; }
public string Status { get; set; }
public IList<Product> Products { get; set; }
}
class Product {
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
which there is a one-to-many relationship from user to product. So we have:
var result =
from user in users
join product in products on user.Id equals product.UserId
where user.Status == "online"
select new { user, product };

Retrieving the username from MembershipUser for a join query

I'm newer using C#, linq. I'm trying to add the UserName into a query to show it as part of a DataSource of a ListView, I have tested several way to joined, but always I m'receiving next error:
"Unable to create a constant value of type 'Web.Admin.system.User'. Only primitive types or enumeration types are supported in this context."
My code is:
//Entities
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public string Description { get; set; }
public Guid CategoryId { get; set; }
public Guid UserId { get; set; }
}
//class added just for getting the user list (possibly, I do not need)
public class User
{
public Guid Id { get; set; }
public String Name { get; set; }
}
Here is my code preparing the filter
//retrieve the data from Order and Category
IQueryable<Order> orders = orderService.GetAllOrders();
IQueryable<Category> category = categoryService.GetAllCategories();
//obtain the users
MembershipUserCollection members = Membership.GetAllUsers();
// 1st option for managing users directly with memberShip variable
var memberShip = members.Cast<MembershipUser>().ToDictionary(m => m.ProviderUserKey, m => m.UserName).AsQueryable();
// 2nd option, I have added this code to see if I could manage the users as a list
List<User> users = new List<User>();
foreach (var _member in memberShip)
{
users.Add(new User { Id = (Guid)_member.Key, Name = _member.Value });
}
//Getting information to fill a listview
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
join u in users on i.UserId equals u.Id // 1st I tried to use memberShip directly but gave me error of types
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = u.Name
};
ListViewOrders.DataSource = DDLsource.ToList();
Here is where the Error is triggered, I'm trying to understand the error and do other solution, I tested the query like:
Example 2
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = (from u in users where u.Id == i.UserId select u.Name)
};
Example 3
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
join u in Membership.GetAllUsers().Cast<MembershipUser>() on i.UserId equals ((Guid)u.ProviderUserKey)
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = u.UserName
};
all with the same results, could someone give me a hand with my mistake will surely be very obvious. Thanks in advance
I would do something like this (sorry, untested code...):
var DDLsource =
from i in orders
join c in category on i.CategoryId equals c.Id
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
i.UserId,
UserName = ""
};
foreach(var ddl1 in DDLsource)
ddl1.UserName = Membership.GetUser(ddl1.UserId).Name;

traversing one to many relationships in entity framework

I am having trouble figuring out how to traverse a one to many relasionship using LINQ-To-SQL in my asp.net site that uses EF 5. I have made the relationships in the class files but when I try to go from parent to child in my where clause I am not given a list of the child columns to filter on. Can anyone tell me what is wrong with my code, I am new to EF and LINQ.
Product.cs:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Category Category { get; set; }
}
}
Category.cs:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
Codebehind:
using (var db = new Compleate())
{
rpBooks.DataSource = (from c in db.Categories
where c.Products.Name == "Books"
select new
{
c.Name
}).ToList();
}
Do you want all products in the books category?
from p in db.Products
where p.Category.Name == "Books"
select new
{
p.Name
}
Or do you want to have all categories that contain products that are called called books?
from c in db.Categories
where c.Products.Contains( p => p.Name == "Books")
select new
{
c.Name
}
BTW, if you're only selecting the name, you can skip the anonymous type in the select part...
select p.name
Ok I had to update the codebhind to look like:
using (var db = new Compleate())
{
rpBooks.DataSource = (from c in db.Categories
join p in db.Products on c.ID equals p.id
where c.Products.Name == "Books"
select new
{
c.Name
}).ToList();
}
It should be name = c.Name it's not an issue with traversing, it's an issue with syntax, read the brief article on anonymous types here

Categories

Resources