Linq2Sql IQueryable inside another IQueryable - c#

I'm trying to structure my code and be able to better maintain some of my LINQ queries. Actually I created a new helper class with some functions but I'm having some problems executing some of my IQueryable functions inside another IQueryable functions.
When I execute the function SupplierMappings.GetSupplierAssociatedRT(int supplierID) I'm getting the following error:
Method 'System.Linq.IQueryable`1[VMPortal.DataAccessLayer.CostCentre]
GetSupplierAssociatedCCPerRT(Int32, Int32)' has no supported translation to SQL.
What I'm doing wrong? Below is the part of related code:
public class SupplierMappings
{
private static DataLayer dl = DataLayer.GetDataContext();
public static IQueryable<ResourceType> GetSupplierAvailableRT(int supplierID)
{
return dl.ResourceTypes.Where(x =>
dl.SuppliersCompanies2.Any(y => y.SupplierID == supplierID
&& y.ResourceTypeID == x.ResourceTypeID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active));
}
public static IQueryable<ResourceType> GetSupplierAssociatedRT(int supplierID)
{
return GetSupplierAvailableRT(supplierID).Where(x =>
// Check if we have at least one CC associated with that RT
GetSupplierAssociatedCCPerRT(supplierID, x.ResourceTypeID).Count() >= 1);
}
public static IQueryable<CostCentre> GetSupplierAvailableCCPerRT(int supplierID, int rtID)
{
return dl.CostCentres.Where(x => x.StatusID == (short)CostCentersStatusEnum.Active
// Check than the supplier is mapped at supplier level at same company & RT
&& dl.SuppliersCompanies2.Any(y => y.CompanyID == x.CompanyID
&& y.SupplierID == supplierID
&& y.ResourceTypeID == rtID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active)
// Check than the PA is active
&& x.DeliveryGroup.StatusID == (short)DeliveryGroupsStatusEnum.Active);
}
public static IQueryable<CostCentre> GetSupplierAssociatedCCPerRT(int supplierID, int rtID)
{
return GetSupplierAvailableCCPerRT(supplierID, rtID).Where(x =>
dl.SuppliersCostCentre2.Count(y => y.SupplierID == supplierID
&& y.StatusID == (short)SuppliersCostCentreStatusEnum.Inactive
&& y.ResourceTypeID == rtID) != dl.SuppliersCompanies2.Count(y => y.SupplierID == supplierID
&& x.CompanyID == y.CompanyID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active
&& y.ResourceTypeID == rtID)
&& dl.SuppliersPracticeAreas.Count(y => y.SupplierID == supplierID
&& y.StatusID == (short)SuppliersPracticeAreaStatusEnum.Inactive
&& y.ResourceTypeID == rtID) != dl.SuppliersCompanies2.Count(y => y.SupplierID == supplierID
&& x.CompanyID == y.CompanyID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active
&& y.ResourceTypeID == rtID));
}
}

It is pity but in linq to sql you cannot use you defined methods that returns anything except expressions. And there is limited number of expressions that can be translated to sql query (this translations is defined in linqtosql library). So you cannot do what you try to do.
You could try to inline your functions.

Related

How to reuse a function inside a where in EF Core?

I have a query that select all Photos if its public, and only if friends if visibility is private in this code.
_communityDbContext
.Photos
.Where(x => (
x.Privacy == 1 ||
(x.Privacy == 2 &&
_communityDbContext.Friendships.FirstOrDefault(d => x.CreatedBy.Id == user1 && d.FriendUser.Id == user2) !=
null)
)
);
But I trying to extract this piece of code to reuse in another queries
_communityDbContext.Friendships.FirstOrDefault(d => x.CreatedBy.Id == user1 && d.FriendUser.Id == user2) != null
Is possible to call an function and make the code like that:
_communityDbContext
.Photos
.Where(x => (
x.Privacy == 1 ||
(x.Privacy == 2 &&
hasFriendship(x.CreatedBy.Id, user))
)
);
I fond a solution, the method invocation must be done to an expression, not to a function call. So I can extract that part of code by doing
public Expression<Func<Photo, bool>> Visible()
{
return x => (
x.Privacy == 1 ||
(x.Privacy == 2 &&
_communityDbContext.Friendships.FirstOrDefault(d => x.CreatedBy.Id == 3 && d.FriendUser.Id == 3) !=
null)
);
}
And calling this by
.where(Visible())

Getting List() From DB

