LINQ Query not pulling all records needed - c#

I am having some trouble with a LINQ query.
This query creates a list of new objects based on entries from a repository.
Here is the original query:
var accounts = (from a in entityRepository.Queryable<Account>()
from l in a.ExternalLogins
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
},
a.AdminAccess,
a.Username,
a.Email
}).ToList();
My issue is that not all a have an a.ExternalLogins. The query is not pulling those accounts because of the additional from statement from l in a.ExternalLogins. I tried modifying the query to:
var accounts = (from a in entityRepository.Queryable<Account>()
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = (from l in a.ExternalLogins
select new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
}),
a.AdminAccess,
a.Username,
a.Email
}).ToList();
But I am getting a 'System.Reflection.AmbiguousMatchException' exception.
From looking up that exception, I am guessing that the reason is because both Account and Connection have the field ID.
Am I headed in the right direction with this? Do I chase down this exception, or is my query not correct?
I apologize if this is trivial; I'm new to LINQ queries, and my google skills have failed me at this point!

To do a left outer join in Linq, add a DefaultIfEmpty() call and check for null in the results:
var accounts = (from a in entityRepository.Queryable<Account>()
from l in a.ExternalLogins.DefaultIfEmpty()
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = (l == null) ? null : new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
},
a.AdminAccess,
a.Username,
a.Email
}).ToList();

Related

LINQ to Entities after group by, COUNT and WHERE not working

I have the following LINQ-to-Entities query for MySQL DB
var data = (from agent in db.User
join agentrole in db.UserRole.DefaultIfEmpty() on agent.Id equals agentrole.UserId
join role in db.Role.DefaultIfEmpty() on agentrole.RoleId equals role.Id
join department in db.Department.DefaultIfEmpty() on role.DepartmentId equals department.Id
join client in db.Client.DefaultIfEmpty() on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient.DefaultIfEmpty() on client.Id equals aggclient.ClientId
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount =grp.Count(a => a.department != null),
aggclientfilter = grp.Where(a => a.aggclient != null && a.aggclient.FirstDepositAmount>0).Sum(a => a.aggclient.FirstDepositAmount)
});
On the current query, the last two operations are not working.
The entity cannot parse count and where operations.
change select clause to :
select new
{
grp.Key,
agentName = grp.agent.Max(a => a.FirstName + " " + a.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.aggclient.Sum(a => a.FirstDepositAmountEuro),
depcount = grp.department.Count(),
aggclientfilter = grp.aggclient.Where(a => a.FirstDepositAmount>0).Sum(a => a.FirstDepositAmount)
});
I assume that you do not use EF Core 5.x, because it supports filtered count.
Problem that there is no correct translation to SQL of such LINQ query. But there are tricks which can return needed result. Also corrected bad LEFT join.
var data =
from agent in db.User
join agentrole in db.UserRole on agent.Id equals agentrole.UserId into ga
from agentrole in ga.DefaultIfEmpty()
join role in db.Role on agentrole.RoleId equals role.Id into gr
from role in gr.DefaultIfEmpty()
join department in db.Department on role.DepartmentId equals department.Id into dg
from department in dg.DefaultIfEmpty()
join client in db.Client on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient on client.Id equals aggclient.ClientId into acg
from aggclient in acg.DefaultIfEmpty()
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount = grp.Sum(a => a.department != null ? 1 : 0),
aggclientfilter = grp.Sum(a => a.aggclient.FirstDepositAmount > 0 ? a.aggclient.FirstDepositAmount : 0)
};

Attempted Left Join in Query Syntax and Lambda results in "The method or operation is not implemented."

