简体   繁体   English

为什么我从两个几乎相同的表达式获得不同的结果,以使用Entity Framework上下文从数据库获取数据

[英]Why I am getting different result from two almost equal expressions to get data from database using Entity Framework context

I am validating username (case-insensitive) and password (case-sensitive) from database 我正在验证数据库中的用户名(不区分大小写)和密码(区分大小写)

I am using Entity Framework 5.0 to interact with database 我正在使用Entity Framework 5.0与数据库进行交互

In the database password is 在数据库中密码是

"0x11A46971EFF1E1A2CA228CF592CA37DC77E7CCF759602D53629C22B693AEEE96CCD9F889D8E9A92C19391E6BD2DD07E741E9B7AA07E391ACDC939B993C9D7F5D" “0x11A46971EFF1E1A2CA228CF592CA37DC77E7CCF759602D53629C22B693AEEE96CCD9F889D8E9A92C19391E6BD2DD07E741E9B7AA07E391ACDC939B993C9D7F5D”

I am expecting null in return for my following code block when I change case of password to lower ie 当我将密码的大小写更改为ie时,我期望为我的后续代码块返回null

"0x11a46971eff1e1a2ca228cf592ca37dc77e7ccf759602d53629c22b693aeee96ccd9f889d8e9a92c19391e6bd2dd07e741e9b7aa07e391acdc939b993c9d7f5d" “0x11a46971eff1e1a2ca228cf592ca37dc77e7ccf759602d53629c22b693aeee96ccd9f889d8e9a92c19391e6bd2dd07e741e9b7aa07e391acdc939b993c9d7f5d”

but it does not fail and return proper user entity. 但它不会失败并返回适当的用户实体。

using (var context = new MyDbContext(ConnectionString))
{
  return context.Users.Where(x => (x.Name.Equals(userName, StringComparison.OrdinalIgnoreCase) && x.Password.Equals(password))).FirstOrDefault();
}

Whereas if I get all Users and then compare it gives proper output ie user == null 然而,如果我得到所有用户然后比较它给出正确的输出,即user == null

using (var context = new MyDbContext(ConnectionString))
{
  var users = context.Users.ToList();

  return users.Where(x => (x.Name.Equals(userName,StringComparison.OrdinalIgnoreCase) && x.Password.Equals(password))).FirstOrDefault();
}

This is Strange? 这很奇怪? Why it is happening? 为什么会这样? How to write case sensitive query to SQL from LINQ? 如何从LINQ中将区分大小写的查询写入SQL?

SQL is not case sensitive. SQL不区分大小写。 If you looked at the generated SQL it would look something like this: 如果查看生成的SQL,它看起来像这样:

EXISTS(SELECT * FROM Users WHERE userName = 'foo' AND Password = '0x11a46971eff1e1a2ca228cf592ca37dc77e7ccf759602d53629c22b693aeee96ccd9f889d8e9a92c19391e6bd2dd07e741e9b7aa07e391acdc939b993c9d7f5d')

It will return true regardless of case. 无论如何,它都会返回true。 The second example calls ToList so it is now doing a .net string compare what IS case sensitive. 第二个示例调用ToList,因此它现在正在执行.net字符串比较区分大小写。

When using 使用时

return context.Users.Where(x => (x.Name.Equals(userName, StringComparison.OrdinalIgnoreCase) && x.AuthenticationSecret.Equals(password))).FirstOrDefault();

you work on a lazyloaded EntityCollection. 你在一个延迟加载的EntityCollection上工作。 Because SQL is not case sensitive it will return true every time. 因为SQL不区分大小写,所以每次都会返回true。

If you are using 如果你正在使用

var users = context.Users.ToList();

you switch from LINQ To Entities to LINQ To Objects , so you can now make a case sensitive comparison. 您从LINQ To Entities切换到LINQ To Objects ,因此您现在可以进行区分大小写的比较。

The big disadvantage is that everytime you are using ToList() your Query will be executed immediatly and you will load the COMPLETE List of Users from the Database. 最大的缺点是,每次使用ToList()您的查询都会立即执行,您将从数据库中加载COMPLETE用户列表。

The SQL look-up is case insensitive and when you do the ToList first you end up doing a case sensitive comparison. SQL查找不区分大小写,当您首先执行ToList ,最终会进行区分大小写的比较。 But the ToList will return everything from the DB. ToList将返回DB中的所有内容。 instead do the Where on the db and then a further comparison to make sure the password is the correct case. 而是在数据库上的Where ,然后进行进一步的比较,以确保密码是正确的情况。

using (var context = new MyDbContext(ConnectionString))
{
  var users = context.Users.Where(x => x.Name == userName && x.Password == password).ToList();
  return users.FirstOrDefault(x =>  x.Password.Equals(password)));
}

As @nmclean mentioned, this is because your database is configured to perform case insensitive comparisons. 正如@nmclean所提到的,这是因为您的数据库配置为执行不区分大小写的比较。 The following small program shows how EF completely ignores the StringComparison values passed to String.Equals . 下面的小程序显示了EF如何完全忽略传递给String.EqualsStringComparison值。 It also shows how changing the collation of the database column achieves what you need. 它还显示了如何更改数据库列的排序规则以实现所需。

