Linq query rewriting - c#

This has really stumped me...
I have two queries..
This one does NOT work, with the error,
"LINQ to Entities does not recognize the method GetAllCustomers()' method, and this method cannot be translated into a store expression."
results = theDocuments.Select(x => new DocumentModel()
{
document_id = x.document_id,
document_type = x.document_type,
franchisee_id = x.franchisee_id,
customer_id = x.customer_id,
address = x.address,
area_id = x.area_id,
***HERE***customer_name = outletService.GetAllCustomers().Where(xx => xx.id == x.customer_id).First().name
}).OrderBy(x => x.document_id);
Now rewriting it like this works:
results = from p in theDocuments
join cust in outletService.GetAllCustomers() on p.customer_id equals cust.id
select new DocumentModel()
{
customer_name = cust.name,
document_id = p.document_id,
document_type = p.document_type,
franchisee_id = p.franchisee_id,
customer_id = p.customer_id,
address = p.address
};
I understand that this cannot be converted to it's relevant SQL, however, I am not sure what the second query is doing differently in it's execution in order for it to work, does it execute the GetAllCustomers() before doing anything else? How is it working differently internally?
How could I rewrite the first query so that it executes correctly?
Thanks,
James.
**This is what I ended up with **
results = theDocuments.Join(
outletService.GetAllCustomers(), docs => docs.customer_id, customers => customers.id, (doc, cust) => new DocumentModel()
{
document_id = doc.document_id,
document_type = doc.document_type,
franchisee_id = doc.franchisee_id,
customer_id = doc.customer_id,
address = doc.address,
area_id = doc.area_id,
customer_name = cust.name
});

The first one try to translate your method into a SQL method, the second create a join in the query. In my opinion the correct way is using join :)

I was a problem. I found a solution. That might be help you.
dynamic user = new
{
Name = "",
Surname = ""
};
dynamic userModel = new
{
Name = "",
Surname = "",
FullName = ""
};
var query = db.User.AsNoTracking();
query = query.Select(x => new userModel
{
Name = x.Name,
Surname = x.SurName,
FullName = $"{x.Name}{x.SurName}",
}).ToList();
If you try to execute this query. You will take a crash. Linq doesnt support
FullName = $"{x.Name}{x.SurName}"
You have to change this line to
FullName = x.Name + " " + x.SurName

Related

MongoDB Linq query in C# with filters

