Linq query in next line - c#

Why line 2 of mycode can not be read :
var allResarchs = db.Researchs;
allResarchs.Where(a => a.ChiefManagerId == 1);
allResarchs.ToList();

You never set the value of Where statement to the variable.
var allResarchs = db.Researchs.Where(a => a.ChiefManagerId == 1).ToList();

You should assign results of LINQ queries:
var allResarchs = db.Researchs;
var filtered = allResarchs.Where(a => a.ChiefManagerId == 1);
var list = filtered.ToList();
Also you can do it in simplier way (if you do not need intermediate results):
var list = db.Researchs.Where(a => a.ChiefManagerId == 1).ToList();

Linq never changes the input sequence!
allResearches.Where(research => research.ChiefManagerId == 1);
This statement won't change allResearches. You could do the following. (by the way, I've changed the var into the actual returned types, so you understand better what is going on.)
IQueryable<Research> queryResearches = db.Researches;
IQueryable<Research> queryResearchesWithId1 = queryResearches
.Where(research => research.ChiefManagerId == 1);
List<Research> researchesWithId1 = queryResearchedWithId1.ToList();
Be aware, that until the last statement the query is not executed, there is no communication with the database yet. Only the last statement will actually contact the database.
Of course you can write it all in one statement. However, this won't improve performance very much:
var researchesWithId1 = db.Researches
.Where(research => research.ChiefManagerId == 1)
.ToList();

Related

How do I convert this looped code to a single LINQ implementation?

I am trying to optimise the code below which loops through objects one by one and does a database lookup. I want to make a LINQ statement that will do the same task in one transaction.
This is my inefficient looped code;
IStoreUnitOfWork uow = StoreRepository.UnitOfWorkSource.GetUnitOfWorkFactory().CreateUnitOfWork();
var localRunners = new List<Runners>();
foreach(var remoteRunner in m.Runners) {
var localRunner = uow.CacheMarketRunners.Where(x => x.SelectionId == remoteRunner.SelectionId && x.MarketId == m.MarketId).FirstOrDefault();
localRunners.Add(localRunner);
}
This is my very feable attempt at a single query to do the same thing. Well it's not even an attempt. I don't know where to start. The remoteRunners object has a composite key.
IStoreUnitOfWork uow = StoreRepository.UnitOfWorkSource.GetUnitOfWorkFactory().CreateUnitOfWork();
var localRunners = new List<Runners>();
var localRunners = uow.CacheMarketRunners.Where(x =>
x.SelectionId in remoteRunners.SelectionId &&
x.MarketId in remoteRunners.MarketId);
Thank you for looking
So you have an object m, which has a property MarketId. Object m also has a sequence of Runners, where every Runner has a property SelectionId.
Your database has CacheMarketRunners. Every CacheMarketRunner has a MarketId and a SelectionId.
Your query should return allCacheMarketRunners with a MarketId equal to m.MarketId and a SelectionId that is contained in the sequence m.Runners.SelectionId.
If your m does not have too many Runners, say less then 250, consider using Queryable.Contains
var requestedSelectionIds = m.Runners.Select(runner => runner.SelectionId);
var result = CacheMarketRunners.Where(cacheMarketRunner =>
cacheMarketRunner.MarketId == m.MarketId
&& requestedSelectionIds.Contains(cacheMarketRunner.SelectionId));
To improve performance, you need caching transaction results:
var marketRunners = uow.CacheMarketRunners.Where(x => x.MarketId == m.MarketId).ToList();
Transaction results regarding uow are stored in the List, such that you don't have transaction in the for loop. Hence performance should be improved:
var localRunners = new List<Runners>();
foreach(var remoteRunner in m.Runners) {
var localRunner = marketRunners.FirstOrDefault(x => x.SelectionId == remoteRunner.SelectionId);
localRunners.Add(localRunner);
}
You can even remove the for loop:
var localRunners = m.Runners.Select(remoteRunner => marketRunners.FirstOrDefault(x => x.SelectionId == remoteRunner.SelectionId)).ToList();

Why does EF load data from the database and ignores local changes?