This is an imporant concept when working with EF (and other LINQ providers too). 与EF(以及其他LINQ提供商)合作时,这是一个重要的概念。 Your query as it is written looks like normal .NET code. 您编写的查询看起来像普通的.NET代码。 However, it isn't! 但是,它不是! Many providers translate the code to something completely different, in this case SQL (for SQL Server, Transact-SQL). 许多提供程序将代码转换为完全不同的代码,在本例中为SQL(对于SQL Server,Transact-SQL)。 Only a sub-set of the available .NET methods will work at all (which is why, for example, you cant use Regex in an EF query), and those that do work may exhibit subtly different behaviour compared to what you would expect. 只有可用的.NET方法的子集才能工作(这就是为什么,例如,你不能在EF查询中使用Regex ),那些工作的人可能会表现出与你期望的微妙不同的行为。

Where you use ToList() on a query, then apply your filter, you are actually using LINQ to Objects, not EF, to apply the filter. 如果在查询上使用ToList()然后应用过滤器,则实际上使用LINQ to Objects而不是EF来应用过滤器。 LINQ to Objects is "real" .NET, so you have the full range of .NET functionality at your disposal. LINQ to Objects “真正的”.NET,因此您可以随意使用各种.NET功能。 Of course, you lose the benefit of having your code run on the SQL Server, so this isn't usually an option. 当然,您失去了在SQL Server上运行代码的好处,因此通常不会这样做。

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace ConsoleApplication2
{
    public class Initializer<T> : DropCreateDatabaseAlways<T> where T : DbContext
    {
        protected override void Seed(T context)
        {
            base.Seed(context);

            // Comment out this line and note how the count changes.
            context.Database.ExecuteSqlCommand("ALTER TABLE MyEntity ALTER COLUMN MyString nvarchar(MAX) COLLATE Latin1_General_CS_AS");
        }
    }

    [Table("MyEntity")]
    public class MyEntity
    {
        [Key]
        public virtual int MyEntityId { get; set; }

        [Required]
        public virtual string MyString { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<MyEntity> Entities
        {
            get { return this.Set<MyEntity>(); }
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            var e = modelBuilder.Entity<MyEntity>();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new Initializer<MyContext>());

            using (MyContext context = new MyContext())
            {
                context.Entities.Add(new MyEntity { MyString = "aaa" });
                context.Entities.Add(new MyEntity { MyString = "AAA" });

                context.SaveChanges();
            }

            using (MyContext context = new MyContext())
            {
                var caseSensitiveQuery = from e in context.Entities
                                         where e.MyString.Equals("aaa", StringComparison.Ordinal)
                                         select e;

                var caseInsensitiveQuery = from e in context.Entities
                                           where e.MyString.Equals("aaa", StringComparison.OrdinalIgnoreCase)
                                           select e;

                // Note how the StringComparison parameter is completely ignored.  Both EF queries are identical.
                Console.WriteLine("Case sensitive query (count = {0}):\r\n{1}", caseSensitiveQuery.Count(), caseSensitiveQuery);
                Console.WriteLine();

                Console.WriteLine("Case insensitive query (count = {0}):\r\n{1}", caseInsensitiveQuery.Count(), caseInsensitiveQuery);
            }

            Console.ReadLine();
        }
    }
}

There is a more detailed answer to this here: 这里有一个更详细的答案:

https://stackoverflow.com/a/3843382/933416 https://stackoverflow.com/a/3843382/933416

If you need to, you can configure the column to be case sensitive on the server side. 如果需要,可以将列配置为在服务器端区分大小写。 But in your case, I don't see why you even need to query the password. 但在你的情况下,我不明白为什么你甚至需要查询密码。 Surely there is only one password for each username, and a username is meant to be case insensitive -- so why not just query by username and then check the password on the result? 当然每个用户名只有一个密码,用户名不区分大小写 - 所以为什么不用用户名查询然后检查结果的密码呢?

As everyone else has already mentioned, the cause of the problem is that SQL Server defaults to not case sensitive and C# defaults to case sensitive. 正如其他人已经提到的那样,问题的原因是SQL Server默认不区分大小写,C#默认区分大小写。

You can change the password field from varchar to varbinary to get around this problem. 您可以将密码字段从varchar更改为varbinary以解决此问题。


Why the anonymous down votes? 为什么匿名投票? Using a string to store and compare what appears to be a 64 byte hash value is not the best approach. 使用字符串来存储和比较看似64字节的哈希值并不是最好的方法。 There's no need to base 64 encode a hash to store it (unless Entity Framework can't handle it?). 没有必要基于64编码哈希来存储它(除非实体框架无法处理它?)。

http://support.microsoft.com/kb/307020 - How to compute and compare hash values by using Visual C# Comparing hash passwords http://support.microsoft.com/kb/307020 - 如何使用Visual C# 比较哈希密码 来计算和比较哈希值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用实体框架linq从数据库结果中获取项目 - Get item from database result by using entity framework linq 使用实体框架移动和比较来自不同数据库的数据 - Moving and comparing data from different database using entity framework 使用实体框架从数据库获取行 - Get rows from database using the Entity framework 使用实体框架从数据库中检索数据 - Retrieving data from database using Entity Framework 如何首先从实体框架+数据库中的存储过程中获取结果 - how to get the result from stored procedure in entity framework + database first 为什么我在System.Data.Entity.dll(实体框架)中收到NullReferenceException? - Why am I getting a NullReferenceException in System.Data.Entity.dll (Entity Framework)? 如何通过实体框架中数据库的一列上的多个值与其他列上的不同值获取数据? - How to get data by mutiple values on one column with different values on other columns from database in Entity Framework? 我正在尝试从实体框架返回XML数据 - I am trying to return XML data from Entity Framework 实体框架结合了来自不同实体的lambda表达式 - Entity Framework combine lambda expressions from different entities 从两个上下文实例实体框架中使用一个实体 - Use one entity from two context instance entity framework
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM