Linq to SQL Updating Data - c#

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.

Related

how to search record from single table with multiple parameters using LINQ?

I am trying to search record(s) from table by appying multiple search parameters.
as per below snap.
here by using various parameters as per above snap i want to filter the records.
here user could enter any combination of parameter(s) to search record.
i tried something like below code hich works for single condition but fails for combination of any search paramets.
public List<students> SearchStudents(students search)
{
var result = new List<students>();
var records= from stud in db.students
where stud.enrollmentNumber== search.enrollmentNumber
|| stud.enrollmentDate==search.enrollmenttDate
|| stud.enrollmentType==search.enrollmentType
|| stud.className==search.className
select new Search()
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
};
result = records.ToList();
return result;
}
but this is not working properly. means it returns same result whatever parameters I pass.
Like in the table i ahve 20 records and the enrollment number is the unique value field in DB so here when i am passing enrollment number thats like "2018-0001" it returns all records when it should return only single reocrd.
can someone guide me with this?
Without further explanation in your question about how this isn't working, the best we can do is guess. However, one very plausible reason for this is because you're including parameters you don't want to be filtering on.
Because you're using ORs in your statement, if any of those other properties are defaulted in the database, you're going to be returning those records. What you need to be doing is conditionally including your pieces of the WHERE clauses for only the properties that you want to search on. Unfortunately, that is not possible with the "SQL syntax" version of LINQ, so you will need to convert your query to that. (Good news: It's slightly more performant as well as it usually has to convert the SQL to the method syntax.)
Because of deferred execution, your query will not be sent to the database until you call a .ToList() or something to actually start processing the results. This allows you to chain method calls together, even if they are completely different C# statements. This is what you'll want to do:
public List<students> SearchStudents(students search)
{
var query = db.students;
if (!string.IsNullOrWhiteSpace(search.enrollmentNumber))
{
query = query.Where(s => s.enrollmentNumber == search.enrollmentNumber);
}
if (search.enrollmentDate != DateTime.MinValue)
{
query = query.Where(s => s.enrollmentDate == search.enrollmentDate);
}
if (!string.IsNullOrWhiteSpace(search.enrollmentType))
{
query = query.Where(s => s.enrollmentType == search.enrollmentType);
}
if (!string.IsNullOrWhiteSpace(search.className))
{
query = query.Where(s => s.className == search.className);
}
return query.Select(stud => new Search
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
})
.ToList();
}
You may need to adjust the if statements in there to accommodate different data types than what is intuitive from the names, but this will only add the filter if a value has been provided.

Why is my object being disposed?

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();

LINQ statment looks unreasonable to me: what's my error?

There has to be a better way:
public IList<ApplicationUser> GetProspects()
{
var UsrNames = Roles.GetUsersInRole("prospect");
IList<ApplicationUser> Users = new List<ApplicationUser>();
foreach ( var u in UsrNames)
{
// In theory should return at most one element.
var UserListFromQuery = from usr in (new ApplicationDbContext()).Users where usr.UserName == u select usr;
Users.Add(UserListFromQuery.ElementAtOrDefault(0));
}
return Users;
}
Can you please show me the error of my ways?
This should do what you want:
using (var context = new ApplicationDbContext())
{
var result = Roles.GetUsersInRole("prospect")
.Select(name => context.Users.FirstOrDefault(user => user.UserName == name))
.Where(user => user != null)
.ToList();
}
I've modified your code to utilize a using statement for the context, so as to ensure it is disposed of properly even if there is an exception. Then I have a linq query which does the following:
Get the usernames
For every username, select the first user in Users with a matching username
Remove all nulls from the resulting enumeration. This is necessary because FirstOrDefault returns null if no matching user is found
Turn our final enumeration into a List
I guess you could join it, then group it, then cull the grouping. I'm not sure whether there is an overall advantage of the front-loading that you'd be doing by joining and grouping, so you might want to put a stopwatch to this (and to your original code) and figure that one out.
My suggestion is:
// force a prefetch, rather than potentially slamming the server again and again
var allUsers = (new ApplicationDbContext()).Users.ToList();
// use the prefetched list to join & filter on
var result = from entitled in UsrNames
join user in allUsers
on entitled equals user.UserName
group user by user.UserName into grp
select grp.First();
return result.ToList();
Couple of thoughts:
This is clearly a user-related table. So I'm guessing that you're not going to have 100k records or something along that scale. At most, maybe thousands. So safe to cache in local memory, especially if the data will not change many times throughout the day. If this is true, you might even want to preload the collection earlier on, and store into a singular instance of the data, to be reused later on. But this observation would only hold true if the data changes very infrequently.

