Why is my object being disposed? - c#

I'm trying to access SQL server using LINQ with c# in asp.net framework.
I'm writing a simple log-in form on a webpage. The following is my c# code to check if it is in the database.
public int getUserId(string un, string pw)
{
IEnumerable<int> query;
using (var context = new IngredientsLinqDataContext())
{
query = from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID;
}
if(query.Count() >= 1)
{
return query.Min();//a very serious kludge. Need to fix this
}
else { return -1; }
}
}
An object-disposed-exception is thrown when checking if the count of the query is greater than or equal to one. I had thought that, since query gets declared outside of the curly braces, this shouldn't be an issue of scope.
Additionally, what is a good way to fix the kludge? It should only be returning a single int, not a list of ints. How can I treat it that way? Thanks!

Change
query = from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID;
to
query = (from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID).ToList();
LINQ is using deferred execution, so you query is not evaluated immediately, but it is evaluated only at query.Count(). The context at that moment is already disposed. That's why you receive error.

context is disposed because you are trying to retrieve the data from database out of the context using block;
using (var context = new IngredientsLinqDataContext())
{
query = from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID;
if (query.Count() >= 1)
{
return query.Min();//a very serious kludge. Need to fix this
}
else { return -1; }
}

You have to be aware that a Linq query can beAsEnumerable or AsQueryable.
If your query is AsEnumerable it holds all information needed to access the elements in your query: You can ask for the first element in your sequence (if there is one), and once you've got an element you can ask for the next one (if there is one). If your query uses other functions in your process, it has all information to access these other functions. In short: AsEnumerable is processed in local memory.
If your query is AsQueryable it holds an Expression and a Provider. The Provider knows which process is designated to process the Expression. It knows which language this process uses. It is the task of the Provider to convert the Expression into the language of the destination processor and to send it to this processor.
For Entity Framework this is usually a database like SQL or MySQL, but it could also be a spreadsheet or a CSV file. The nice thing about a DbContext is that you don't need to know which language it uses to communicate with the other process. You could use the same IDbSet classes to communicate with a completely different process.
So an IQueryable does not hold the information to enumerate over the elements in the query. It only knows how to translate it into the language of the designated process and who to ask to execute this query.
The translation and execution is not done until you ask for elements. This means that you have to keep the providing object that your query uses alive until you don't need any more results from this providing object anymore.
Executing the query is done whenever you ask for a function that doesn't use deferred execution (as is described in the remarks section of every linq function). These are functions like ToList(), FirstOrDefault(), Single(), Count(), etc.
Functions like Where, Select, GroupBy and others that use deferred execution only change the Expression.
Conclusion
Make sure you've fetched all data that you need from your provider before
you Dispose() it

May be this will fix your issue.
public int getUserId(string un, string pw)
{
int query;
using (var context = new IngredientsLinqDataContext())
{
--change here
query = (from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID).FirstOrDefault();
}
if(query != 0) --change here
{
return query;//a very serious kludge. Need to fix this
}
else { return -1; }
}

Your use of query in this construct instructs LINQ to wait until the object is accessed before executing the actual query against the database. In this case query is accessed outside your using block, so that's why you're getting the error.
Try this:
result = (from c in context.USERs
where c.Username == un && c.Password == pw
select c.UserID).Min();

Related

EF Core Include() statement is null for IQueryable