I am trying to do a LINQ query on several Mongo collections. All the collections have to be joined based on ApplicationId and an outer Join has to be done - so that persons that have no statuses are returned as well.
The JOIN part and everything around it works as expected. The problem is that when I add a filter to one of the collections, the whole thing breaks
An exception of type 'System.ArgumentException' occurred in System.Linq.Expressions.dll but was not handled in user code: 'Expression of type 'System.Collections.Generic.IEnumerable`1[CDM.Person]' cannot be used for parameter of type 'System.Linq.IQueryable`1[CDM.Person]' of method 'System.Linq.IQueryable`1[CDM.Person] Where[Person](System.Linq.IQueryable`1[CDM.Person], System.Linq.Expressions.Expression`1[System.Func`2[CDM.Person,System.Boolean]])''
Here is my query
var applications = _dbContext.GetCollection<Application>(typeof(Application).Name).AsQueryable().Where(
x => x.OrganizationID == TokenContext.OrganizationID);
var persons = _dbContext.GetCollection<Person>(typeof(Person).Name).AsQueryable().Where(p =>p.FirstName == "j");
var statuses = _dbContext.GetCollection<ApplicationStatus>(typeof(ApplicationStatus).Name).AsQueryable();
var mortgages = _dbContext.GetCollection<Mortgage>(typeof(Mortgage).Name).AsQueryable();
var statusQuery = from a in applications
join p in persons on a.ApplicationID equals p.ApplicationID
join s in statuses on a.ApplicationID equals s.ApplicationID into pas
join m in mortgages on a.ApplicationID equals m.ApplicationID into morgs
from subWHatever in pas.DefaultIfEmpty()
select new ApplicationStatusProjection
{
ApplicationId = a.ApplicationID,
FirstName = p.FirstName,
LastName = p.Surname,
Prefix = p.Prefix,
DateOfBirth = p.DateOfBirth,
Initials = p.Initials,
PostalCode = p.Addresses.First().PostalCode,
MortgageAmount = morgs.Sum(i => i.MortgageTotal) ?? 0,
StatusExpireAt = subWHatever.ExpireAt ?? DateTime.MinValue,
StatusMessageText = subWHatever.MessageText ?? "",
StatusMessage = subWHatever.MessageStatus ?? ""
};
if (!String.IsNullOrEmpty(orderBy))
{
statusQuery = statusQuery?.OrderBy(orderBy);
}
if (nrOfRecords != null)
{
statusQuery = statusQuery?.Take(nrOfRecords.Value);
}
// Execute the query
var result = statusQuery?.ToList();
return result;
I followed the guidelines here https://mongodb.github.io/mongo-csharp-driver/2.6/reference/driver/crud/linq/ and I also tried this
var statusQuery =
from a in applications
join p in persons on a.ApplicationID equals p.ApplicationID into pa
from paObject in pa.DefaultIfEmpty()
join s in statuses on paObject.ApplicationID equals s.ApplicationID into pas
But I got the same error as before.
Thank you in advance.
So after may tryouts I have discovered that you cannot filter before the join because of the way the LINQ query is translated to Mongo query.
The fix for this is to have the filtering afterwards, on the statusQuery object. In that case, the filtering has to happen on the projected object (so a new filter is needed).
See below how I solved it:
//get collections
var applications = _dbContext.GetCollection<Application>(typeof(Application).Name).AsQueryable().Where(
x => x.OrganizationID == TokenContext.OrganizationID);
var persons = _dbContext.GetCollection<Person>(typeof(Person).Name).AsQueryable();
var statuses = _dbContext.GetCollection<ApplicationStatus>(typeof(ApplicationStatus).Name).AsQueryable();
var mortgages = _dbContext.GetCollection<Mortgage>(typeof(Mortgage).Name).AsQueryable();
//query
var query = from a in applications
join p in persons on a.ApplicationID equals p.ApplicationID
join s in statuses on a.ApplicationID equals s.ApplicationID into applicationStatusView
join m in mortgages on a.ApplicationID equals m.ApplicationID into morgs
from subStatus in applicationStatusView.DefaultIfEmpty()
select new ApplicationStatusProjection
{
ApplicationId = a.ApplicationID,
FirstName = p.FirstName,
LastName = p.Surname,
Prefix = p.Prefix,
DateOfBirth = p.DateOfBirth,
Initials = p.Initials,
Addresses = p.Addresses ?? new List<Address>(),
PostalCode = p.Addresses.First().PostalCode,
MortgageAmount = morgs.Sum(i => i.MortgageTotal) ?? 0,
StatusExpireAt = subStatus.ExpireAt ?? DateTime.MinValue,
StatusMessageText = subStatus.MessageText ?? "",
StatusMessage = subStatus.MessageStatus ?? "",
StatusDate = subStatus.StatusDate
};
//filter & order
var filteredResult = ApplyFilters(query, searchCriteria);
if (!String.IsNullOrEmpty(orderBy))
{
filteredResult = filteredResult?.OrderBy(orderBy);
}
if (nrOfRecords != null)
{
filteredResult = filteredResult?.Take(nrOfRecords.Value);
}
// Execute the query
var result = filteredResult?.ToList();
return result;
And applying the filters (there is probably a smarter way to do this):
private IQueryable<ApplicationStatusProjection> ApplyFilters(IQueryable<ApplicationStatusProjection> query, ApplicationStatusProjectionSearch searchCriteria)
{
if (!string.IsNullOrEmpty(searchCriteria.FirstName))
{
query = query.Where(x => x.FirstName.ToLower().StartsWith(searchCriteria.FirstName));
}
if (!string.IsNullOrEmpty(searchCriteria.LastName))
{
query = query.Where(x => x.LastName.ToLower().StartsWith(searchCriteria.LastName));
}
if (!string.IsNullOrEmpty(searchCriteria.PostalCode))
{
query = query.Where(x => x.Addresses.Any(a => a.PostalCode.ToLower().StartsWith(searchCriteria.PostalCode)));
}
//other irrelevant filters
return query;
}

