i have a problem with some data i retrievied from db with linq.
When I try to access data I obtain the following exception:
System.ObjectDisposedException : The istance of ObjectContext was deleted and is not possible to use it again for action that need a connection.
This is the code:
using (ProvaDbEntities DBEntities =
new ProvaDbEntities(Utilities.ToEntitiesConnectionString()))
{
ObjectQuery<site> sites = DBEntities.site;
IEnumerable<site> q = from site in sites
select site;
{
ObjectQuery<auction> auctions = DBEntities.auction;
IEnumerable<auction> q1 = from auction in auctions
where auction.site == this.Name
select auction;
IEnumerable<IAuction> res = q1.Cast<IAuction>();
return res;
}
}
catch(Exception e)
{
throw new UnavailableDbException("[GetAuctions]" + e.Message);
}
Someone can help me???
Tanks
Fabio
Yes - you're returning a result which will be lazily evaluated - but you're disposing of the data context which would be used to fetch the results.
Options:
Load the results eagerly, e.g. by calling ToList on the result
Don't dispose of the context (I don't know what the situation is in the Entity Framework; you could get away with this in LINQ to SQL, but it may not be a good idea in EF)
Dispose of the context when you're finished with the data
In this case I'd suggest using the first option - it'll be safe and simple. As you're already filtering the results and you're casting to IEnumerable<IAuction> anyway, you're unlikely to get the normal downsides of materializing the query early. (If it were still IQueryable<T>, you'd be throwing away the ability to add extra bits to the query and them still be translated to SQL.)
Related
Admittedly I don't perform lots of LINQ queries. Therefore I'm uncertain whether the problem I see is due to an obvious LINQ blunder or a legitimate Mongo driver problem (I use 10Gen 1.9.2 C# driver). In the below code I get an error indicating invalid where clause for .where(ques => unAnswered...). Code compiles fine but generates runtime error stating "unsupported where clause". Am I up against a driver limitation or is my LINQ bad?
public IEnumerable<QuestionDataModel> getUnanswered(String username, Category cat)
{
IQueryable<QuestionDataModel> questions =
from e in this.questionCollection.AsQueryable<QuestionDataModel>()
where (e.questionCategory == cat)
select e;
IQueryable<AnswerDataModel> answers =
from e in this.answerCollection.AsQueryable<AnswerDataModel>()
where (e.questionCategory == cat && e.username == username)
select e;
IEnumerable<QuestionDataModel> filteredquestionslist = null;
if (answers.Count()==0) // it's possible the user has not answered anything
filteredquestionslist = questions.ToList();
else
filteredquestionslist = questions.Where(ques => unAnswered(ques, ref answers)).ToList();
return filteredquestionslist;
}
private bool unAnswered(QuestionDataModel qdm, ref IQueryable<AnswerDataModel> answer_queryable)
{
bool retval;
retval = answer_queryable.Any(ans => ans.questionID == qdm.questionID) ? false:true;
return retval;
}
You can't combine two collections in a single query like this with MongoDB - there are no join operations in the database. (You also generally can't use your own method like that in LINQ since they don't translate into SQL (or any other database) but that's a separate issue and even if you fixed that it still wouldn't help here. unAnswered question cannot be translated into Mongo a query).
You must either iterate over one collection, performing the other query and yield return the results you want (i.e. the join happens not in the database but on the computer making the query), or you could denormalize the data in some way such that you can query a single collection to get the results. Of if the number of questions is really small you could possibly load them into a list using .ToList() and then operating on that list in memory.
Why isn't the exception triggered? Linq's "Any()" is not considering the new entries?
MyContext db = new MyContext();
foreach (string email in {"asdf#gmail.com", "asdf#gmail.com"})
{
Person person = new Person();
person.Email = email;
if (db.Persons.Any(p => p.Email.Equals(email))
{
throw new Exception("Email already used!");
}
db.Persons.Add(person);
}
db.SaveChanges()
Shouldn't the exception be triggered on the second iteration?
The previous code is adapted for the question, but the real scenario is the following:
I receive an excel of persons and I iterate over it adding every row as a person to db.Persons, checking their emails aren't already used in the db. The problem is when there are repeated emails in the worksheet itself (two rows with the same email)
Yes - queries (by design) are only computed against the data source. If you want to query in-memory items you can also query the Local store:
if (db.Persons.Any(p => p.Email.Equals(email) ||
db.Persons.Local.Any(p => p.Email.Equals(email) )
However - since YOU are in control of what's added to the store wouldn't it make sense to check for duplicates in your code instead of in EF? Or is this just a contrived example?
Also, throwing an exception for an already existing item seems like a poor design as well - exceptions can be expensive, and if the client does not know to catch them (and in this case compare the message of the exception) they can cause the entire program to terminate unexpectedly.
A call to db.Persons will always trigger a database query, but those new Persons are not yet persisted to the database.
I imagine if you look at the data in debug, you'll see that the new person isn't there on the second iteration. If you were to set MyContext db = new MyContext() again, it would be, but you wouldn't do that in a real situation.
What is the actual use case you need to solve? This example doesn't seem like it would happen in a real situation.
If you're comparing against the db, your code should work. If you need to prevent dups being entered, it should happen elsewhere - on the client or checking the C# collection before you start writing it to the db.
Thanks for checking this out.
My situation is that I have a system where the user can create custom filtered views which I build into a linq query on the request. On the interface they want to see the counts of all the views they have created; pretty straight forward. I'm familiar with combining multiple queries into a single call but in this case I don't know how many queries I have initially.
Does anyone know of a technique where this loop combines the count queries into a single query that I can then execute with a ToList() or FirstOrDefault()?
//TODO Performance this isn't good...
foreach (IMeetingViewDetail view in currentViews)
{
view.RecordCount = GetViewSpecificQuery(view.CustomFilters).Count();
}
Here is an example of multiple queries combined as I'm referring to. This is two queries which I then combine into an anonymous projection resulting in a single request to the sql server.
IQueryable<EventType> eventTypes = _eventTypeService.GetRecords().AreActive<EventType>();
IQueryable<EventPreferredSetup> preferredSetupTypes = _eventPreferredSetupService.GetRecords().AreActive<EventPreferredSetup>();
var options = someBaseQuery.Select(x => new
{
EventTypes = eventTypes.AsEnumerable(),
PreferredSetupTypes = preferredSetupTypes.AsEnumerable()
}).FirstOrDefault();
Well, for performance considerations, I would change the interface from IEnumerable<T> to a collection that has a Count property. Both IList<T> and ICollection<T> have a count property.
This way, the collection object is keeping track of its size and you just need to read it.
If you really wanted to avoid the loop, you could redefine the RecordCount to be a lazy loaded integer that calls GetViewSpecificQuery to get the count once.
private int? _recordCount = null;
public int RecordCount
{
get
{
if (_recordCount == null)
_recordCount = GetViewSpecificQuery(view.CustomFilters).Count;
return _recordCount.Value;
}
}
I am writing an application that works with MS SQL database via LINQ to SQL. I need to perform filtering sometimes, and occasionally my filtering conditions are too complicated to be translated into SQL query. While I am trying to make them translatable, I want my application to at least work, though slow sometimes.
LINQ to SQL data model is hidden inside repositories, and I do not want to provide several GetAll method overloads for different cases and be aware of what overload to use on upper levels. So I want to test my expression inside repository to be translatable and, if no, perform in-memory query against the whole data set instead of throwing NotSupportedException on query instantiating.
This is what I have now:
IQueryable<TEntity> table = GetTable<TEntity>();
IQueryable<TEntity> result;
try
{
result = table.Where(searchExpression);
//this will test our expression
//consuming as little resources as possible (???)
result.FirstOrDefault();
}
catch (NotSupportedException)
{
//trying to perform in-memory search if query could not be constructed
result = table
.AsEnumerable()
.Where(searchExpression.Compile())
.AsQueryable();
}
return result;
searchExpression is Expression<Func<TEntity, bool>>
As you see, I am using FirstOrDefault to try to instantiate the query and throw the exception if it cannot be instantiated. However, it will perform useless database call when the expression is good. I could use Any, Count or other method, and it may well be a bit less expensive then FirstOrDefault, but still all methods that come to my mind make a costly trip to database, while all I need is to test my expression.
Is there any alternative way to say whether my expression is 'good' or 'bad', without actual database call?
UPDATE:
Or, more generally, is there a way to tell LINQ to make in-memory queries when it fails to construct SQL, so that this testing mechanism would not be needed at all?
Instead of
result.FirstOrDefault();
would it be sufficient to use
string sqlCommand = dataContext.GetCommand(result).CommandText;
?
If the expression does not generate valid Sql, this should throw a NotSupportedException, but it does not actually execute the sqlCommand.
I think this will solve your problem:
IQueryable<TEntity> table = GetTable<TEntity>();
IQueryable<TEntity> result;
try
{
return table.Where(searchExpression).ToList();
}
catch (NotSupportedException)
{
//trying to perform in-memory search if query could not be constructed
return table
.AsEnumerable()
.Where(searchExpression.Compile())
.ToList();
}
So the method returns is the expression is converted to valid SQL. Otherwise it catches the exception and runs the query in memory. This should work but it doesn't answer your question if it's possible to check if a specific searchExpression can be converted. I don't think such a thing exists.
Don't know what's wrong here, when I run the application it says "Specified method is not supported" pointing at "var result in query" in foreach loop. Please help...
var query = from c in entities.Customer
select c.CustomerName;
List<string> customerNames = new List<string>();
foreach (var result in query)
{
customerNames.Add(result.ToString());
}
EDIT: using ToList() also gives the same error.
The reason for your error is scope, which is what the "method not supported" error is telling you.
This usually happens when using a Linq to [fill in the blank] ORM. So, I'm guessing your entities must be from an ORM tool, something like Entity Framework, and you are using something like Linq to Entities.
When using linq your query is not enumerated out until you access it, which for an ORM means hitting the database or other data repository. This delayed action can cause some strange behavior if you do not know it is there, such as this error.
But, you have local (non-linq) code and your query intertwined, so the linq to [] compiler does not know how to handle your local code when compiling the linq code. Thus the "method not supported" error - it is basically the same as referencing a private method from outside of the class, the method you called is unknown in the current scope.
In other words the compiler is trying to compile your query and hit the database when you do the result.ToString(), but does not know anything about the private variable of CustomerNames or the foreach method. The database logic and the local object logic have to be kept separate - completely resolve the database query results before using locally.
You should be able to write it like this:
var customerNames = entities.Customer.Select(c => c.CustomerName).ToList();
If you have to keep the foreach (for more complicated logic, not for this simple of an example) you still need to resolve the Linq to [] portion (by forcing it to enumerate the query results) prior to involving any non-linq code:
var query = from c in entities.Customer
select c.CustomerName;
var qryList = query.ToList();
List<string> customerNames = new List<string>();
foreach (var result in qryList)
{
customerNames.Add(result.ToString());
}
Can you try using just the ToList() method instead of the foreach?
List<string> customerNames = query.ToList();
If the problem is not ToString() as Gart mentioned my second suspicious falls in c.CustomerName. Is this a custom property in your partial class?
Also, the stacktrace of the exception must surly show what is the unsupported method.
Try removing .ToString() and see if this will work:
foreach (var result in query)
{
customerNames.Add(result);
}
Seems like that the root of the problem lies deep inside LINQ-to-SQL query translation mechanism. I suppose the translation engine tries to translate .ToString() into SQL and fails there.
try this
var query = from c in entities.Customer
select c.CustomerName;
List<string> customerNames = new List<string>();
query.ToList().ForEach(r=>customerNames.Add(r));