How do I force LINQ to SQL to make case sensitive comparison on the database side? When authenticating I need the password authentication to be case sensitive while the username to be case insensitive. The generated query does not make a distinction the SQL server comparisons are case insensitive.
public Boolean checkLogin(string uname, string pwd)
{
DataClassesDataContext dc = new DataClassesDataContext(RetrieveConnection());
int count=(from c in dc.Logins
where c.UserName == uname && c.Password == pwd
select c).Count();
if(count>0)
{
return true;
}
else
{
return false;
}
}
Your approach on this is not a very good one. Best approach would be to save all the usernames in the database with small letters using method string.ToLower(). Regarding password, maybe you should try some encryption method, not store strings as passwords in database. There are plenty simple encryption methods, so it's not really too complicated ( seeSystem.Crypthography)
Related
We are using Asp.Net Identity version 2.2.1 and EF 6.1.3 over an Oracle Database.
We do have an index on the usertable on UPPER(USERNAME) but still get full table scan on our user table for each user login.
The issue seems to be caused by this code in UserStore:
public virtual Task<TUser> FindByNameAsync(string userName)
{
this.ThrowIfDisposed();
return this.GetUserAggregateAsync((
Expression<Func<TUser, bool>>) (u =>
u.UserName.ToUpper() == userName.ToUpper()));
}
this result in an SQL with
WHERE (((UPPER("Extent1".USERNAME)) = :p__linq__0)
OR ((UPPER("Extent1".USERNAME) IS NULL) AND
(:p__linq__0 IS NULL)))
and this OR part it seems is making the optimizer choose a full table scan.
When using SQL Server this may be optimized by changing the where statement to:
public virtual Task<TUser> FindByNameAsync(string userName)
{
this.ThrowIfDisposed();
var uName = userName.ToUpper();
return this.GetUserAggregateAsync((
Expression<Func<TUser, bool>>) (u =>
u.UserName.ToUpper() == uName));
}
and the where part is now :
WHERE N'<USERNAME>' = (UPPER([Extent1].[USERNAME]))
But I'm lost for options on how to optimize this when using Oracle. I have tried both Oracle.Managed driver and Devart.Oracle driver and they both generate where statements with OR when using UPPER function on a field.
It is a requirement to have CASE INSENSITIVE usernames in the database.
So why does EF generate a SQL with an OR statement when we use UPPER(field) = something?
Is there another way to pursue this? Does anyone have a good solution?
At this point our last option may be to make the username UPPERCASE in the data table and thereby avoid having to use the UPPER function - but it doesn't seem like the optimal solution.
Have you tried setting
context.UseDatabaseNullSemantics = true;
https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbcontextconfiguration.usedatabasenullsemantics(v=vs.113).aspx
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.
I have a function to retrive the user detail
I have encryped few fields. I want to know how the LINQ will execute the actual sql query
public User GetUserByEmail(string email)
{
return _db.Users.Where(x => x.LastName.Decrypt() == "Patel").ToList();
}
x.LastName.Decrypt() will it fetch all records from sql and perform decrypt on each field in the code side ?
What if i use
public User GetUserByEmail(string email)
{
return _db.Users.Where(x => x.LastName == "Patel".Encrypt()).ToList();
}
which one is better
Good chances are that your first query isn't going to work at all, unless you force bringing the data into the memory by calling AsEnumerable(), ToList, or ToArray, because EF provider does not know how to translate your Encrypt function to SQL.
The second query, on the other hand, should work fine, because it's a string-to-string comparison.
In addition, the second way of querying lets you implement a potentially more secure scheme when "encrypting" a string cannot be reversed with a Decrypt, e.g. when you store a message digest.
Calling .Decrypt() in the Where function will decrypt the LastName on each & every row. You're better off with the .Encrypt() method you displayed, which will call 'Encrypt` once and compare each LastName to the encrypted string for "Pate1".
I'm writing a piece of code with linq which has to hash a string (in that case my login) and then try to find it into my database.
I tried :
var userFind = context.Users.FirstOrDefault(user =>HashHelper.HashCode(user.Login).Equals(u.Login));
I got an error because of the HashCode.
I wouldn't read all of my list with a foreach. I'm wondering if it's possible to do so with one line of code.
Regards.
Edit: I found a way to do so, but it isn't as lighter as I expected.
User userFind = null;
foreach (var user in context.Users)
{
string hashedLogin = HashHelper.HashCode(user.Login);
if(hashedLogin.Equals(u.Login))
{
userFind = user;
}
}
If you don't have too many users, you can do it on one line like this.
var userFind = context.Users.ToList().FirstOrDefault(
user => HashHelper.HashCode(user.Login).Equals(u.Login))
The important bit is the ToList() which evaluates the EF part and makes the rest linq-to-objects. This means the comparison will be done on the client and all users will be retrieved from the server. It is equivalent to your edit.
If performance is a problem you should store the hashcode in the database too.
If the data in the database is already hashed and u.Login is the value of the current user's login, then you could probably do it like this:
var hashedLogin = HashHelper.HashCode(u.Login); // hash the value in the app so it can be compared to already hashed values in database.
var userFind = context.Users.Where(user => user.Login == hashedLogin).FirstOrDefault();
I don't really know much about what you're trying to achieve, so I'll be pleasantly surprised if this actually helps. Good luck!
I'm working on a LINQ provider that uses the IQ Toolkit to tranlate LINQ queries to SQL queries. Are the classes provided by the IQ Toolkit safe from SQL injection attacks? If not, what I have to do to protect against SQL injection attacks, supposing that I'm using the IQ Toolkit and implementing my own LINQ provider. I read the LINQ to SQL uses SqlParameter,
but it's still not clear to me what needs to be done with SqlParameter to protect against SQL injection.
From the blog post it looks like IQ toolkit (or the initial version of the toolkit) is not safe from SQL injection attacks. But you can verify it by yourself - execute a query, capture the generated SQL and see if there are parameters used.
If you want to build your own provider, you must know that it is not that easy. Consider things like nested select, nested where, etc. There are great blog posts on this topic.
But you are interested in protecting your database against SQL injection. So if you look at the sample code on this page and the VisitConstant method, that's the place where you run into constants of value type (string, int, etc.) or IQueryable.
Protection against SQL injections is not complicated, you just create new SQLParameter or you call method DbProviderFactory.CreateParameter described here. You will need some collection to store your parameters while you are traversing the expression tree. So the modified code will look like this:
protected override Expression VisitConstant(ConstantExpression c) {
IQueryable q = c.Value as IQueryable;
if (q != null) {
// assume constant nodes w/ IQueryables are table references
sb.Append("SELECT * FROM ");
sb.Append(q.ElementType.Name);
}
else if (c.Value == null) {
sb.Append("NULL");
}
else {
switch (Type.GetTypeCode(c.Value.GetType())) {
case TypeCode.Boolean:
param = dbProvider.CreateParameter();
param.Name = "#param" + paramsList.Count;
param.Value = (((bool)c.Value) ? 1 : 0;
paramsList.Add(param);
sb.Append(param.Name);
break;
case TypeCode.String:
param = dbProvider.CreateParameter();
param.Name = "#param" + paramsList.Count;
param.Value = c.Value; // you don't have to care about escaping or formatting
paramsList.Add(param);
sb.Append(param.Name);
break;
...
case TypeCode.Object:
throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));
default:
sb.Append(c.Value);
break;
}
}
return c;
}
So while you are travesing the expression tree, you are building the SQL string and collecting the SQL parameters.