Linq, Json, Select product from db

I have a 3 tables Dish, Wine, Suggestion.
Then the idea is use table suggestion table to put the dish and the wine making one of them suggestion each other.
I'm using LINQ, but when one product doesn't have a suggestion he does not add to the json.
var query = (from m in db.Dish
join t in db.TypeDish on m.idTypeDish equals t.idTypeDish
join i in db.ImageDish on m.idDish equals i.idDish into g
join s in db.Suggestion on m.idDish equals s.idDish
join si in db.ImageWine on s.idWine equals si.idWine into f
where m.idTypeDish == dish
select new DishModel()
{
Name = m.name,
CalorificValue = m.calorificValue,
Price = m.price,
ShortName = m.shortName,
Time = m.manufactureTime,
Description = m.description,
UrlImageList = g.Select(i => _url + i.Image.urlImage).ToList(),
BeveragesList = new List<BeverageModel>()
{
new BeverageModel()
{
Name = s.Wine.name,
ShortName = s.Wine.shortName,
Price = s.Wine.price,
Description = s.Wine.description,
AlcoholContent = s.Wine.alcoholContent,
WineEnum = WineEnum.WhiteWine,
Region = s.Wine.Region.name,
WineCaste = s.Wine.wineCaste,
UrlImageList = f.Select(i => _url+i.Image.urlImage).ToList(),
}
}
}).ToList();
return query;
Now I have 2 items on DB, and he sends only one because the other don't have a suggestion.
The error is on joins probably, but I'm a newbie in Linq.
Thanks.

Implicit ViewModel generation from LINQ?

In my project I have a GenericImageViewModel which is used by many entities.
Example of getting the ASP User Entity:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = a.UserProfile.ProfileImage
});
The ProfileImage is the GenericImageViewModel and has an implicit operator as follows:
public static implicit operator TRDGenericImageViewModel(TRDImage image)
{
return new TRDGenericImageViewModel
{
Id = image.Id,
AspectRatio = image.Ratio,
Url = image.Url,
};
}
If I run the query Entity Framework throws an exception:
"Unable to cast the type 'TRDImage' to type 'TRDGenericImageViewModel'. LINQ to Entities only supports casting EDM primitive or enumeration types."
If I create the GenericImageViewModel manually for each ViewModel everything is running fine:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel {
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
});
But in this case I have to copy and paste the TRDGenericImageViewModel generation in every ViewModel and thats not the way it should goes. If something changed I have to modify all related classes.
So is there a way to avoid this exception?
Using .AsEnumerable() prior lambda is not possible due to selects later in that query.
var query = UserRepository
.Get(Id)
.AsEnumerable()
.Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel
{
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
Statistics = new TRDUserStatisticsViewModel
{
PostCount = a.Posts.Count(),
CommentCount = a.Comments.Count(),
ImageCount = a.Images.Count(),
VideoCount = a.Videos.Count(),
VoteCount = a.PostVotes.Count(),
}
});
When calling .AsEnumerable() only the entities included by the Include statement are counted. But if the user has more than 1000 Posts and 10.000 Votes the query is a data nightmare.

Pass string variable as query parameter to LINQ query

