Entity Framework: check if object exists and check if it has an associated object - entity-framework

In a many-to-many relationship between File and Category I want to check if a file exists and if so, if it has any categories (because there's a chance it may not have any) :
public bool existsInDBAndHasCategories(string path)
{
using (WinFileContextContainer c = new WinFileContextContainer())
{
return c.File.Any((o => o.path == path));
}
}
This checks if a file with this path has a record in the database. I got this from a thread on this site. Truth be told I am still not good with LINQ and lambdas, so I don't know how to extend it to give me BOOLEAN for any categories as well. Thanks in advance for the time.

You just have to add another condition to your method (Assuming you have defined Categories as a list of Category in File class) :
return c.File.Any((o => o.path == path && o.Categories.Any()));

If you are not familiar with Lamba, start learning simple LinQ, before you move on to lambda inside Linq queries.
You code shoud be like this:
public bool existsInDBAndHasCategories(string path)
{
using (WinFileContextContainer c = new WinFileContextContainer())
{
var query = from f in c.File
where f.Path == path &&
(f.Categories != null || f.Categories.Count != 0)
select f;
return (f.Count != 0)
}
}

Related

LINQ contains and fix

I have a LINQ query
var age = new int[]{1,2,3};
dbContext.TA.WHERE(x=> age.Contains( x.age)).ToList()
In an online article #11 (https://medium.com/swlh/entity-framework-common-performance-mistakes-cdb8861cf0e7) mentioned it is not a good practice as it creates many execution plan at the SQL server.
In this case, how should LINQ be revised so that I can do the same thing but minimize the amount of execution plans generated?
(note that I have no intention to convert it into a stored procedure and pass & join with the UDT as again it requires too many effort to do so)
That article offers some good things to keep in mind when writing expressions for EF. As a general rule that example is something to keep in mind, not a hard "never do this" kind of rule. It is a warning over writing queries that allow for multi-select and to avoid this when possible as it will be on the more expensive side.
In your example with something like "Ages", having a hard-coded list of values does not cause a problem because every execution uses the same list. (until the app is re-compiled with a new list, or you have code that changes the list for some reason.) Examples where it can be perfectly valid to use this is with something like Statuses where you have a status Enum. If there are a small number of valid statuses that a record can have, then declaring a common array of valid statuses to use in an Contains clause is fine:
public void DeleteEnquiry(int enquiryId)
{
var allowedStatuses = new[] { Statuses.Pending, Statuses.InProgress, Statuses.UnderReview };
var enquiry = context.Enquiries
.Where(x => x.EnquiryId == enquiryId && allowedStatuses.Contains(x.Status))
.SingleOrDefault();
try
{
if(enquiry != null)
{
enquiry.IsActive = false;
context.SaveChanges();
}
else
{
// Enquiry not found or invalid status.
}
}
catch (Exception ex) { /* handle exception */ }
}
The statuses in the list aren't going to change so the execution plan is static for that context.
The problem is where you accept something like a parameter with criteria that include a list for a Contains clause.
it is highly unlikely that someone would want to load data where a user could select ages "2, 4, and 6", but rather they would want to select something like: ">=2", or "<=6, or "2>=6" So rather than creating a method that accepts a list of acceptable ages:
public IEnumerable<Children> GetByAges(int[] ages)
{
return _dbContext.Children.Where(x => ages.Contains( x.Age)).ToList();
}
You would probably be better served with ranging the parameters:
private IEnumerable<Children> GetByAgeRange(int? minAge = null, int? maxAge = null)
{
var query = _dbContext.Children.AsQueryable();
if (minAge.HasValue)
query = query.Where(x => x.Age >= minAge.Value);
if (maxAge.HasValue)
query = query.Where(x => x.Age <= maxAge.Value);
return query.ToList();
}
private IEnumerable<Children> GetByAge(int age)
{
return _dbContext.Children.Where(x => x.Age == age).ToList();
}

Why does this query always return ALL records?

I'm using WCF RIA in a Lightswitch project to create some query results. This query brings back all results regardless. I cannot make it filter the records based on the parameter passed (string Town).
public IQueryable<Enquiries> TestQuery(string Town)
{
List<Enquiries> riaenqs = new List<Enquiries>();
var enqs = this.Context.ClientEnquiries
.Include("Client")
.Include("Client.Town")
.OrderBy(enq => enq.Id);
if (Town != null)
{
enqs.Where(enq => enq.Client.Town.TownName == Town);
}
foreach (ClientEnquiry item in enqs.ToList())
{
Enquiries enq = new Enquiries();
enq.Id = item.Id;
enq.ClientName = item.Client.FirstName + " " + item.Client.Surname;
enq.Town = item.Client.Town != null ? item.Client.Town.TownName : null;
riaenqs.Add(enq);
}
return riaenqs.AsQueryable();
}
During debugging I can see that the Town is correctly populated and I can see that the query is built accordingly if Town is not null. However, when I hit the foreach statement where the linq to ef query is executed I always get all the results. I just cannot figure out where I'm slipping up.
The LINQ methods like the Where do not modify the collection/expression but always returning a new one.
So you need to reassign the result of the Where to your original variable enqs:
if (Town != null)
{
enqs = enqs.Where(enq => enq.Client.Town.TownName == Town);
}

declare variable to store linq entity for conditional statements

I am trying to look up record using if I have the key then use Find if not use Where
private ApplicationDbContext db = new ApplicationDbContext();
public bool DeactivatePrice(int priceId = 0, string sponsorUserName = "")
{
var prices = db.BeveragePrices;
// if we have an id then find
if (priceId != 0)
{
prices = prices.Find(priceId);
}
else
{
prices = prices.Where(b => b.UserCreated == sponsorUserName);
}
if (prices != null)
{
// do something
}
return true;
I get the following error for
prices = prices.Find(priceId);
Cannot convert app.Model.BeveragePrices from system.data.entity.dbset
I am copying the pattern from this answer but something must be different.
Seems you forgot to put a predicate inside the Find function call. Also you need to do ToList on the collection. The second option is a lot more efficient. The first one gets the whole collection before selection.
Another note commented by #Alla is that the find returns a single element. So I assume another declaration had been made for 'price' in the first option I state down here.
price = prices.ToList.Find(b => b.PriceId == priceId);
Or
prices = prices.Select(b => b.PriceId == priceId);
I assume the field name is PriceId.

IQueryable in foreach - open DataReader error

When i run this code
public PartialViewResult GetHardware()
{
IQueryable<Hardware> hardware = db.Hardwares;
HardwareState hwState = new HardwareState();
IQueryable<IGrouping<string, Hardware>> groupByCategory = hardware.GroupBy(g => g.Category);
foreach (IGrouping<string, Hardware> group in groupByCategory)
{
hwState.GroupName = group.Key;
hwState.GroupUnitsCount = group.Count();
hwState.StorageReservedCount
= group.Where(m =>
m.Place.IsStorage == true &&
m.PlaceID != (int)Constants.HardwareState.Created &&
m.HardwareState == (int)Constants.HardwareState.Reserved).Count();
}
return PartialView(hwState);
}
I get an error about that the navigation property m.Place = null
when i transfer some of the text with the code of the foreach block
public PartialViewResult GetHardware()
{
IQueryable<Hardware> hardware = db.Hardwares;
HardwareState hwState = new HardwareState();
IQueryable<IGrouping<string, Hardware>> groupByCategory = hardware.GroupBy(g => g.Category);
hwState.StorageReservedCount
= hardware.Where(m =>
m.Place.IsStorage == true &&
m.PlaceID != (int)Constants.HardwareState.Created &&
m.HardwareState == (int)Constants.HardwareState.Reserved).Count();
foreach (IGrouping<string, Hardware> group in groupByCategory)
{
hwState.GroupName = group.Key;
hwState.GroupUnitsCount = group.Count();
}
return PartialView(hwState);
}
,the navigation property is not set to null and the error does not appear
Extension methods such as .AsQueryable or Include(x => x.Place) do not help me
How can i solve this problem?
UPDATE: If i change the type to IEnumerable instead IQueryable it begins to work!
but i would like to work with the IQueryable type
UPDATE2: I'm sorry, i did not put it correctly when i wrote that the error is an empty navigation property. Error that appears in fact
"There is already an open DataReader associated with this Command which must be closed first."
As described in this answer it is because: (quote)
"Another scenario when this always happens is when you iterate through
result of the query (IQueryable) and you will trigger lazy loading for
loaded entity inside the iteration."
But it does not say how to solve the problem without using ToList () or MARS

The 'ArrayIndex' LINQ expression node type is not supported by LINQ to Entities - using interface & ReportViewer

a quick question really.
I'm struggling to implement Linq2Entities statement that could take more than one value for a particular "field". I'm passing a number of strings to the getClientsProjected() I can easily compare single value. But I've got on my page multiple dropdown and out of that I get string separated with coma I then later use to split it to string[] e.g. __ACCOUNT_SITE = "1234,5678" (see the code below) I've tried for/foreach/contains none of which worked...
public IQueryable<ClientViewModel> getClientsProjected(string __ACCOUNT_SITE, string __ACCOUNT)
{
var projectedClients = from c in getClosedSRs()
select new ClientViewModel
{
_ACCOUNT_ID_CSR = c.ACCOUNT_ID_CSR,
_ACCOUNT = c.ACCOUNT,
_ACCOUNT_FAMILY = c.ACCOUNT_FAMILY,
...
...
_ACCOUNT_SITE = c.ACCOUNT_SITE
};
if (String.IsNullOrEmpty(__ACCOUNT) != true && __ACCOUNT != "ALL")
{
//this works fine as an __ACCOUNT is of a single value
projectedClients = projectedClients.Where(c => c._ACCOUNT == __ACCOUNT);
}
if (String.IsNullOrEmpty(__ACCOUNT_SITE) != true && __ACCOUNT_SITE != "ALL")
{
String[] splitSites = __ACCOUNT_SITE.Split(',');
//????????????????????????????????????????????????
}
return projectedClients;
}
Now, to most of you this will make complete sense. I've read many articles but did not find a proper answer. I however can't use Linq2SQL as already built my entire site using L2E, interface and ReportViewer.
Any workaround?
If you are trying to filter projectedClients based on the values in splitSites, then use:
if (String.IsNullOrEmpty(__ACCOUNT_SITE) != true && __ACCOUNT_SITE != "ALL")
{
String[] splitSites = __ACCOUNT_SITE.Split(',');
projectedClients = projectedClients.Where(x => splitSites.Contains(x._ACCOUNT);
}