I've attempted to write this query two different ways, and no matter how I write it, I can't get the join to pan out. It just keeps tossing "The method or operation is not implemented." at me. What I'm trying to do is, in SQL, simple: Obtain a list of items from table A (below - Customer) where there is no corresponding listing in table B (below - SalesReps).
Attempted Lamda:
var customers =
_sms.CurrentSession.Query<customer>()
.GroupJoin(_sms.CurrentSession.Query<salesReps>(),
c => c.Id,
sr => sr.CustomerId,
(x, y) => new {c = x, sr = y})
.SelectMany(xy => xy.sr.DefaultIfEmpty(),
(x, y) => new {c = x.c, sr = y})
.Where(data => data.sr.SalesRepId == null)
.Select(data => new CustomerDTO
{
Id = data.c.Id,
FullName = data.c.FirstName + " " + data.c.LastName,
FirstName = data.c.FirstName,
LastName = data.c.LastName
});
var custList = customers .ToList();
Attempted Query Syntax:
var customers = from c in _sms.CurrentSession.Query<customer>()
join sr in _sms.CurrentSession.Query<salesReps>()
on c.Id equals sr.CustomerId
into joinedData
from jd in joinedData.DefaultIfEmpty()
where c.IsEmployee == false
&& c.CustomerOptions.Company.Id == companyGuid
&& jd.SalesRepId == null
select
new CustomerDTO
{
Id = c.Id,
FullName = c.FirstName + " " + c.LastName,
FirstName = c.FirstName,
LastName = c.LastName
};
var custList = customers .ToList();
I get the impression that the issue is on the check for null sales reps, but I'm not sure.

LINQ - Limit result based on count returned by custom function

I have the following code and want to return a limited subset of this query in LINQ. The limited subset will take u.ID as an argument to the function and count the number of records associated with u.ID from another table.
So far, this is what I have.
var res = from u in db.Users
where id == u.WorkGroupID && jobCount(u.ID) > 0
select
new
{
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};
How can I modify this query to limit the number of returned records based on a count value associated with each u.ID?
EDIT:
New Query Below. Last line returns to caller a list from the last LINQ query.
var res = from u in db.Users
where id == u.WorkGroupID
select
new
{
// SELF
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};
var res2 = res.ToList<dynamic>();
var res3 = from row in res2.AsEnumerable()
where jobCount(row.ArtistID) > 0
select new
{
row.ArtistName,
row.ArtistID
};
return res3.ToList<dynamic>();
Use a group join:
from u in db.Users
join o in db.Other on u.ID equals o.UserID into grp
where grp.Any()
select new
{
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};

Multiple Left Join LINQ-to-entities