Where IN for linq

I have seen questions with this subject but mine is different.
i have stored procedure (EmpsByManager) imported in EF. it returns data of following fields:
EmpId, EmpName, PrimaryMobile
I have a claimTable in the db having the following fields
EmpId, ClaimId, ClaimDetails...
I want to return all claims from the claimTable IN the Employees of EmpsByManager(ManagerId)
I could manage to do this with a loop:
public dynamic getActiveClaims(int ManagerId)
{
db.Configuration.ProxyCreationEnabled = false;
var myEmps = db.getEmpDetofManager(ManagerId).ToList();
List<List<claimJSON>> claimsList = new List<List<claimJSON>>();
foreach(var Emp in myEmps)
{
claimsList.Add(db.claimJSONs.Where(e => e.EmpId == Emp.EmpId && e.claimstatus != 0 && e.claimstatus != 8).ToList());
}
return claimsList;
}
This is giving correct results but, I myself am not convinced with the complexity and number of database hits to get the required result.
Anyone? Thank You.
Currently you are hitting the db everytime inside your loop. You can replace the Where clause inside your foreach loop with the use of the Contains() method.
var myEmps = db.getEmpDetofManager(ManagerId).ToList();
// Get all EmpIds from the result and store to a List of Int
List<int> empIds = myEmps.Select(f=>f.EmpId).ToList();
// Use the List of EmpId's in your LINQ query.
var claimsList = db.claimJSONs.Where(e => empIds.Contains(e.EmpId)
&& e.claimstatus != 0 && e.claimstatus != 8).ToList();
Also, not that the result in claimsList variable will be a List<claimJSON> , not List<List<claimJSON>>>
This will result in 2 hits to the db. One for the stored proc and another for getting data from the claimJSON table for the list of EmpIds we got from the stored proc result.
Well, there is not a lot you CAN optimize. The main problem is the stored procedure.
As you can not join - even without the limitations of EF - the output of a stored procedure... no, you have no way. Not without rewriting the SP. Which should not be there anyway - this functionality is much better suited for a function which can then be used in a more complex query style. Someone forced that into a SP - and now you have to live with the limitations.

Avoid "Nullable object must have a value." in Linq-To-Sql

I have a method query like this:
public IList<BusinessObject> GetBusinessObject(Guid? filterId)
{
using (var db = new L2SDataContext())
{
var result = from bo in db.BusinessObjects
where (filterId.HasValue)
? bo.Filter == filterId.value
: true
orderby bo.Name
select SqlModelConverters.ConvertBusinessObject(bo);
return result.ToList();
}
}
At runtime, this throws a System.InvalidOperationException: Nullable object must have a value.
Looking at the Debugger, the problem is my Where Clause: Linq To SQL tries to convert the entire thing to SQL, so even if filterId is NULL, it will still try to access filterId.value.
I thought/hoped the C# compiler/CLR would evaluate that where clause as a code block and only send one of the two branches to Linq To SQL, but that's not how it works.
My refactored version works, but is not very elegant:
public IList<BusinessObject> GetBusinessObject(Guid? filterId)
{
using (var db = new L2SDataContext())
{
var temp = from bo in db.BusinessObjects select bo;
if(filterId.HasValue) temp = temp.Where(t => t.Filter == filterId.Value);
var result = from t in temp
orderby t.Name
select SqlModelConverters.ConvertBusinessObject(bo);
return result.ToList();
}
}
I know that Lazy-Evaluation will make sure that only one query is really sent, but having that temp object in there isn't that great really.
Did you try:
where filterId == null || t.Filter == filterId
Your fix is exactly correct. You are effectively trying to build up a query dynamically, based on your function input. It's a good idea to omit the where clause instead of supplying WHERE TRUE anyway. If I were writing this query, I would go with your fixed version myself.
It's not as pretty as using the language keywords, but it's still the right way to approach the query in my opinion.

Categories