I am attempting to pass a string variable via a POST request in my MVC controller into a LINQ query. I have tried a number of variations on the dynamic LINQ examples(which seems to be the primary recommended method.
EXPECTED RESULT
An IEnumerable array which contains results based on specified SiteID displayed in an ng-repeat.
NON-DYNAMIC-LINQ QUERY
public IEnumerable<SpecialsViewModel> GetAllSpecialsBySiteID(string siteID)
{
var query = (from s in _calcContext.Specials
where s.SiteID == siteID
select new SpecialsViewModel
{
ID = s.ID,
SiteID = s.SiteID,
Title = s.Title,
Description = s.Description,
Price = s.Price,
Position = s.Position,
EffectiveDate = s.EffectiveDate,
CreationDate = s.CreationDate,
Status = s.Status,
LastUpdated = s.LastUpdated
}).ToList();
return query;
}
RESULT
No results returned.
DYNAMIC LINQ QUERY
public IEnumerable<SpecialsViewModel> GetAllSpecialsBySiteID(string siteID)
{
var query = (from s in _calcContext.Specials
select new SpecialsViewModel
{
ID = s.ID,
SiteID = s.SiteID,
Title = s.Title,
Description = s.Description,
Price = s.Price,
Position = s.Position,
EffectiveDate = s.EffectiveDate,
CreationDate = s.CreationDate,
Status = s.Status,
LastUpdated = s.LastUpdated
});
query = query.Where("SiteID=#0", siteID);
return query;
}
RESULT
No results returned.
If both of your linq queries fail to return results, I suspect the value you are passing in is not what you expect. Try hardcoding a value for siteID to a known good value and see if that works. (I prefer the first query, but both are fine)
try to cast siteID to int and pass to where clause
eg: where s.SiteID == (int32?)siteID

Find all duplicate records in SQL table with Entity Framework by Multiple colums

I try to refactor this query to an EF query:
SELECT
PS_Adressen.AdName, PS_Adressen.AdVorname,
PS_Adressen.AdStrasse, PS_Adressen.AdStrasseNr, PS_Adressen.AdPLZ6
FROM
PS_Besuch
JOIN
PS_Adressen ON PS_Besuch.BeAdNr = PS_Adressen.Adnr
WHERE
BeAbMonat = #Month
AND BeHostessNr = #Nr
AND (PS_Besuch.BeKoffer = 1 OR PS_Besuch.BeKoffer = 2
OR PS_Besuch.BeKoffer = 3)
GROUP BY
PS_Adressen.AdName, PS_Adressen.AdVorname,
PS_Adressen.AdStrasse, PS_Adressen.AdStrasseNr, PS_Adressen.AdPLZ6
HAVING
COUNT(BeNr) > 1
I found this post: Find all duplicate records in SQL table with Entity Framework
But this only enables me to check if a single value like the name is duplicate, but not different columns.
I started with:
var query = from visit in db.Visits
join address in db.Addresses on visit.AddressId equals address.Id
group address by new {
address.Name,
address.Prename,
address.Street,
address.StreetNr,
address.Zip,
address.ZipLong,
visit.VisitNr
} into temp
select new {
Name = temp.Key.Name,
Prename = temp.Key.Prename,
Street = temp.Key.Street,
StreetNr = temp.Key.StreetNr,
Zip = temp.Key.Zip,
ZipLong = temp.Key.ZipLong,
VisitNr = temp.Key.VisitNr
};
var list = query.Where(x => x.VisitNr.Count() > 1).ToList();
But here the where clause is not correct. Something with the Count seems wrong..
Can anyone tell me what I'm doing wrong?
Thanks
NPadrutt
Try this code:
var query = from visit in db.Visits
join address in db.Addresses on visit.AddressId equals address.Id
group address by new {
address.Name,
address.Prename,
address.Street,
address.StreetNr,
address.Zip,
address.ZipLong
} into temp
select new {
Name = temp.Key.Name,
Prename = temp.Key.Prename,
Street = temp.Key.Street,
StreetNr = temp.Key.StreetNr,
Zip = temp.Key.Zip,
ZipLong = temp.Key.ZipLong,
RecCount = temp.Count()
};
var list = query.Where(x => x.RecCount > 1).ToList();

Categories