I have the following code:
var existingParticipant = Context.CaseParticipants.Where(p => p.CaseId == caseId);
foreach (var cp in existingParticipant)
{
var ncp = caseParticipantList.First(a => a.Id == cp.Id);
cp.IsIncompetent = ncp.IsIncompetent;
cp.IsLeave = ncp.IsLeave;
cp.SubstituteUserId = ncp.IsPresent ? null : ncp.SubstituteUserId;
}
var withSubs = existingParticipant.Where(c => c.SubstituteUserId != null).ToList();
What surprised me is that the last line fetches the rows from the DB a second time, ignoring any changes I've just done in the previous lines, why is that, and how do I avoid it?
I think your problem is that your existingParticipant is a query and not a list. That query gets executed for the foreach, but existingParticipant still stays a query that will get executed on the database when calling ToList() again. To solve it execute the initial query straight away and that way you work in memory on your changed entities.
IList<...> existingParticipant = Context.CaseParticipants.Where(p => p.CaseId == caseId).ToList(); // Explicit executing of query
foreach (var cp in existingParticipant)
{
var ncp = caseParticipantList.First(a => a.Id == cp.Id);
cp.IsIncompetent = ncp.IsIncompetent;
cp.IsLeave = ncp.IsLeave;
cp.SubstituteUserId = ncp.IsPresent ? null : ncp.SubstituteUserId;
}
var withSubs = existingParticipant.Where(c => c.SubstituteUserId != null).ToList(); // Working in memory on list
The type of existingParticipants is IQueryable, that means you won't get the objects into memory but only a query itself working on database directly
If you want to process your objects into memory call .ToList() after
Context.CaseParticipants.Where(p => p.CaseId == caseId)

The method ‘Skip’ is only supported for sorted input in LINQ to Entities. The method ‘OrderBy’ must be called before the method ‘Skip’

Using Entity Framework 6.0.2 and .NET 4.5.1 in Visual Studio 2013 Update 1 with a DbContext connected to SQL Server:
I have a long chain of filters I am applying to a query based on the caller's desired results. Everything was fine until I needed to add paging. Here's a glimpse:
IQueryable<ProviderWithDistance> results = (from pl in db.ProviderLocations
let distance = pl.Location.Geocode.Distance(_geo)
where pl.Location.Geocode.IsEmpty == false
where distance <= radius * 1609.344
orderby distance
select new ProviderWithDistance() { Provider = pl.Provider, Distance = Math.Round((double)(distance / 1609.344), 1) }).Distinct();
if (gender != null)
{
results = results.Where(p => p.Provider.Gender == (gender.ToUpper() == "M" ? Gender.Male : Gender.Female));
}
if (type != null)
{
int providerType;
if (int.TryParse(type, out providerType))
results = results.Where(p => p.Provider.ProviderType.Id == providerType);
}
if (newpatients != null && newpatients == true)
{
results = results.Where(p => p.Provider.ProviderLocations.Any(pl => pl.AcceptingNewPatients == null || pl.AcceptingNewPatients == AcceptingNewPatients.Yes));
}
if (string.IsNullOrEmpty(specialties) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Specialties.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(degrees) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Degrees.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(languages) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Languages.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(keyword) == false)
{
results = results.Where(p =>
(p.Provider.FirstName + " " + p.Provider.LastName).Contains(keyword));
}
Here's the paging I added to the bottom (skip and max are just int parameters):
if (skip > 0)
results = results.Skip(skip);
results = results.Take(max);
return new ProviderWithDistanceDto { Locations = results.AsEnumerable() };
Now for my question(s):
As you can see, I am doing an orderby in the initial LINQ query, so why is it complaining that I need to do an OrderBy before doing a Skip (I thought I was?)...
I was under the assumption that it won't be turned into a SQL query and executed until I enumerate the results, which is why I wait until the last line to return the results AsEnumerable(). Is that the correct approach?
If I have to enumerate the results before doing Skip and Take how will that affect performance? Obviously I'd like to have SQL Server do the heavy lifting and return only the requested results. Or does it not matter (or have I got it wrong)?
I am doing an orderby in the initial LINQ query, so why is it complaining that I need to do an OrderBy before doing a Skip (I thought I was?)
Your result starts off correctly as an ordered queryable: the type returned from the query on the first line is IOrderedQueryable<ProviderWithDistance>, because you have an order by clause. However, adding a Where on top of it makes your query an ordinary IQueryable<ProviderWithDistance> again, causing the problem that you see down the road. Logically, that's the same thing, but the structure of the query definition in memory implies otherwise.
To fix this, remove the order by in the original query, and add it right before you are ready for the paging, like this:
...
if (string.IsNullOrEmpty(languages) == false)
...
if (string.IsNullOrEmpty(keyword) == false)
...
result = result.OrderBy(r => r.distance);
As long as ordering is the last operation, this should fix the runtime problem.
I was under the assumption that it won't be turned into a SQL query and executed until I enumerate the results, which is why I wait until the last line to return the results AsEnumerable(). Is that the correct approach?
Yes, that is the correct approach. You want your RDBMS to do as much work as possible, because doing paging in memory defeats the purpose of paging in the first place.
If I have to enumerate the results before doing Skip and Take how will that affect performance?
It would kill the performance, because your system would need to move around a lot more data than it did before you added paging.

i can not understand how this is working x => x.Id == _purchase.Centre.Id?

I can not understand the following line: x => x.Id == _purchase.Centre.Id
the code is here:
private void LoadCentreOptions()
{
MaterialCentreDataSource mcds = new MaterialCentreDataSource();
List<MaterialCentre> centres = mcds.GetAll() as List<MaterialCentre>;
_blankCentre = new MaterialCentre()
{
Name = Strings.JournalViewModel_CreditedPartyOption_NotSpecified
};
centres.Insert(0, _blankCentre);
_centreOptions = new ReadOnlyCollection<MaterialCentre>(centres);
if (_purchase.Centre == null)
_purchase.Centre = _blankCentre;
else
_purchase.Centre = _centreOptions.First(x => x.Id == _purchase.Centre.Id);
}
here at the debugging time i am watching that X.Id is incremented .How it is possible?
The api First is enumerating over the collection and applying the condition to check which one matches and then returns the first match.. So effectively the code translates to
foreach(var centreOption in _centreOptions)
{
if(centreOption.Id==_purchase.Centre.Id)
return centreOption;
}
and if you understand LINQ its basically short for
_centreOptions.Where(x => x.Id == _purchase.Centre.Id).First();
To understand the exact implementation of First read Jon Skeets blogpost.
This line runs for every element in the _centreOptions Enumerable:
_purchase.Centre = _centreOptions.First(x => x.Id == _purchase.Centre.Id);
And checking if the Id of object equals to _purchase.Centre.Id.
It will return the first element that satisfy the condition.
So you see two id in two iterations.

Linq - Retrieve a single value in a String

I use Asp.net 3.5 and EF 4.
I need find a specific row in my DataBase and display on a label a single value as string.
At the moment I use this code, it is working, so I find a single Object and read its properties.
var myAuthor = (from at in context.CmsAuthors
where at.AuthorId == myRow.AuthorId
select at).Single();
myAuthorNameLabel.Text = myAuthor.LastName;
I would like to know:
If there is another syntax in Linq to achieve the same result.
How to do it using Lamba?
Which approach would you suggest me?
Here's the method syntax (using lambdas)
myAuthorNameLabel.Text = context.CmsAuthors
.Where(at => at.AuthorId == myRow.AuthorId)
.Select(at => at.LastName)
.SingleOrDefault() ?? string.Empty;
You can use:
var myAuthorName =
(from at in context.CmsAuthors where at.AuthorId == myRow.AuthorId select at).Single().Select(a => a.LastName);
actually this would be even better:
var myAuthorName =
(from at in context.CmsAuthors where at.AuthorId == myRow.AuthorId select at).Select(a => a.LastName).Single();
Update
An example of how to use with Anonymous type:
var myAuthorNames =
(from at in context.CmsAuthors where at.AuthorId == myRow.AuthorId select at).Select( a => new {a.LastName, a.FirstName}).Single();

Categories