Ok, so this might be a bit hard to explain without a ton of code to support it but I will try my best.
Essentially I am doing a query (currently on ef core 2.1) with involves a 1 to many relationships. However, the "many" collection is null when it materialized.
Here is the query in question (some code removed for brevity)
IQueryable<AccountViewModel> baseQuery = from ms in _managedSupportRepository.GetAllIncluding(m => m.Users) // here is the problem
// a few lines of filters like the one below
where string.IsNullOrEmpty(clientVersionFilter) || !string.IsNullOrEmpty(ms.ClientVersion) && ms.ClientVersion.Contains(clientVersionFilter, StringComparison.OrdinalIgnoreCase)
join c in _contractRepository.GetAll() on ms.Id equals c.AssetId into contracts
from c in contracts.DefaultIfEmpty()
let isAssigned = c != null
where !isAssignedFilter.valueExists || isAssignedFilter.value == isAssigned
join a in _autotaskAccountRepository.GetAll() on ms.TenantId equals a.Id
where string.IsNullOrEmpty(accountNameFilter) || !string.IsNullOrEmpty(a.AccountName) && a.AccountName.Contains(accountNameFilter, StringComparison.OrdinalIgnoreCase)
select new AccountViewModel
{
AccountName = a.AccountName,
ActiveUsers = ms.GetConsumed(), // here is the problem
ClientVersion = ms.ClientVersion,
ExternalIpAddress = ms.IpAddress,
Hostname = ms.Hostname,
Id = ms.Id,
IsActive = ms.IsActive,
IsAssigned = isAssigned,
LastSeen = ms.CheckInTime,
Status = ms.Status
};
int count = baseQuery.Count();
baseQuery = baseQuery.Paging(sortOrder, start, length);
return (baseQuery.ToList(), count);
Just for clarity, the _managedSupportRepository.GetAllIncluding(m => m.Users) method is just a wrapper around the .Include() method.
So the problem is in the view model for active users ActiveUsers = ms.GetConsumed(),. The GetConsumed() method is as follows
public long GetConsumed()
{
return Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
}
however, this throws a null reference exception because the Users collection is null.
Now my question is, why is the Users collection null when I am explicitly asking it to be loaded?
A workaround at the moment is to alter the queries first line to be this _managedSupportRepository.GetAllIncluding(m => m.Users).AsEnumerable() which is just ridiculous as it brings all the records back (which is several thousand) so performance is nonexistent.
The reason it needs to be an IQueryable is so the paging can be applied, thus reducing the amount of information pulled from the database.
Any help is appreciated.
There are two parts to this problem:
1) Includes not in the projection don't get included
When you do queries on EF on the provider (server evaluation), you are not -executing- your new expressions, so this:
ActiveUsers = ms.GetConsumed(),
Never actually executes ms.GetConsumed(). The expression you pass in for the new is parsed and then translated to the query (SQL in case of sql server), but ms.GetConsumed() is not executed on the provider (on the query to the database).
So you need to include Users on the expression. For example:
select new AccountViewModel
{
AccountName = a.AccountName,
AllUsers = Users.ToList(),
ActiveUsers = ms.GetConsumed(),
// etc.
}
This way EF knows it needs Users for the query and actually includes it (you are not using Users in your expression, so EF thinks it doesn't need it even if you Include() it... it'll probably show a warning on the Output window in Visual Studio), otherwise it tries to project and request only the fields it understands from the new expression (which doesn't include Users).
So you need to be explicit here... try:
ActiveUsers = Users.Count(u => !u.IsDeleted && u.Enabled && u.UserType == UserType.Active);
And Users will be actually included.
2) Automatic client side evaluation when queries can't get translated
In this case, Users will be included because it's in the actual expression... BUT, EF still doesn't know how to translate ms.GetConsumed() to the provider query, so it'll work (because Users will be loaded), but it won't be ran on the database, it'll still be run on memory (it'll will do client side projection). Again, you should see a warning about this in the Output window in Visual Studio if you are running it there.
EF Core allows this (EF6 didn't), but you can configure it to throw errors if this happens (queries that get evaluated in memory):
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
/* etc. */
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
You can read more about this here: https://learn.microsoft.com/en-us/ef/core/querying/client-eval

How to structure a partially dynamic LINQ statement?

I currently have a method on my repository like this:
public int GetMessageCountBy_Username(string username, bool sent)
{
var query = _dataContext.Messages.AsQueryable();
if (sent)
query = query.Where(x => x.Sender.ToLower() == username.ToLower());
else
query = query.Where(x => x.Recipient.ToLower() == username.ToLower());
return query.Count();
}
It currently builds one of two queries based on the sent boolean. Is this the best way to do this or is there a way to do this within the query itself? I want to check if x.Sender is equal to username if sent equals true. But I want to check if x.Recipient is equal to username if sent equals false.
I then want this LINQ expression to translate into SQL within Entity Framework, which I believe it is doing.
I just want to avoid repeating as much code as possible.
You could do something like this :
public int GetMessageCountBy_Username(string username, bool sent)
{
Func<Message, string> userSelector = m => sent ? m.Sender : m.Recipient;
var query =
_dataContext.Messages.AsQueryable()
.Where(x => userSelector(x).ToLower() == username.ToLower());
return query.Count();
}
Thus the choosing of the right user (the sender or the recipient) is done before the linq part, saving you from repeating it twice.
Yes, I believe this is correct way to do it. Because it is easy to create complex queries without repeating whole parts of queries.
And your thinking about translating to SQL is correct too. But beware, this is done at the moment, when data or agregation is requested. In your case, the SQL will be generated and executed when you call Count().

Calling functions from within linq statement

Just wondering if this is the most efficient method of doing this? is there a way of having all the linq within one statement instead of calling a method, like a subselect or something?
newEmployee = (from emp
in db.employees
select new
{
a.EmployeeID,
a.Username,
Status = emp.GetEmployeeCurrentStatus(a.Username)
}).ToList();
This is the GetEmployeeCurrentStatus which returns the status of the employee:
public string GetEmployeeCurrentStatus(string username)
{
using (Entities db = new Entities())
{
var times = (from d in db.TimeTables
where d.DateTime == DateTime.Today &&
d.Employee.Username == username
select d)
.OrderByDescending(d => d.TimeID).FirstOrDefault();
return (x.ClockOut == null ? "IN" : "OUT");
}
}
how about:
newEmployee = (db.employees.Select(emp => new
{
emp.EmployeeID,
emp.Username,
Status = db.TimeTables
.Where(d => d.Employee.Username == emp.Username
&& d.DateTime == DateTime.Today)
.Select(x => x.ClockOut == null ? "IN" : "OUT")
.FirstOrDefault()
})).ToList();
Your attempt may appear cleaner and is functionally ok. However, it is firing up a secondary db call. This will be bad for scalability and performance. The version i've posted uses the same initial db connection and will make the join 1-1. This will result in tighter, faster queries as well as lower resource usage.
You cannot really call a custom method inside a query (or a part of a query that will be executed using the databse). You have essentially two options:
Call ToList before performing the select that needs to call the method (this way, the method will be called on in-memory data)
Compose the query such that it can all run on the SQL server if it is possible. This can be done using AsExpandable extension in predicate builder. For more information on how this works, see also my blog post.
its fine for small data (employees count) but since each GetEmployeeCurrentStatus requires an sql new connection so its not that best practice.
I personally will get all employees (one trip to database) and then get all employees status (one trip to database) so i cashed them all, now i'll join them locally
Hope this helped
Regardless of efficiency, having GetEmployeeCurrentStatus(...) as a method makes the code clearer and more reusable.
Assuming you are using LINQ to SQL or EF, I would refactor your query to use a Join. That way, you will execute a single efficient SQL query on the database, instead of two separate queries.

Linq to SQL Updating Data

I am just starting to use linq to sql for data access. It is working fine for read only. But it does not work for update. I have been reading the threads over several forums. It is clear that anonymous types (in my case var) cannot be updated. I cannot find what I should replace the var with and where I find it. I will appreciate any help.
Below is the code. The exception is
Error 1 Property or indexer 'AnonymousType#1.date_last_logon' cannot be assigned to -- it is read only
fmcsaDataContext db = new fmcsaDataContext();
// DataTable _UserTable;
UserModel _UserModel = new UserModel();
var users = from u in db.FMCSA_USERs
where u.USER_NAME == pName && u.ACTIVE == true
select new
{
date_last_logon = u.DATE_LAST_LOGON,
date_current_logon = u.DATE_CURRENT_LOGON,
failed_login = u.FAILED_LOGIN,
};
if (users.Count() == 0)
return null;
foreach (var user in users)
{
user.date_last_logon = user.date_current_logon;
}
This is the case for any ORM tool; you will have to use the entity types that LINQ-to-SQL generates for you when you make your .dbml file if you want to perform CRUD operations.
Also, be aware that your query is being executed twice and is not concurrently safe; calling Count() executes your query with a Count aggregate in the database, then looping over it executes the query again, this time bringing back results. Given what you're doing, this may be better:
var users = (from u in db.FMCSA_USERs
where u.USER_NAME == pName && u.ACTIVE == true
select u).ToList(); // This executes the query and materializes
// the results into a List<FMCSA_USER>
if (users.Count == 0) return null;
foreach (var user in users)
{
user.date_last_logon = user.date_current_logon;
}
db.SaveChanges();
In order to update data, you cannot use anonymous types.
Instead, you can end your query with select u; to select the actual entities.
Table you are trying to update using LINQ, should have Primary key.

Lambda syntax in linq to db4o?

I know the following is possible with linq2db4o
from Apple a in db
where a.Color.Equals(Colors.Green)
select a
What I need however is something that allows me to build my query conditionally (like I can in other linq variants)
public IEnumerable<Apple> SearchApples (AppleSearchbag bag){
var q = db.Apples;
if(bag.Color != null){
q = q.Where(a=>a.Color.Equals(bag.Color));
}
return q.AsEnumerable();
}
In a real world situation the searchbag will hold many properties and building a giant if-tree that catches all possible combinations of filled in properties would be madman's work.
It is possible to first call
var q = (from Color c in db select c);
and then continue from there. but this is not exactly what I'm looking for.
Disclaimer: near duplicate of my question of nearly 11 months ago.
This one's a bit more clear as I understand the matter better now and I hope by now some of the db4o dev eyes could catch this on this:
Any suggestions?
Yes it's definitely possible to compose optimized LINQ queries using db4o. Granted that db is defined as follows:
IObjectContainer db;
Here is your query:
public IEnumerable<Apple> SearchApples (AppleSearchbag bag)
{
var query = db.Cast<Apple> ();
// query will be a Db4objects.Db4o.Linq.IDb4oLinqQuery<Apple>
if (bag.Color != null)
query = query.Where (a => a.Color == bag.Color);
return query;
}
In that case, the query will be executed whenever the returned enumerable is being iterated over.
Another possibility is to use the IQueryable mechanism, that has the advantage of being better recognized by developers:
public IQueryable<Apple> SearchApples (AppleSearchbag bag)
{
var query = db.AsQueryable<Apple> ();
// query will be a System.Linq.IQueryble<Apple>
if (bag.Color != null)
query = query.Where (a => a.Color == bag.Color);
return query;
}
In both cases, db4o will try to deduce an optimized query from the lambda expression upon execution, and if it fails, will fallback to LINQ to objects. The first one has the advantage of being more direct, by avoiding the queryable to LINQ to db4o transformation.

Categories