SQL-statement with JOIN to LINQ-statement - c#

How would you write the following SQL as a Linq-statement?
SELECT *
FROM Projekt i
LEFT JOIN Projekt j on i.name=j.id

If you have for example these two classes:
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
}
public class Address
{
public int ID { get; set; }
public string AddressLine { get; set; }
}
then with Method Syntax(MS) for LEFT OUTER JOIN we'll have this code below:
var JoinedTables = context.Employee.GroupJoin(Address,
emp => emp.AddressId,
add => add.ID,
(emp, add) => new { emp, add })
.SelectMany(x => x.add.DefaultIfEmpty(),
(employee, address) => new { employee, address }))
.ToList();
But with Query Syntax(QS)
which is more understandable for humans
for LEFT OUTER JOIN again all you have to do is:
var JoinedTables = (from emp in Employee
join add in Address
on emp.AddressId equals add.ID
into EmployeeAddressGroup
from address in EmployeeAddressGroup.DefaultIfEmpty()
select new { employee, address }).ToList();

Related

Hierarchical select in Entity Framework with lambda expression

I have a relational dataset as dummy. I want to take the data as hierarchical (Role > SubRoles > Permissions) then I will convert to JSON but I get an exception:
Error CS0266
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
Thanks for your answer.
Model classes:
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public List<SubRole> SubRoles { get; set; }
}
public class SubRole
{
public int Id { get; set; }
public string Name { get; set; }
public string EndPoint { get; set; }
public List<Permission> Permissions { get; set; }
}
public class RoleSubRole
{
public int Id { get; set; }
public int RoleId { get; set; }
public int SubRoleId { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubRolePermission
{
public int Id { get; set; }
public int SubRoleId { get; set; }
public int PermiisonId { get; set; }
}
public class RoleModel
{
public Role Role { get; set; }
}
Program class:
public static void Main(string[] args)
{
RoleModel roleModel = new RoleModel()
{
Role =
(from u in DataSet.Users
join r in DataSet.Roles on u.RoleId equals r.Id
where u.Id == 1
select new Role
{
Name = r.Name,
SubRoles =
(from rsb in DataSet.RoleSubRoles
join sr in DataSet.SubRoles on rsb.SubRoleId equals sr.Id
where r.Id == rsb.RoleId
select new SubRole
{
Name = sr.Name,
EndPoint = sr.EndPoint,
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
})
})
})
};
}
You should use
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
}).ToList()
The Permissions is a List<T>, while the query returns an IEnumerable<T>. Same is the case with SubRoles.You need to convert to List<T>, which could be done using the ToList() method
Complete Query
Role =
(from u in DataSet.Users
join r in DataSet.Roles on u.RoleId equals r.Id
where u.Id == 1
select new Role
{
Name = r.Name,
SubRoles =
(from rsb in DataSet.RoleSubRoles
join sr in DataSet.SubRoles on rsb.SubRoleId equals sr.Id
where r.Id == rsb.RoleId
select new SubRole
{
Name = sr.Name,
EndPoint = sr.EndPoint,
Permissions =
(from srp in DataSet.SubRolePermissions
join p in DataSet.Permissions on srp.PermiisonId equals p.Id
where srp.SubRoleId == sr.Id
select new Permission
{
Name = p.Name
}).ToList()
}).ToList()
}).First()
};
Also note that the Role represents a single entity, while the query returns a collection. You need to make a choice on which entity from the collection needs to be stored. For the sample code above, I have used First()

LINQ - SQL Script Changing based on JOIN Order

