I have a table which contains columns among others like Id as Primary Key and LastMeterReadingId (Foreign Key which references to same table) - something like Parent Meter Reading.
I would like to get all rows which are not already used like Parent. I would like to avoid situation when meter reading is parent for more than one meter reading.
I know how to join to same table, but I have no idea how to choose only those records which aren't parent already. That's how query looks like without condition statement.
return (from m in uow.MeterReadingReadWriteRepository.Query()
join parent in uow.MeterReadingReadWriteRepository.Query() on m.Id equals parent.LastMeterReadingId
select new MeterReadingDto()
{
(...)
}).ToList();
Do you have any idea how to achieve it in efficient way?
Regards.
I would like to get all rows which are not already used like Parent
In other words, you want all rows that have no children. Note that the variable name parent in your query is misleading - when you do a join b on a.Id equals b.ParentId, a is the parent and b is the child.
Anyway, there are at least 3 ways to achieve your goal, IMO being equivalent from nowadays database query optimizers point of view (i.e. should be equally efficient):
(1) Using !Any(...) which is equivalent to SQL NOT EXISTS(...):
from m in uow.MeterReadingReadWriteRepository.Query()
where !uow.MeterReadingReadWriteRepository.Query().Any(child => m.Id == child.LastMeterReadingId)
select ...
(2) Using group join:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
where !children.Any()
select ...
(3) Using left outer antijoin:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
from child in children.DefaultIfEmpty()
where child == null
select ...
If this is EF (LINQ to Entities), the first two are translated to one and the same SQL NOT EXISTS based query. While the last is translated to the "traditional" SQL LEFT JOIN ... WHERE right.PK IS NULL based query.
You could just add
where !(from child in uow.MeterReadingReadWriteRepository.Query() where child.Id == m.LastMeterReadingId select child).Any()
Not sure how intelligently this would be optimised though. It would also be better to factor out uow.MeterReadingReadWriteRepository.Query().
Do you not have a Child relationship/collection in your Meter Reading entity from the foreign key constraint? - this would make the query much more straightforward.
var readings = uow.MeterReadingReadWriteRepository.Query();
var parents = readings
.Join(readings, child => child.Id, parent => parent.LastMeterReadingId,
(child, parent) => new {parent.Id})
.Distinct()
.ToDictionary(a => a.Id);
var result = (from m in readings
where !parents.Contains(m.Id)
select new
{
Id = m.Id
}).ToList();
Thanks #Ben Jackson
public class MeterReading : EntityBase
{
public long PropertyId { get; set; }
public long? LastMeterReadingId { get; set; }
public long? PaymentId { get; set; }
public Property Property { get; set; }
public MeterReading LastReading { get; set; }
public Payment Payment { get; set; }
}
That's how most value properties looks like. Maybe should I use T-SQL query with JOIN to CTE which mentioned before condition statement? I'll try your solution ASAP.
Related
Let say I have these 2 Entities
public class Product
{
//..
public ICollection<Photo> Photos { get; set; }
= new List<Photo>();
}
public class Photo
{
//..
public PageLocation PageLocation { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}
I use this code to select a list of products. Each of them will associated with an image given the location where the product will be displayed.
var productList = GetAll().Include(p => p.PhotoImages)
.Where(p =>
p.PhotoImages.Any(
i => i.PageLocation == PageLocation.Home_Slider));
The problem is that when a product has no image for that location, then the product is not selected.
Every examples that I've seen on left join show how to use 2 sources. In my case, I don't need 2 sources because I've a navigation property between Product and Image.
Is there a way to accomplish a left join without having to use 2 sources data? Something like this
SELECT *
FROM [dbo].[Products] p
LEFT JOIN
(
SELECT *
FROM [dbo].[PhotoImages]
WHERE PageLocation = 1
)img
ON p.Id = img.ProductId
Thanks for helping
Well, if you use the navigation property EF will generate a SQL statement that has an inner join though.
How Navigation property works
When we apply navigation property in our code, it means we are asking EF to automatically perform a join between the two tables.
source: Navigation Property With Code First (Navigation Property In EF)
So you could use the example you already found on MS docs - Perform left outer joins - to realize your query. Your SQL example has two sources too. First Products table and as second the PhotoImages table or I misunderstood what you mean.
An approach for your query could be:
var products = (
from p in _context.Products
join pi in _context.PhotoImages on p.Id equals pi.ProductId
into productsWithImages
from pwi in productsWithImages.Where(pwi => pwi.PageLocation == 1).DefaultIfEmpty()
select new ExtendedProduct
{
p.Id,
p.Name,
photoId = (int?)pwi.Id,
pwi.PageLocation,
pwi.Url
}
)
Is there a way to accomplish a left join without having to use 2 sources data?
I don't think there is. In your SQL query you are joining with a new set, not the photos related to your products.
I believe that the best you can do is something like this:
var q = from prod in ctx.Products.Include(p => p.Photos)
join img in (
from ph in ctx.Photos where ph.PageLocation == 1 select new { ph.ProductId, PageLocation = (int?)ph.PageLocation }
) on prod.Id equals img.ProductId into pjoin
from pj in pjoin.DefaultIfEmpty()
select new { prod, photo = pj, pj.PageLocation };
foreach (var p in q)
{
Console.WriteLine($"{p.prod.Id} - {p.photo} - {p.PageLocation}");
}
EDIT: There is only one join in this query, as can be seen from the generated SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[PageLocation] AS [PageLocation],
[Extent2].[ProductId] AS [ProductId]
FROM [dbo].[Product] AS [Extent1]
LEFT OUTER JOIN [dbo].[Photo] AS [Extent2] ON (1 = [Extent2].[PageLocation]) AND ([Extent1].[Id] = [Extent2].[ProductId])
I am pretty new to Entity Framework and LINQ and I have an entity with more than 10+ other associated entities (one-to-many relationships). Now, I'm planning to make a search page in my application in which users could select which fields (i.e. those 10+ tables) they want to be considered when searching.
Now, I'm trying to write a query to achieve the above goal. Any help how I could sort this out using LINQ method syntax? I mean, to write a multiple join query based on user's choice. (i.e. which of Class1, Class2, ... to join with main Entity to finally have all the related fields in one place). Below is a sample code (Just a hunch, in fact)
if(somefilter#1)
result = db.Companies.Join(db.Channels, p => p.Id, k => k.CId,
(p, k) => new {Company = p, Channels=k});
if(somefilter#2)
result = result.Join(db.BusinnessType, ........);
if(somefilter#3)
result = result.Join(db.Values, .......);
For complex queries it may be easier to use the other LINQ notation. You could join multiple entities like this:
from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new {
myEntity.Id,
myEntity.Name,
myOtherEntity.OtherProperty,
oneMoreEntity.OneMoreProperty
}
You can join in other entities by adding more join statements.
You can select properties of any entity from your query. The example I provided uses a dynamic class, but you can also define a class (like MyJoinedEntity) into which you can select instead. To do it you would use something like:
...
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
}
EDIT:
In case when you want to have conditional joins you can define MyJoinedEntity with all the properties you will need if you were to join everything. Then break up the join into multiple methods. Like this:
public IEnumerable<MyJoinedEntity> GetEntities() {
var joinedEntities = from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
};
if (condition1) {
joinedEntities = JoinWithRelated(joinedEntities);
}
}
public IEnumerable<MyJoinedEntity> JoinWithRelated(IEnumerable<MyJoinedEntity> joinedEntities) {
return from joinedEntity in joinedEntities
join relatedEntity in dbContext.RelatedEntities on joinedEntity.Id equals relatedEntity.MyEntityId
select new MyJoinedEntity(joinedEntity) {
Comments = relatedEntity.Comments
};
}
I need to join all data according to GroupIDs, which these GroupIDs are owned by EmpNo
public IEnumerable<EmployeeWithEmail> GetAllEmployeesWithEmail(int EmpNo)
{
using (var context = new SQL_TA_SCOREBOARDEntities1())
{
return (from ea in context.View_SystemAdminMembers
join vh in context.View_HCM on (Int16)ea.EmpNo equals vh.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id into outer_join
from subjoin in outer_join
//need code to join all data according to EmpNo's GroupIDs
group new
{
ea.EmpNo,
subjoin.Role,
vh.EmailAddress,
vh.LNameByFName,
ea.Active
} by vh.LNameByFName into grp
let item = grp.FirstOrDefault()
orderby item.Role ascending
select new EmployeeWithEmail
{
EmpNum = item.EmpNo ?? 0,
Role = item.Role,
EmailAddress = item.EmailAddress,
LNameByFname = item.LNameByFName,
Active2 = item.Active ?? false
}).ToList();
}
}
I guess I'm trying to filter twice and join common data, but there are actually two filters, which I don't know how to control.
So my output would be like:
EmpNo ---> __ 01 | 01 | 01 | 01
GroupID ---> __10 | 10 | 20 | 20
Data ---> _________Apple | Apple | Orange | Orange
I can filter EmpNo 01 and GroupID 10 but What if the EmpNo belongs to two groups?
Sorry for not finding the right terminologies.
Thanks in advance.
Based on your comment, the SQL you are trying to generate should be (I've simplified slightly)
SELECT EmployeeAccess.EmpNo, View_SystemAdminMembers.LNameByFName, View_SystemAdminMembers.GroupName,
View_SystemAdminMembers.Role, View_SystemAdminMembers.Active, View_SystemAdminMembers.EmpNo,
View_SystemAdminMembers.RoleID
FROM EmployeeAccess
INNER JOIN View_SystemAdminMembers ON EmployeeAccess.GroupID = View_SystemAdminMembers.GroupID
WHERE (EmployeeAccess.EmpNo = '01')
This is pretty different from what you show in your question:
from ea in context.View_SystemAdminMembers
join vh in context.View_HCM on (Int16)ea.EmpNo equals vh.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id into outer_join
from subjoin in outer_join
so I'm not sure if my answer is going to help or not, but to get the SQL you specify, I think you would want to do:
var query =
from ea in context.EmployeeAccess
join vsam in context.View_SystemAdminMembers on ea.GroupID equals vsam.GroupID
where ea.EmpNo == "01"
select new
{
ea.EmpNo, vsam.LNameByFName, vsam.GroupName, vsam.Role, vsam.Active, vsam.EmpNo, vsam.RoleID
};
Using fluent syntax (not query syntax) it would look a bit like:
var query =
context.EmployeeAccess
.Join(context.View_SystemAdminMembers, allEA => allEA.GroupID, allVSAM => allVSAM.GroupID, (ea, vsam) => new {ea, vsam})
.Where(combined => combined.ea.EmpNo == "01")
.Select(combined => combined.ea.EmpNo, combined.vsam.LNameByFName, combined.vsam.GroupName, combined.vsam.Role, combined.vsam.Active, combined.vsam.EmpNo, combined.vsam.RoleID);
(although I admit--I normally return the whole entity like
.Select(combined => combined.ea) or something like that so I'm not 100% certain on that last line...)
Note that in both cases, "var query" is going to be an IQueryable which means you will still need to add a ToList or equivalent to get your results. Before you do that, though, you'll want to apply Tim Burkhart's answer to make any modifications you want on it (like GroupBy or whatever). As he noted, one of the cool things about IQueryable is that you don't have to do it all in one statement; you can take query just as I have defined it above and then add something like
query = query.Where(c => c.LNameByFName.Contains("A"))
or whatever.
One more note--your return value is entirely made up of items from View_SystemAdminMembers with the exception of EmployeeAccess.EmpNo, but since you are filtering on that, you should already know what it is. It may be easier to return just a View_SystemAdminMember object, rather than creating a new type. That's up to you.
I'm not really following what you're asking for. Perhaps it can be rephrased or given more context or formatted differently?
But I thought I would make some suggestions that might help you get to where you are wanting to go.
1 - The function name implies that the person will provide an email and receive multiple Employee objects with that email. But it appears to accept an employee id and return a list of EmployeeWithEmail. Consider renaming the function to match what it is doing. Also consider returning IEnumerable<Employee> or IEnumerable<IEmployeeEmailView>
2 - This function is doing a lot. (And it takes more than a few seconds to figure out what it is doing). In cases like this, I would start off simple. Don't do the grouping or sorting or anything. Have some function return the results of this:
from ea in context.View_SystemAdminMembers
join vh in context.View_HCM on (Int16)ea.EmpNo equals vh.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id into outer_join
from subjoin in outer_join
in the form of something like IEnumerable<Employee>
public class Employee {
public int Id { get; set; }
public string Role { get; set; }
public string EmailAddress { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
From there, things are easier to deal with. You're not having to work with LINQ-to-SQL. You can just do...
IEnumerable<Employee> employees = <your renamed function>();
var employeesGroupedByName = employees.GroupBy(e => e.Name);
Hopefully that makes things easier for you. It doesn't solve your answer, but I think it might make things less complicated/easier to work with.
So written out fully, it could be something like:
public class EmployeeRepository {
public IEnumerable<Employee> GetAll() {
// This function won't compile. I don't know the syntax for this type of LINQ
using (var context = new SQL_TA_SCOREBOARDEntities1()) {
return (from ea in context.View_SystemAdminMembers
join vh in context.View_HCM on (Int16)ea.EmpNo
join rl in context.EmployeeAccessLevels on ea.RoleID equals rl.id into outer_join
}
}
public IEnumerable<Employee> GetAllEmployeesWithEmployeeId(int employeeId) {
return GetAll().Where(e => e.Id == employeeId).ToList();
}
public IEnumerable<Employee> SomeOtherFunctionThatDoesWhatYouWantToDoFromThisPost() {
// You could also create a class that extends IEnumerable<Employee> to
// encapsulate the logic so that the repository doesn't have a million functions
}
}
That's all I've got for you. I could give a more complete answer if the problem was spelled out a little bit better, but hopefully this gets you on the right track.
I have the following tables
ClassRoom (ClassID,ClassName)
StudentClass (StudentID,ClassID)
Student (StudentID,StudentName,Etc..)
StudentDescription. (StudentDescriptionID,StudentID,StudentDescription)
I want to retrieve all the information on student==1
In sql I would do something like BELOW and get all the info about a student.
select * from Student s
join StudentClass sc on s.StudentID=sc.StudentID
join ClassRoom c on sc.ClassID=c.ClassID
left join StudentDescription sd on s.StudentID=sd.StudentID
where s.StudentID=14
Now my problem.Using EF4 I did something like this but cannot make it work.
Also can you do an include and a left join
Attempt 1
private static StudentDto LoadStudent(int studentId)
{
StudentDto studentDto = null;
using (var ctx = new TrainingContext())
{
var query = ctx.Students
.Include("ClassRooms")
.Include("StudentDescriptions")
.Where(x=>x.StudentID==studentId)
.SingleOrDefault();
studentDto = new StudentDto();
studentDto.StudentId = query.StudentID;
studentDto.StudentName = query.StudentName;
studentDto.StudentDescription = ??
}
return studentDto;
}
Attempt 2 again incomplete and wrong
using (var ctx = new TrainingContext())
{
var query = (from s in ctx.Students
.Include("ClassRooms")
join sd in ctx.StudentDescriptions on s.StudentID equals sd.StudentID into g
from stuDesc in g.DefaultIfEmpty()
select new
{
Name=s.StudentName,
StudentId=s.StudentID,
}).SingleOrDefault();
As you can see I dont know what I am doing here.
How can I convert that Sql into a EF Query?
Yes, it is possible.
Firstly, .Include does a LEFT OUTER JOIN, using the navigational property you pass through.
This is how you would explicitly do a LEFT JOIN between Student and StudentDescription:
var query = from s in ctx.Students
from sd in s.StudentDescriptions.DefaultIfEmpty()
select new { StudentName = s.Name, StudentDescription = sd.Description };
As you can see, it's performing the JOIN based on the entity association between Students and StudentDescriptions. In your EF model, you should have a navigational property called StudentDescriptions on your Student entity. The above code is simply using that to perform the join, and defaulting if empty.
The code is basically identical to .Include.
Please don't get confused with LEFT JOIN vs LEFT OUTER JOIN.
They are the same thing.
The "OUTER" keyword is optional, i believe it is there for ANSI-92 compatability.
Just .Include everything you need in your query:
using (var ctx = new TrainingContext())
{
studentDo = ctx.Students
.Include("ClassRooms")
.Include("StudentDescriptions")
.Where(x=>x.StudentID==studentId)
.Select(x => new StudentDto
{
StudentId = x.StudentId,
StudentName = x.StudentName
StudentDescription = x.StudentDescription.Description
})
.SingleOrDefault();
}
Basically, make sure all your FK's are expressed as navigational properties on your model, then if so, you don't need to do any joins. Any relationships you require can be done with .Include.
I just had this problem, in my case it was the EntityTypeConfiguration that was wrong
I had:
HasRequired(s => s.ClassRoom)
.WithMany()
.HasForeignKey(student => student.ClassRoomId);
Instead of:
HasOptional(s => s.ClassRoom)
.WithMany()
.HasForeignKey(student => student.ClassRoomId);
It seems HasRequired makes a INNER JOIN while HasOptional makes a LEFT JOIN.
Exactly:
If StudentDescription.StudentId is nullable -> EF performs a LEFT JOIN, i.e. select * from Student s LEFT JOIN StudentDescription sd on s.StudentID=sd.StudentID.
Otherwise EF does INNER JOIN.
The behavior of .Include:
The property is requeired: Always translates to INNER JOIN;
The type of foreign key is not nullable: translates to INNER JOIN in default, but you can add .IsRequired(false) with .HasForeignKey to turn it to be LEFT OUT JOIN.
The type of property is collection will always translates to LEFT OUT JOIN.
If you want a 1 to 1 relation, you can simply map your foreign Id and make it nullable.
public int? MyForeignClassId { get; set; }
public MyForeignClass MyForeignClass { get; set; }
I have 3 kinds of objects: Agency, BusinessUnit and Client (each with their own respective table)
In terms of hierarchy, Agencies own BusinessUnits, and BusinessUnits own Clients.
I have 3 C# POCO Objects to represent them (I usually select new {} into them, rather than use the LINQ generated classes):
public class Agency
{
public IEnumerable<BusinessUnit> BusinessUnits { get; set; }
}
public class BusinessUnit
{
public IEnumerable<Client> Clients { get; set; }
}
public class Client
{
public int NumberOfAccounts { get; set; }
public Decimal AmountOfPlacement { get; set; }
public Decimal AvgBalance { get; set; }
public Double NeuPlacementScore { get; set; }
}
You can see that Agencies contain a list of BusinessUnits, and BusinessUnits contain a list of Clients.
I also have a mapping table called BAC_Map in the database which says which owns which, and it looks something like this:
How can I construct a query, so I can query for and return a list of Agencies? Meaning that, I want each Agency to have its list of BusinessUnit objects set, and I want the list of BusinessObjects to have its list of Clients set.
I can do basic LINQ queries, but this is a little over my head concerning the Map table and the multiple? queries.
How could I construct a method like GetAllAgencies() which would query, for not only all agencies, but populate its BusinessUnits that Agency owns, and the Clients those BusinessUnits own?
Edit: Any tips or info is appreciated. Do I need to do joins? Does this need to be multiple queries to return an Agency list, with its submembers populated?
If you drop all four tables (Agency, BusinessUnit, Client, Map) on the linq to sql designer, and draw relationships from Map to the other three, there will be some useful properties on Map.
//construct a query to fetch the row/column shaped results.
var query =
from m in db.map
//where m.... ?
let a = m.Agency
let b = m.BusinessUnit
let c = m.Client
// where something about a or b or c ?
select new {
AgencyID = a.AgencyID,
AgencyName = a.Name,
BusinessUnitID = b.BusinessUnitID,
ClientID = c.ClientID,
NumberOfAccounts = c.NumberOfAccounts,
Score = c.Score
};
//hit the database
var rawRecords = query.ToList();
//shape the results further into a hierarchy.
List<Agency> results = rawRecords
.GroupBy(x => x.AgencyID)
.Select(g => new Agency()
{
Name = g.First().AgencyName,
BusinessUnits = g
.GroupBy(y => y.BusinessUnitID)
.Select(g2 => new BusinessUnit()
{
Clients = g2
.Select(z => new Client()
{
NumberOfAccounts = z.NumberOfAccounts,
Score = z.Score
})
})
})
.ToList();
If approriate filters are supplied (see the commented out where clauses), then only the needed portions of the tables will be pulled into memory. This is standard SQL joining at work here.
I created your tables in a SQL Server database, and tried to recreate your scenario in LinqPad. I ended up with the following LINQ statements, which basically result in the same structure of your POCO classes:
var map = from bac in BAC_Maps
join a in Agencies on bac.Agency_ID equals a.Agency_ID
join b in BusinessUnits on bac.Business_Unit_ID equals b.Business_Unit_ID
join c in Clients on bac.Client_ID equals c.Client_ID
select new
{
AgencyID = a.Agency_ID,
BusinessUnitID = b.Business_Unit_ID,
Client = c
};
var results = from m in map.ToList()
group m by m.AgencyID into g
select new
{
BusinessUnits = from m2 in g
group m2 by m2.BusinessUnitID into g2
select new
{
Clients = from m3 in g2
select m3.Client
}
};
results.Dump();
Note that I called map.ToList() in the second query. This actually resulted in a single, efficient query. My initial attempt did not include .ToList(), and resulted in nine separate queries to produce the same results. The query generated by the .ToList() version is as follows:
SELECT [t1].[Agency_ID] AS [AgencyID], [t2].[Business_Unit_ID] AS [BusinessUnitID], [t3].[Client_ID], [t3].[NumberOfAccounts], [t3].[AmountOfPlacement], [t3].[AvgBalance], [t3].[NeuPlacementScore]
FROM [BAC_Map] AS [t0]
INNER JOIN [Agencies] AS [t1] ON [t0].[Agency_ID] = [t1].[Agency_ID]
INNER JOIN [BusinessUnits] AS [t2] ON [t0].[Business_Unit_ID] = [t2].[Business_Unit_ID]
INNER JOIN [Clients] AS [t3] ON [t0].[Client_ID] = [t3].[Client_ID]
Here is a screenshot of the results:
alt text http://img411.imageshack.us/img411/5003/agencybusinessunitclien.png
If you are doing this with direct LINQ to SQL, there is no way to do this without some kind of recursion, whether you do it yourself or you hide it behind an extension method. Recursive SQL is very bad (many round trips, many single queries).
There are two options here. One is to pull the entire table(s) with the hierarchy into memory and use LINQ to Objects on it. Leave the "details" tables in SQL. If you have less than several thousand entities, this is probably the most efficient way to go. You can keep a single copy of the table(s) in cache and refresh them when necessary. When you need to fetch more detailed data from the DB for a single record, you can reattach that entity from your cached hierarchy to a new DataContext and fetch it.
The other option is to use a more complex relationship model in your database. Storing parent only by nature demands recursion, but you can use the adjacency list model to construct a single query which can span many levels of inheritance. This will mean your LINQ to SQL queries become less intuitive (querying against Entity.Right and Entity.Left isn't quite as pretty as Parent or Children...) but you can do in one query what might take hundreds or thousands in the literal recursive approach.