public JsonResult SearchApplicantList(string SSession, string ApStatus, string StudyLevel, string SLPrograms)
{
ab db = new ab();
var SearchList = db.Students
.Where(x => x.SemesterSessioin == SSession
&& x.OverAllStatus == ApStatus
&& x.StudyLvl == StudyLevel
&& x.ProgPref1 == SLPrograms
&& x.ProgPref2 == SLPrograms
&& x.ProgPref3 == SLPrograms
&& x.progPref4 == SLPrograms).ToList();
return Json(SearchList, JsonRequestBehavior.AllowGet);
}
I want to get a list on the basis of parameters, but few cases the many parameters can be null, null means all.
How can I do this?
Programming. Step by step.
Let me explain:
There is no need to have only one line for the query.
var searchQuery = db.Students (possibly with .Where that is constant).
if (ApStatus != null) {
searchQuery = searchQuery.Where(x => x.OverAllStatus = ApStatus
}
Btw., plenty of spelling. It is App (not Ap) and Overall not OverAll - it is ONE word as per dictionary.
Anyhow, you can repeat adding where conditions as often as you want, then at the end materialize.
All where conditions are ANDed together.
This is one thing most people overlook - LINQ allows a TON of programming and manipulation of the query tree.
public JsonResult SearchApplicantList(string SSession, string ApStatus, string StudyLevel, string SLPrograms)
{
ab db = new ab();
var SearchList = db.Students;
SearchList = SearchList.Where(x => (x.SemesterSessioin == SSession || SSession ==null || SSession =="")
&& (x.OverAllStatus == ApStatus || ApStatus ==null || ApStatus =="")
&& (x.StudyLvl == StudyLevel || StudyLevel ==null || StudyLevel =="")
&& (x.ProgPref1 == SLPrograms || SLPrograms ==null || SLPrograms =="")
&& (x.ProgPref2 == SLPrograms || SLPrograms ==null || SLPrograms =="")
&& (x.ProgPref3 == SLPrograms || SLPrograms ==null || SLPrograms =="")
&& (x.progPref4 == SLPrograms|| SLPrograms == null|| SLPrograms == "").ToList();
return Json(SearchList, JsonRequestBehavior.AllowGet);
}
Apply filter by parameter/s which can not be null, for instance SSession and StudyLevel are those parameters which can not be null. So apply filter by them first
var SearchList = db.Students.Where(x => x.SemesterSessioin == SSession && x.StudyLvl == StudyLevel).ToList();
Then check rest of parameters one by one, for SLPrograms will be like:
if(!string.IsNullOrEmpty(SLPrograms))
{
SearchList = SearchList.Where(Apply Filter By SLPrograms).ToList();
}
And then for rest of Parameters. Remember, use if and if to check each parameters. Like
if(Param1)
{
//Code
}
if(Param2)
{
//Code
}

Return the same type for two views from datacontext

I have created two views that return exactly the same columns from the same tables. The only difference between the two views is they filter on different parameters. I have added these into my .dbml file
(Picture of views in dbml) This has then auto generated two classes for these two views.
In my code depending on the value of the property Filter one of the two views is queried, either current or previous. I need these views to be returned as the same type. So that IOrderedQueryable<> items has one return type.
Currently they are returning as either clientOrdersQueryCurrent or clientOrdersQueryPrevious. If I set IOrderedQueryable<> items to either one of these and attempt to cast the other type then this causes a runtime error.
IOrderedQueryable<> items;
bool filterQuery = false;
if (!string.IsNullOrEmpty(OrderNumber) || DateFrom != null || Dateto != null || !string.IsNullOrEmpty(TrackingNumber) || UserId != null)
{
filterQuery = true;
}
if (Filter == "Current")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else if (Filter == "Previous")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else
{
//Default call - current orders
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
The only thing I can currently think of to resolve this is to create a class and have the query map the result to the class after it returns.
What is the best way to do this?
The ORM I am currently using is NHibernate.
Better to map to some common class
IOrderedQueryable<CommonItem> items;
items = dataContext.clientOrdersQueryCurrents.Select(e => new CommonItem{...});
...
items = dataContext.clientOrdersQueryPrevious.Select(e => new CommonItem{...});
It can be AutoMapper's ProjectTo method
items = dataContext.clientOrdersQueryPrevious.ProjectTo<CommonItem>();

How to handle empty fields during searching / filtering data?

I'm struggling with data searching algorithm, which has to retrieve some data from the database using multiple fields. Each textbox provides a given parameter, stored in the database, accessed by Entity Framework. It's working when I enter data to all fields but if I leave any field empty it doesn't retrieve any record.
My question is - How to handle empty fields. If I leave any field without data, it should just not consider this parameter during selecting from the database and select data basing on non-null parameters.
This is what I've created so far:
[HttpPost]
public ViewResult Search(string brand, string model, int? manufactDateMin,
int? manufactDateMax, int? priceMin, int? priceMax, int? engineCapMin,
int? engineCapMax, string engineType, int page = 1)
{
IEnumerable<Car> foundItems = repository.Cars
.Where(c => brand == null || c.Brand == brand)
.Where(c => model == null || c.Model == model)
.Where(c => manufactDateMin == null || manufactDateMax == null || (c.ManufactDate.Year >= manufactDateMin) && (c.ManufactDate.Year < manufactDateMax))
.Where(c => priceMin == null || priceMax == null || (c.Price >= priceMin) && (c.Price < priceMax))
.Where(c => engineCapMin == null || engineCapMax == null || (c.EngineCapacity >= engineCapMin) && (c.EngineCapacity < engineCapMax))
.Where(c => engineType == null || c.EngineType == engineType)
.OrderBy(c => c.Id)
.Skip(PageSize * (page - 1))
.Take(PageSize);
CarListViewModel VMmodel = new CarListViewModel
{
Cars = foundItems,
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = foundItems.Count(),
},
CarType = null
};
return View("List", VMmodel);
}
Dont use a new where statement for each comparison. combine them into one. separate them into a new line for readability. Also, your parameters are defined as strings but your comparing it to null. You will have to pass a null for this to work. If you are passing the contents of a textbox or something then the string will be "" and not a null.
.Where(c => brand == "" || c.Brand == brand) &&
(model == "" || c.Model == model) &&
((manufactDateMin == null || manufactDateMax == null || (c.ManufactDate.Year >= manufactDateMin) && (c.ManufactDate.Year < manufactDateMax)) &&
(priceMin == null || priceMax == null || (c.Price >= priceMin) && (c.Price < priceMax))) &&
(engineCapMin == null || engineCapMax == null || (c.EngineCapacity >= engineCapMin) && (c.EngineCapacity < engineCapMax))) &&
(engineType == "" || c.EngineType == engineType))
I think you should do it separately like this
IQueryable<Car> query = repository.Cars;
if (!string.IsNullOrEmpty(brand))
{
query = query.Where(x => x.Brand == brand);
}
if (!string.IsNullOrEmpty(model ))
{
query = query.Where(x => x.Model == model );
}
and after all of them you put the take and skyp, it is faster and more readable