This is just strange behavior to me.
LINQ transforming my statements creating bizarre execution plans. It's adding sub-queries when I don't ask it to and it's including or excluding joins in this sub-query based on the order of my joins, which leaves me scratching my head. At this point it's impossible for me to optimize this script based on the unpredictable nature of LINQ to Entities.
SETUP
//define global filter
Expression<Func<Contract_SeqExecution, bool>> globalFilter = r => r.ModifiedByFirstName.Contains("w");
...
//extend global filter
Expression<Func<Contract_SeqExecution, bool>> descendantFilter = x => x.client_id == 1 && x.project_status == true && x.parentId != null;
descendantFilter = descendantFilter.And(globalFilter);
//query
IQueryable<Contract_SeqExecution> geDescendantResults = c.queryGroups(this);
//execute
geDescendantResults.AsExpandable().Where(descendantFilter).Dump();
LINQ QUERY
public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
{
var result = (from ge in context.group_execution
join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
from modify_u in modify_uSub.DefaultIfEmpty()
join p in context.project on aseq.project_id equals p.id
join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
join es in context.execution_schedule on ge.schedule_id equals es.id
join exe_u in context.users on ge.executed_by_id equals exe_u.id
join create_u in context.users on aseq.created_by_id equals create_u.id
select new Contract_SeqExecution
{
client_id = p.client_id,
project_status = p.status,
ID = ge.id,
Name = aseq.name,
ModifiedByFirstName = modify_u.first_name,
ModifiedByLastName = modify_u.last_name,
FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id
});
return result;
}
If I reorder the joins in the above statement my execution plan changes and LINQ-to-entities decides I want a sub-query....sigh.
Seriously all I did was change the order of the joins in the images below. Drastically different execution plans which will later on hurt performance of the query. What gives? Am I missing something obvious?
Is it picking information up from the entity framework schema? Could it be missing FK definitions or indexes that are driving this crazy behavior? At this point I'm just shooting in the dark. Hopefully someone here can shed some light on this.
Below is the class Contract_SeqExecution. I'm currently running this in a LinqPad C# Program hitting my DAL dll, linq-to-entities.
public class Contract_SeqExecution
{
public int ID { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public int SeqID { get; set; }
public int CaseGroupInd { get; set; }
public string CaseGroupText { get; set; }
public string ExecRatio { get; set; }
public string ModifiedByFirstName { get; set; }
public string ModifiedByLastName { get; set; }
public string Project { get; set; }
public string Uploaded { get; set; }
public string UploadRatio { get; set; }
public bool FailedInd { get; set; }
public bool HoldInd { get; set; }
public int? parentId { get; set; }
public int? patriarchId { get; set; }
public DateTime? SchedRunTime { get; set; }
//other fields
public string Machine {get;set;}
public int is_accepting_changes {get;set;}
public string holding_at_name {get;set;}
public string RunStatus {get;set;}
public string CaseGroupStatus {get; set;}
public string TCStatusColor {get; set;}
public string ExecutedBy {get;set;}
public string CreatedBy {get;set;}
public int? ScheduleID {get;set;}
public bool SeqUploaded {get;set;}
//end other fields
public int? parent_group_exec_id { get; set; }
public int client_id { get; set; }
public bool project_status { get; set; }
public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
{
var result = (from ge in context.group_execution
join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
join es in context.execution_schedule on ge.schedule_id equals es.id
join exe_u in context.users on ge.executed_by_id equals exe_u.id
join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
join p in context.project on aseq.project_id equals p.id
join create_u in context.users on aseq.created_by_id equals create_u.id
join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
from modify_u in modify_uSub.DefaultIfEmpty()
select new Contract_SeqExecution
{
client_id = p.client_id,
project_status = p.status,
ID = ge.id,
Name = aseq.name,
ModifiedByFirstName = modify_u.first_name,
ModifiedByLastName = modify_u.last_name,
FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id,
});
return result;
}
}
UPDATE
So I took Andrew's advice and looked into doing this the more "LINQ" way. I like to include FKs in my entities because I feel it's a more natural way to write SQL, habits die hard. I went ahead and rewrote my LINQ expression and the results are now consistent.
Example:
var result = (from ge in context.group_execution
//join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
//join es in context.execution_schedule on ge.schedule_id equals es.id
//join exe_u in context.users on ge.executed_by_id equals exe_u.id
//join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
//join p in context.project on aseq.project_id equals p.id
//join create_u in context.users on aseq.created_by_id equals create_u.id
//join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
//from modify_u in modify_uSub.DefaultIfEmpty()
select new Contract_SeqExecution
{
//client_id = p.client_id,
//project_status = p.status,
ID = ge.id,
Name = ge.automation_sequences.name,
ModifiedByFirstName = ge.automation_sequences.modified_by_user.first_name,
ModifiedByLastName = ge.automation_sequences.modified_by_user.last_name,
//FailedInd = asstatus.fail_alert_ind,
parentId = ge.parent_group_exec_id,
patriarchId = ge.patriarch_id,
});
return result;

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;

How can I group by a navigation property member in linq to sql?

I have three tables holding Users Groups and their association, UserGroups as laid out in this fiddle:
I am trying to obtain the maximum level among the users' groups as shown in the query in the fiddle using linq2sql.
However, EntityFramework obfuscates the join table, TblUserGroup and instead just gives me the navigation properties: TblGroups.Users or User.TblGroups
This is what I have put together thus far but Linqpad tells me it cannot execute:
var maxGroup = from ua in ctx.TblGroups
group ua by ua.TblUsers.Select(s=>s.UserId)
into g
select new
{
UserId= g.Key,
MaxLevel = g.Max(s => s.GroupLevel)
};
Seems you can do it like this:
var result = users.Select(u => new
{
UserId = u.Id,
MaxLevel = u.Groups.Max(g => g.GroupLevel)
});
Having:
class User
{
public int UserId { get; set; }
public string UserName { get;set; }
public List<Group> Groups { get; set; }
}
class Group
{
public int GroupId { get; set; }
public string GroupName { get; set; }
public int GroupLevel { get; set; }
public List<User> Users { get; set; }
}
Does it work for you?
var maxGroup = ctx.TblUsers
.Where(u => u.TblUserGroups != null)
.Select(u => new
{
UserId = u.UserId,
MaxGroupLevel = u.TblUserGroups.TblGroups.Max(g => g.GroupLevel)
}
);

Given a list of items, each containing a subitem, get the subitems and remove duplicates

Given a list of groupos, where each groupo has a single empresa and multiple groupos can have the same empresa, how do you get the empresas that contain any of the list's groupos?
I have this Model:
public class Grupo
{
public int id { get; set; }
public string descripccion { get; set; }
[ForeignKey("Empresas")]
public int empresa { get; set; }
public virtual empresa Empresas { get; set; }
}
public class empresa
{
public int id { get; set; }
public string descripcion { get; set; }
public virtual ICollection<Grupo> Grupos { get; set; }
}
So this method gives me a List
private List<Grupo> VerEmpresas(int userId)
{
var lista = (from ga in db.GrupoAccesos
join g in db.Grupos
on ga.grupo equals g.id
where ga.usuario == userId
select g).ToList();
return lista;
}
and now I want to use this method to show me the empresas that are related to grupo.
Below emp gives a bool, and instead I want all the empresas that are in my list of grupos.
List<Grupo> verEmpresa = VerEmpresas(1);
var emp = (from p in db.Empresas
select p.Grupos).Contains(verEmpresa);
ViewBag.empresa = new SelectList(emp, "id", "descripcion");
You can try getting the empresa ids from your Grupo list and using the foreign key relationship to get the empresas:
var empresaIds = verEmpresa.Select( v => v.empresa ).Distinct().ToList();
var emp = from p in db.Empresas
where empresaIds.Contains( p.id )
select p;
In your code you are passing 'VerEmpresa' which is a list of Grupo. You should pass one object of Grupo from that list. Try using Any and All method on verEmpresa List.
Try something like:
from p in db.Empresas
where verEmpresa.Any(val => p.Contains(val))
select p;
If all you want is the empresas, try:
var emp = (from e in db.Empresas
from g in db.Grupos
where e.Grupos.Contains(g)
select e);

Categories

Resources