I would achieve this goal using EFCore method syntax
Given an user retrieve all roles and set IsInRole = 1 if the given user has the role, otherwise 0
in tsql is something like
CREATE TABLE USER(USERNAME VARCHAR(200) PRIMARY KEY);
CREATE TABLE ROLE(ROLENAME VARCHAR(200) PRIMARY KEY);
CREATE TABLE USERROLE(USERNAME VARCHAR(200), ROLENAME VARCHAR(200));
INSERT INTO USER VALUES('LELU');
INSERT INTO ROLE VALUES('ROLE1');
INSERT INTO ROLE VALUES('ROLE2');
INSERT INTO USERROLE VALUES('LELU', 'ROLE1');
SELECT R.ROLENAME, CASE WHEN T.ROLEID IS NULL THEN 0 ELSE 1 END ISINROLE
FROM ROLE R
LEFT JOIN (
SELECT UR.ROLENAME ROLEID
FROM USERROLE UR
WHERE UR.USERNAME='LELU'
) T ON T.ROLEID=R.ROLENAME
ROLENAME ISINROLE
-------------------------------------
ROLE1 1
ROLE2 0
I would like to achive this goal using EF core without navigation properties.
I have the following entities
public class User
{
[Key]
public string Username {get; set;}
}
public class Role
{
[Key]
public string Rolename {get; set;}
}
public class UserRole
{
public string Username {get; set;}
public string Rolename {get; set;}
}
where UserRole (Username, Rolename) are primary keys
public class MyDbContext : DbContext
{
public DbSet<User> Users {get; set;}
public DbSet<Role> Roles {get; set;}
public DbSet<UserRole> UsersRoles {get; set;}
...
}
I do some research online but i didn't find anything that can accomplish a left join without navigation properties.
How can I achive this goal without using navigation property neither injecting raw query in EFCore?
It is possible without navigation properties. It's not pretty by any stretch and depending on what details you want from the associated entities it will get a lot uglier. However, to get a list of Roles with a flag for whether the provided user is assigned to them or not:
using (var context = new MyDbContext())
{
var roles = context.Roles
.SelectMany(r => context.UserRoles.Where(ur => ur.RoleName == r.RoleName
&& ur.UserName == userName).DefaultIfEmpty(),
(r, ur) => new
{
RoleName = r.RoleName,
IsInRole = ur != null
}).ToList();
}
That would return each role, and whether the provided userName value was assigned to that role using a Left join via the DefaultIfEmpty().
Although LINQ query syntax is generally disliked, I believe it fits the scenario of joining entities without navigation properties very nicely.
var query =
from role in context.Roles
join userRole in context.UserRoles on userRole.RoleName equals role.RoleName
select new
{
...
};
Albeit the left join is not as nice.
var query =
from role in context.Roles
join userRole in context.UserRoles on userRole.RoleName equals role.RoleName into ur
from userRole in ur.DefaultIfEmpty()
select new
{
RoleName = role.RoleName,
IsInRole = userRole != null
};
As a bonus, in case you need to do multiple joins, with LINQ query syntax you only have to add a single additional line describing the join.
Related
I have the following SQL Query which I am trying to translate to LINQ Query
SELECT C.NAME,C.MOBILEPHONE,ISNULL (SUM(P.PAYMENTAMOUNT),0) AS
PAYAMOUNT,BILLAMOUNT ,B.ID,BILLNO , BILLDATE FROM CUSTOMERS C
JOIN BILLS B ON B.CUSTOMERID=C.ID
LEFT JOIN BILLPAYMENTS P ON P.BILLID=B.ID
GROUP BY C.NAME ,B.BILLAMOUNT,B.ID,BILLNO,BILLDATE,C.MOBILEPHONE
HAVING B.BILLAMOUNT> ( ISNULL(SUM(P.PAYMENTAMOUNT),0))
How do you represent this in LINQ ?
I have seen the typical implementation this
var query = from c in db.Customers
join b in db.Bills on c.Id equals b.CustomerId
join p in db.BillPayments on b.Id equals p.BillId into cs
from xx in cs.DefaultIfEmpty()
group xx by new { c.Name, c.MobilePhone, b.BillAmount, b.BillNo, b.Id, b.BillDate } into g
where g.Sum(p => p.PaymentAmount) < g.Key.BillAmount
select new
{
Received = g.Key,
ReceivedTotal = g.Sum(p => p.PaymentAmount)
};
but am unsure how to implement the following :
HAVING B.BILLAMOUNT> ( ISNULL(SUM(P.PAYMENTAMOUNT),0))
So you have a sequence of Customers, where every Customer has zero or more Bills, and every Bill belongs to exactly one Customer: a straightforward one-to-many relation.
Furthermore, every Bill has zero or more BillPayments, where every BillPayment belongs to exactly one Bill, also a one-to-many relation.
Alas you forgot to tell us your classes. If you followed the entity framework code first conventions, you'll have something similar to:
class Customer
{
public int Id {get; set;}
public string Name {get; set;}
...
// every Customer has zero or more Bills (one-to-many)
public virtual ICollection<Bill> Bills {get; set;}
}
class Bill
{
public int Id {get; set;}
public int BillNo {get; set;}
public decimal BillAmount {get; set;}
...
// every Bill belongs to exactly one Customer, using foreign key
public int CustomerId {get; set;}
public virtual Customer Customer {get; set;}
// every Bill has zero or more BillPayments (one-to-many)
public virtual ICollection<BillPayment> BillPayments {get; set;}
}
class BillPayment
{
public int Id {get; set;}
...
// every BillPayment belongs to exactly one Bill, using foreign key
public int BillId {get; set;}
public virtual Bill Bill{get; set;}
// every Bill has zero or more BillPayments (one-to-many)
public virtual ICollection<BillPayment> BillPayments {get; set;}
}
In entity framework, the columns of your table are represented by non-virtual properties, the virtual properties represent the relations between the tables.
You also forgot the requirements of your query. It seems to me, that you want the following:
Give me certain properties of Bills (Id, BillNo, BillDate, BillAmount), with certain properties of the Customer of this Bill (Name and MobilePhone), of all Bills that are not fully paid yet. Or in other words, of all Bills where the sum of all payments is less than the BillAmount.
One of the nice things about entity framework, is that you don't have to do the joins yourself, you can use the virtual properties. Entity Framework knows the relations between the tables and does the proper joins for you.
Just for fun, we'll add the original BillAmount, the AmountPaid, and the RemainingAmount, so you can tell your Customer how much he still has to pay when you phone him on his mobile phone
In the requirement you see the central role of Bills, so let's use that as starting point:
// (1) from all bills, calculate the AmountPaid; remember the original bill data:
var notFullyPaidBills = myDbContext.Bills
.Select(bill => new
{
BillData = bill,
AmountPaid = bill.BillPayments
.Select(billPayment => billPayment.PaymentAmount)
.Sum(),
})
// (2) Keep only those bills that are not fully paid yet
.Where(bill => bill.Bil.BillAmount > bill.AmountPaid)
// (3) from the remaining bills select the required properties:
.Select(bill => new
{
// Customer properties:
CustomerName = bill.BillData.Customer.Name,
MobilePhone = bill.BillData.Customer.MobilePhone,
// bill properties:
BillId = bill.BillData.Id,
BillNo = bill.BillData.BillNo,
BillDate = bill.BillData.Date,
// Amounts:
BillAmount = bill.BillData.BillAmount,
AmountPaid = bill.AmountPaid,
RemainingAmount = bill.BillData.BillAmount - bill.AmountPaid,
});
See? When using the virtual properties of your entity framework classes, the queries will look much simpler and more intuitive than when you are doing the (group)joins yourself.
I am trying to get all users with role names, but query miserable fails with error:
Additional information: Invalid column name 'AppRoleId1'.
My query is:
var query = this.context.Users
.Include(u => u.Roles)
.ThenInclude(r => r.Role)
.OrderBy(u => u.DisplayName);
I override IdentityUser, IdentityUserRole ... and add property Role to AppUserRole class so ThenInclude could work:
[Table("UserRoles")]
public partial class AppUserRole : IdentityUserRole<int>
{
public AppUser User { get; set; }
public AppRole Role { get; set; }
public AppUserRole() { }
public AppUserRole(AppUser user, AppRole role)
{
this.RoleId = role.Id;
this.UserId = user.Id;
}
}
Entity Framework generates this SQL:
SELECT [u3].[RoleId], [u3].[UserId], [u3].[AppRoleId1], [u3].[AppUserId], [r].[Id], [r].[ConcurrencyStamp], [r].[Name], [r].[NormalizedName]
FROM [UserRoles] AS [u3]
INNER JOIN (
SELECT DISTINCT [u].[DisplayName], [u].[Id]
FROM [Users] AS [u]
) AS [u4] ON [u3].[UserId] = [u4].[Id]
LEFT JOIN [Roles] AS [r] ON [u3].[AppRoleId1] = [r].[Id]
ORDER BY [u4].[DisplayName], [u4].[Id]
which is odd (where on earth did it get AppRoleId1 column?).
I think this should work, also based on this thread, but it is not. Why? Is there a bug in Entity Framework?
Edited:
I did migrations (dotnet ef migrations add ...) and error is gone, but result is null for query.Single().Roles[0].Role:
I have the following two entity sets representing Dept & Emp:-
public partial class Dept
{
public Dept()
{
this.Emps = new HashSet<Emp>();
}
public int DeptID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Emp> Emps { get; set; }
}
public partial class Emp
{
public int EmpID { get; set; }
public string Fname { get; set; }
public string Lname { get; set; }
public int DeptID { get; set; }
public virtual Dept Dept { get; set; }
}
Now I wrote this test action method , which will try to delete a dept which have one Emp assigned to it, as follow:-
public ActionResult Test()
{
Dept d = db.Depts.SingleOrDefault(a=>a.id ==1);
Emp e = db.Emps.SingleOrDefault(a => a.id == 100);
db.Entry(d).State = EntityState.Deleted;
db.Emps.Remove(e);
db.SaveChanges();
return Content("done");
}
I thought that an exception will be raised when calling this action method, since the Dept with id=1 already has one emp with id=100. But what happened is that EF has removed the emp first, then the dept. As a final result the above action method, deleted both the dept with id=1 and emp with id =100.. so can you advice on this please? Bearing in mind that if I try to delete the Dept only as follows:
public ActionResult Test()
{
Dept d = db.Depts.SingleOrDefault(a=>a.id ==1);
//Emp e = db.Emps.SingleOrDefault(a => a.id == 100);
db.Entry(d).State = EntityState.Deleted;
//db.Emps.Remove(e);
db.SaveChanges();
return Content("done");
}
I will get the following exception:-
The DELETE statement conflicted with the REFERENCE constraint \"FK_Emp_ToTable\". The conflict occurred in database \"C:\USERS\...\DOCUMENTS\VISUAL STUDIO 2013\PROJECTS\WEBAPPLICATION19\WEBAPPLICATION19\APP_DATA\DATABASE1.MDF\", table \"dbo.Emp\", column 'DeptID'.\r\nThe statement has been terminated."}
So can anyone advice on how EF is implementing these scenarios?
EDIT
i check the sql profiler for my action method and i noted the following 2 delete sql statments:-
exec sp_executesql N'delete [dbo].[Emp]
where ([EmpID] = #0)',N'#0 int',#0=100
exec sp_executesql N'delete [dbo].[Dept]
where ([DeptID] = #0)',N'#0 int',#0=1
so it have deleted the emp first then the dept,, so how EF determine the order , you mentioned it is smart enough to know ,, but what govern this behavior ?
You tried to delete a Dept which has a collection of Emp assigned to it.
The following exception occurred
The DELETE statement conflicted with the REFERENCE constraint
That means that there's a constraint to the Dept - Emp relationship.
My guess is that it's an one - to many relationship, with one Dept being optional to Emp.
I can tell it's optional because the foreign key DeptID is a Nullable<int>.
When you try to delete a Dept, you get an exception because dept is referenced in Emp.
In the first action method you deleted by primary key
Dept d = db.Depts.SingleOrDefault(a=>a.id ==1);
Emp e = db.Emps.SingleOrDefault(a => a.id == 100);
And then used db.Emps.Remove(e); to mark the relationship between as deleted.
If the relationship was optional, Emp would be set to Null using only an SQL Update.
In your case i see two SQL Delete statements were called
The relationship is therefor identifying.
When a primary key of the principal entity is also part of the primary key of the dependent entity, the relationship is an identifying relationship. In an identifying relationship the dependent entity cannot exist without the principal entity. This constraint causes the following behaviors in an identifying relationship:
Deleting the principal object also deletes the dependent object. This is the same behavior as specifying in the model for the relationship.
Removing the relationship deletes the dependent object. Calling the Remove method on the EntityCollection marks both the relationship and the dependent object for deletion.
I'm trying to find out how I would define the code first navigation properties on these two classes to perform something similiar to this query:
SELECT USERID, FIRSTNAME, LASTNAME, COURSEID, NAME
FROM User
LEFT OUTER JOIN Course ON User.USERID = Course.USERID
WHERE COURSEID = 1
So I'm trying to find a list of users together with if they have attended a certain course.
public class User
{
public int UserId {get;set; }
public string FirstName {get;set;}
public string LastName {get;set;}
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseId { get;set; }
public int UserId { get;set; }
public string Name { get;set; }
public virtual User User {get;set;}
}
If I was to write a query to achieve this
var u = Users.Where(x => x.Courses.Any(x => x.CourseId = 1));
This does a subquery, which is not what I wanted (as people who didnt attend the course would not show).
How would the navigation property be defined?
HasMany(t => t.Courses).WithOptional(t => t.User).HasForeignKey(t => t.UserId);
Check this link:
http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
Left outer joins in LINQ are done via DefaultIfEmpty method.
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any()
} );
For specific course id,
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any( c => c.CourseID == 1 )
} );
Sub query is the only way to write related queries, However, EF will choose the best suitable join type and will give you correct results. And EF can manage mostly all kinds of queries without doing joins.
I'm pretty sure it's something regarding hidden conventions, but I always get an error when trying to map a many-to-many relation to an existing database.
Here is the simplest example:
[Table("ALRole", SchemaName = "AL")]
public class Role
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("ALUser", SchemaName = "AL")]
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
I got the usual three tables in the db: the first two are obvious, and the third is created with this script:
CREATE TABLE AL.ALUsersRoles
(
RoleID int NOT NULL,
UserID int NOT NULL,
CONSTRAINT PK_ALUserRole PRIMARY KEY(RoleID, UserID),
CONSTRAINT FK_ALUserRole_RoleID FOREIGN KEY(RoleID) REFERENCES AL.ALRole(ID),
CONSTRAINT FK_ALUserRole_UserID FOREIGN KEY(UserID) REFERENCES AL.ALUser(ID)
)
Now I try to map the many-to-many relation, with code like this:
// ...I'm in the EntityTypeConfiguration-derived class (User)
HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.MapLeftKey(u => u.ID, "UserID");
m.MapRightKey(r => r.ID, "RoleID");
ToTable("ALUsersRoles", "AL");
});
I tried all the possibile combinations and variations in this code, but I always get the error:
{"Invalid column name 'Name'.\r\nInvalid ...and so on...
So I think it must be the table that is not created correctly.
Any ideas?
Thanks in advance
Andrea
P.S.: I stripped down some of my code, so maybe there can be some small typo...
well, this works for me same as OP.
//many-to-many between *Users -> Web_User_Rol <- Web_Rol*
modelBuilder.Entity<Users>()
.HasMany(u => u.Web_Rols).WithMany(r => r.Users)
.Map(t=>t.MapLeftKey("user_id")
.MapRightKey("roleID")
.ToTable("Web_User_Rol"));
There is nothing wrong with your object model or fluent API code. I've used them and they perfectly created the desired schema without any exception. I think your problem comes from another entity (perhaps one with a "Name" property) unrelated to what you've shown here. To find that, drop (or rename) your existing database and let Code First create one for you and then compare the 2 databases and see what is different.