How can I write the following lambda expression in one line?

I want to fetch the records as follows
SearchResult.condition is null then fetch all the rows from Person
if SearchResult.condition is false then fetch the rows where PersonType column contains null value
if SearchResult.condition is true then fetch the rows where PersonType column contains non null value
struct SearchResult
{
public string Name;
public bool? condition;
}
Expression<Func<Person, bool>> expression;
if(condition==null)
{
expression= (a =>
(SearchResult.Name==null || a.Name == SearchResult.Name)
);
}
else if(condition.Value == true)
{
expression= (a =>
(SearchResult.Name==null || a.Name == SearchResult.Name)
&& a.PersonType != null)
}
else if(condition.Value == false)
{
expression= (a =>
(SearchResult.Name==null || a.Name == SearchResult.Name)
&& a.PersonType == null)
}
I want to write the expression in one expression instead of using if else conditions. Can u plz help me in it?
Well you can do it with a conditional operator, but you need to specify the type of the expression tree for each lambda expression:
var expression = condition == null
? (Expression<Func<Person, bool>>) a => SearchResult.Name == null ||
a.Name == SearchResult.Name
: condition.Value
? (Expression<Func<Person, bool>>) a => (SearchResult.Name == null ||
a.Name == SearchResult.Name) &&
a.PersonType != null
: (Expression<Func<Person, bool>>) a => (SearchResult.Name == null ||
a.Name == SearchResult.Name) &&
a.PersonType == null;
But assuming you're going to use this with a LINQ query, you'd be much better off with something like:
var query = foo.Where(a => SearchResult.Name == null ||
a.Name == SearchResult.Name);
if (condition != null)
{
query = condition.Value ? query.Where(a => a.PersonType != null)
: query.Where(a => a.PersonType == null);
}
As an aside, I'd strongly advise you to avoid writing mutable structs or using public fields.
You could shorten as:
expression = a =>
(SearchResult.Name == null || a.Name == SearchResult.Name) &&
(SearchResult.condition == null || Search.condition == (a.PersonType != null));

Categories

Resources