I have 3 tables:
Dealerships
------------
ID, Name, Website
Locations
------------
ID, DealershipID, Address, Ect.
Contacts
------------
ID, LocationID, Name, Ect.
So the relationship shows that we have dealerships who have multiple locations (Example: Weed Chevrolet of PA, Weed Chevrolet of NJ) and then each location has its own contacts (Example: Managers of PA location, Managers of NJ location). I need to join the 3 tables together. This is what I have:
var results = from d in entities.dealerships
join l in entities.locations on d.ID equals l.DealershipID
join c in entities.contacts on l.ID equals c.LocationID
select new
{
Name = d.Name,
Website = d.Website,
Address = l.Address + ", " + l.City + ", " + l.State + " " + l.Zip,
Contact = c.FirstName + " " + c.LastName,
WorkPhone = c.WorkPhone,
CellPhone = c.CellPhone,
HomePhone = c.HomePhone,
Email = c.Email,
AltEmail = c.AltEmail,
Sells = l.Sells
}
When I attempt to bind results to a BindingSource and then to a DataGridView I receive the following error:
Unable to cast the type 'System.Nullable`1' to type 'System.Object'.
LINQ to Entities only supports casting Entity Data Model primitive types.
What can it be? I am new to JOIN statements in LINQ so I am sure I am doing something wrong.
EDIT: There is data in the database so the results shouldn't be null, just to clarify
You were close but I discovered that you have to convert it from LINQ-To-Entities to LINQ-To-Objects. First I had to cast the entities using AsEnumerable() then use ToList(). This made it so I could use functions like ToString() and String.Format(). Thanks for leading me in the right direction. Here is the final code:
var query = from d in entities.dealerships
from l in entities.locations.Where(loc => loc.DealershipID == d.ID).DefaultIfEmpty()
from c in entities.contacts.Where(cont => cont.LocationID == l.ID).DefaultIfEmpty()
where d.Keywords.Contains(keywords) || l.Keywords.Contains(keywords) || l.Sells.Contains(keywords) || c.Keywords.Contains(keywords)
select new
{
Dealership = d,
Location = l,
Contact = c
};
var results = (from r in query.AsEnumerable()
select new
{
Name = r.Dealership.Name,
Website = r.Dealership.Website,
Contact = r.Contact.FirstName + " " + r.Contact.LastName,
Address = r.Location.Address + ", " + r.Location.City + ", " + r.Location.State + " " + r.Location.Zip,
WorkPhone = r.Contact.WorkPhone,
CellPhone = r.Contact.CellPhone,
Fax = r.Contact.Fax,
Email = r.Contact.Email,
AltEmail = r.Contact.AltEmail,
Sells = r.Location.Sells
}).ToList();
bindingSource.DataSource = results;
Since your results is IQueryable, EF will try to cast on the data store side and it won't work because cast only works with scalar types. You should call ToList() on the results like this:
var results = (from d in entities.dealerships
join l in entities.locations on d.ID equals l.DealershipID
join c in entities.contacts on l.ID equals c.LocationID
select new
{
Name = d.Name,
Website = d.Website,
Address = l.Address + ", " + l.City + ", " + l.State + " " + l.Zip,
Contact = c.FirstName + " " + c.LastName,
WorkPhone = c.WorkPhone,
CellPhone = c.CellPhone,
HomePhone = c.HomePhone,
Email = c.Email,
AltEmail = c.AltEmail,
Sells = l.Sells
}).ToList();
var EmplistDriver = (from a in data
join b in db.DesignationDetails on a.DesignationID equals b.DesignationDetailID into EmployeeBonus
from b in dataBonus.DefaultIfEmpty()
join x in db.EmployeeCommission on a.EmployeeDetailID equals x.EmployeeDetailID into EmployeeCommission
from x in dataComm.DefaultIfEmpty()
join c in db.EmployeeAdvance on a.EmployeeDetailID equals c.FKEAEmployeeID
join d in db.EmployeeAllowance on a.EmployeeAllowanceID equals d.EmployeeAllowanceID
join e in dataAtt on a.EmployeeDetailID equals e.EmployeeDetailID
join f in dataDri on a.EmployeeDetailID equals f.EmployeeDetailID
join h in db.ProjectAllocation on f.FKAllocationID equals h.PKAllocationID
join i in db.ProjectDetails on h.FKProjectDetailID equals i.ProjectDetailID
where a.IsActive == true && c.EAIsActive == true && d.IsActive == true && e.EAIsActive == true && h.IsActivity == true
select new
{
c.BalanceAmount,
c.BalanceDue,
d.FoodAllowance,
i.DriverBasicSalary,
d.OtherAllowance,
d.AccommodationAllowance,
e.EABasicWorktime,
BonusAmount = (b.BonusAmount == null ? 0 : b.BonusAmount),
CommissionAmount = (x.CommissionAmount == null ? 0 : x.CommissionAmount),
TotalOverTime,
TotalHr
}).FirstOrDefault();

LINQ sorting anonymous types?

How do I do sorting when generating anonymous types in linq to sql?
Ex:
from e in linq0
order by User descending /* ??? */
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
If you're using LINQ to Objects, I'd do this:
var query = from e in linq0
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = e.Date.ToString("d")
} into anon
orderby anon.User descending
select anon;
That way the string concatenation only has to be done once.
I don't know what that would do in LINQ to SQL though...
If I've understood your question correctly, you want to do this:
from e in linq0
order by (e.User.FirstName + " " + e.User.LastName).Trim()) descending
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
Would this work, as a way of avoiding Jon's select...into?
from e in linq0
let comment = new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
orderby comment.User descending
select comment
I'm going to get a necromancer badge for this answer, but I still think it's worth showing this snippet.
var records = await (from s in db.S
join l in db.L on s.LId equals l.Id
where (...)
select new { S = s, Type = l.MyType }
).ToListAsync();
//Data is retrieved from database by now.
//OrderBy below is LINQ to Objects, not LINQ to SQL
if (sortbyABC)
{
//Sort A->B->C
records.OrderBy(sl => sl.Type, new ABC());
}
else
{
//Sort B->A->C
records.OrderBy(sl => sl.Type, new BAC());
}
ABC and BAC implement IComparer<MyType>.

Categories

Resources