I have a sql server procedure I am accessing with Linq to Sql. When I execute the query I am getting the error 'The Query Results cannot be enumerated more than once'. The parameter is entered in the txtName text box and the results are displayed in the lstName listview.
public void GetSearchString()
{
Data.Database.FRCDatabaseDatacontext context =
new Data.Database.FRCDatabaseDatacontext();
var result = context.GetSearchProcedure(txtName.Text);
foreach (GetSearchProcedureResult search in result)
if ( search.UserGuid ==
Workspace.Instance.ActiveUser.CurrentUserActiveDirectoryGuid)
{
lstName.ItemsSource = result.ToList();
}
}
This method will return every result, but I want to return the results where the guids match.
Thanks!
Data.Database.FRCDatabaseDatacontext context =
new Data.Database.FRCDatabaseDatacontext();
var result = context.GetSearchProcedure(txtName.Text);
lstName.ItemsSource = result.ToList();
You are trying to enumerate the data more than once - once in the foreach and then again with the .ToList() for each matching UserGuid, which is causing the error.
Perhaps this LINQ select statement will help:
public void GetSearchString()
{
Data.Database.FRCDatabaseDatacontext context = new Data.Database.FRCDatabaseDatacontext();
lstName.ItemsSource = (from s in context.GetSearchProcedure(txtName.Text)
where s.UserGuid == Workspace.Instance.ActiveUser.CurrentUserActiveDirectoryGuid
select s).ToList();
}
public void GetSearchString()
{
var context = new Data.Database.FRCDatabaseDatacontext();
var result = context.GetSearchProcedure(txtName.Text);
var itemSource = result.ToList();
foreach (GetSearchProcedureResult search in itemSource)
if (search.UserGuid == Workspace.Instance.ActiveUser.CurrentUserActiveDirectoryGuid)
{
lstName.ItemsSource = itemSource;
}
}
Anyway, It will be better to pass the parameter to procedure instead recalculating it in the code
Calling .ToList on an IQueryable causes it to be materialized (i.e. the query is passed on to the back-end provider). Obviously, this is something that you'd probably prefer to only do once, as it's likely to be an expensive operation. This is what the error is trying to tell you.
If you only call .ToList once and store the result, the problem should go away.
Related
I am trying to remove duplicate code throughout my project and I am at a standstill trying to figure this out. What I am trying to do is create a base linq query that will be reused to add things like Where, Take...etc in multiple different methods.
public IQueryable<Object> FooLinq(int id)
{
using (var ctx = new dbEntities())
{
var results =
(from account in ctx.account
join memberProducts in ctx.tblMemberProducts on account.Id equals memberProducts.AccountId
orderby account.date descending
select new{account,memberProducts}).ToList();
return results;
}
}
So that would be by base query above and I would have a seperate method that would reuse VioLinq but this time would use a where clause in it.
public List<IncomingViolations> Foo1(int id)
{
//Linq query FooLinq() where Name == "Bob"
}
You'll need to do two things:
Return the query prior to materializing it.
Make sure the context is still in scope when the final query is materialized.
These two requirements will play off each other somewhat, and there are a number of approaches you can take to meet them.
For example, you could make your method take the context as a parameter, forcing the caller to provide it and manage its lifecycle.
public IQueryable<AccountInfo> FooLinq(DbEntities ctx, int id)
{
return
from account in ctx.account
orderby account.date descending
select new AccountInfo()
{
Name = account.Name,
Mid = account.MemberID,
Date = account.Date,
Address = account.Address,
};
}
public List<IncomingViolations> Foo1(int id)
{
using(var ctx = new dbEntities())
{
//Linq query FooLinq() where Name == "Bob"
return FooLinq(ctx).Where(v => v.Name == "Bob").ToList();
}
}
You could alternatively inject the context as a constructor-injected dependency, and use a DI framework to manage the context's lifecycle.
You can do it as Queryable then add conditions to it.
For example:
public List<account> GetAccountsByName(string name, bool usePaging, int offset = 0, int take = 0) {
var query = GetMyQuery();
query = query.Where(x => x.Name == name);
query = query.OrderBy(x => x.Name);
if(usePaging) {
query = query.Take(take).Skip(offset);
}
query = PrepareSelectForAccount(query);
return query.ToList(); .
}
public IQueryable<account> GetMyQuery(){
return ctx.account.AsQueryable();
}
public IQueryable<account> PrepareSelectForAccount(IQueryAble<account> query){
return query.Select(select new AccountInfo()
{
Name = account.Name,
Mid = account.MemberID,
Date = account.Date,
Address = account.Address,
}
);
}
Sure, but don't call .ToList(), and return IQueryable<T> instead of List<T>. LINQ is based on the concept of deferred execution which means the query is not actually performed until the enumerable is iterated over. Until then, all you have done is built an object which knows how to do the query when the time comes.
By returning an IQueryable<T> from a function that sets up the "basic query," you are then free to tack on additional LINQ methods (such as .Where() or .Take()) to produce a modified query. At this point you are still simply setting up the query; it is actually performed only when you iterate over the enumerable, or call something like .ToList() which does that for you.
I'm using jquery datatables plugin in my mvc application. filtering works perfectly against the fields with string datatype. but i cant use filtering against non string fields without enumerating the results first. here is my code.
public List<MerchAgreementMain> getAgreementbyAccountNo(jQueryDataTableParamModel model)
{
var entity = new CCMSEntities();
var query = (from _first in entity.First
from _second in entity.Second
where _first.No == _second.No
select new MerchAgreementMain
{
AcctNo = _Account.AcctNo, // datatype long
BusnName = _Account.BusnName,
CreatedBy = _Account.CreatedBy,
CreationDate = _Account.CreationDate ?? DateTime.MinValue,
Status = _Reflib.Descp
});
if (model.sSearch != null)
{
var x = query.AsEnumerable().Where(p => Convert.ToString(p.AcctNo).Contains(model.sSearch) || p.BusnName.Contains(model.sSearch) || p.CreatedBy.Contains(model.sSearch)||
p.Status.Contains(model.sSearch));
this.displayRecods = x.Count();
return x.ToList();
}
//var result = query.ToPagedList(Convert.ToInt16(model.sEcho),Convert.ToInt16(model.iDisplayLength));
return query.ToList();
}
this works fine. but the issue is that the query is enumerated first before applying the filters and the database contains thousands of records; that seems to be a bad practice and can take some delay before showing the filtered result. how do i apply the filter in an Iqueryable for long?
Instead of
query.AsEnumerable().Where(....
apply filters directly.
query.Where(
Please see: IQueryable vs. IEnumerable in terms of LINQ to SQL queries
Since you are doing AsEnumerable your query is iterating first and then applying the filter. If you apply the filter without AsEnumerable then the query is IQueryable and it will apply the filters only when iterating over the resultset.
I'm trying to emulate a SQL IN statement on a series of SQL LIKE statements using LINQ (C#).
I start off with a IQueryable<user> object named Query which has not yet been 'filtered'.
I'm just starting to post onto Stack Overflow so please be patient with me :)...
ObjectQuery<user> Context = this.Context.users;
IQueryable<user> Query = (IQueryable<user>)Context;
// create a BLANK clone of the FULL list (Query)
var QueryFinal = Query.Where(i => i.ID == 0);
foreach (String Item in userFilter.Name.Contains)
{
// based on the FULL list (Query) return all records that apply to this item & then append results to the final list
QueryFinal = QueryFinal.Concat(Query.Where(i => i.Name.Contains(Item)));
}
return QueryFinal.ToList();
I thought that on each iteration, the result set being returned on the Query.Where statement would be appended to the QueryFinal list, which it does, but for some reason, on each subsequent iteration, it appears to overwrite all the previous records which are supposed to be 'stored for safe-keeping' in the final list. I've have also tried using .Union but still not the results I was hoping for. All it seems to return is the last result set, not all of the appended result sets together. Anyone spot what I'm doing wrong?
Because of deferred execution, when you call QueryFinal.ToList();, it will be using the last value of Item - this is a pretty common problem when doing these sorts of queries in a foreach loop.
In your case, something like this might help:
foreach (String Item in userFilter.Name.Contains)
{
string currentItem = Item;
// based on the FULL list (Query) return all records that apply to this item & then append results to the final list
QueryFinal = QueryFinal.Concat(Query.Where(i => i.Name.Contains(currentItem)));
}
try this:
var query = this.Context.users.Where(u =>
userFilter.Name.Contains.Any(c => u.Name.Contains(c))
);
I have the following function (which is hosted in a WCF service, if that matters):
public List<IceVsRepositoryFile> GetRepositoryFilesByRepositoryId(int repId)
{
var entity = new IceVSEntities();
var files = from p in entity.Files where p.RepositoryId == repId select p.FileId;
List<long> iList = files.ToList();
var repFiles = from p in entity.RepositoryFiles where iList.Contains(p.FileId) select p;
if (!repFiles.Any())
return null;
var retFiles = repFiles.ToList().Select(z => new IceVsRepositoryFile
{
FileId = (int)z.FileId,
RollbackFileId = (int)z.RollbackFileId,
UserId = (int)z.UserId,
FileContents = z.FileContents,
ChangeDescription = z.ChangeDescription
}).ToList();
return retFiles;
}
When I run this function I am getting the following an error that says "LINQ to Entities does not recognize the method 'Boolean Contains(Int64)' method and this method cannot be translated into a store expression.
I understand why I am getting the error message. My question is, how can I rewrite my query to make this work as expected? My backend database, if it matters, if SqlLite 3. I am using .NET 3.5.
The contains you used is for List, it's not in IEnumerable so it can't be converted to corresponding sql query. Instead you can use Any, ... like:
iList.Any(x=>x == p.FileId) (or use related property)
Also instead of doing:
List<long> iList = files.ToList();
use files.Any... in your query to prevent from too many fetching from DB. Actually use IEnumerable functions instead of List functions.
I believe a join can do this:
public List<IceVsRepositoryFile> GetRepositoryFilesByRepositoryId(int repId)
{
var entity = new IceVSEntities();
var repFiles = from file in entity.Files where file.RepositoryId == repId join repFile in entity.RepositoryFiles on repFile.FileId equals file.FileId select repFile;
var retFiles = // as before
return retFiles;
}
Do you have a relationship between Files and RepositoryFiles? If so, it would be easier to do something like this:
var repFiles = from p in entity.RepositoryFiles where p.File.RepositoryId == repId select p;
This will avoid the problems with being unable to translate the query to SQL.
This linq works fine:
return (from page in db.WebPages
where pageids.Contains(page.page_id)
select new Result
{
a = page.a,
b = page.b
});
However I would like to do something like this which does not work:
return (from page in db.WebPages select GetResult(page));
public Result GetResult(WebPage page)
{
return new Result
{
a = page.a,
b = page.b
};
}
This gives an error of no supported translation to Linq.
The reason that I want to do this is because the Result is more complex and the code is done a lot of times so to avoid writing the same thing in every linq select new {} clause.
Try something like the following:
private static IQueryable<Result> toResults(IQueryable<WebPage> query)
{
return query.Select(item => new Result
{
//...
}
}
Usage:
return toResults(db.WebPages);
Organizing them like this has worked for me with other query providers, although I haven't worked with Linq-to-SQL personally.
You are trying to execute the method GetResult() in the scope of the database. Linq to Sql / Entities is translated into SQL queries where your method will not work. You will have to go with approach A. Alternatively you can force execution of the later part of your query as Linq to Objects using AsEnumerable():
return (from page in db.WebPages
where pageids.Contains(page.page_id)).AsEnumerable()
.Select(GetResult);
Try this?
return db.WebPages.Where( x=> x.Contains(page.page_id).Select(x => GetResult(x));