【问题标题】:Does Entity Framework Database First know about unique keys set in the database?Entity Framework Database First 是否知道数据库中设置的唯一键?
【发布时间】:2015-12-31 10:52:57
【问题描述】:

EF6.

我有一个项目首先使用 EF6 数据库。

我的数据库表 6 列的唯一键。在项目中一切都运行良好。然后我意识到唯一键不正确,我不需要它,所以我从数据库中删除了它。

我有一个Product,其中包含一个ICollection<ProdPrice> ProdPrices

现在,当我使用 Product product = db.Products.Find(id); 时,我得到了一个产品,但 ProdPrices 集合只包含 6 个项目,而它应该包含 12 个项目。(前 6 个项目在列表中)。

如果我从数据库中删除第一个项目并再次运行代码,则返回 6 个项目 - 项目 2 - 7。

如果我更改列中的一个值,该列中在唯一键中,该项目将出现在代码中。

所以我认为 EF 以某种方式记住唯一键,并且只返回与唯一键不冲突的第一个项目。

我尝试在 edml 文件中“从数据库更新模型” - 没有解决问题。所以我从 edml 中删除了 ProdPrice 表,并且“从数据库更新模型” - 没有用。

所以我的问题是 - 我是否正确地说 EF 正在记住已删除的唯一密钥?如果是,我如何让它忘记它?如果我不正确,那么您能解释一下实际发生的情况吗?

编辑:调用数据库生成的 SQL - 在 SSMS 中运行时返回所有 12 条记录的标准选择语句(为了便于阅读,删除了列):

SELECT 
[Extent1].[ProdPriceID] AS [ProdPriceID], 
[Extent1].[ProdID] AS [ProdID],
[Extent1].[PermanentlyDelete] AS [PermanentlyDelete], 
[Extent1].[DateCreated] AS [DateCreated]
FROM [dbo].[ProdPrice] AS [Extent1]
WHERE [Extent1].[ProdID] = 67577

这是结果,您可以看到我得到了 12 条记录。

代码结果:

所以我决定只询问产品的价格 - 我得到 12 个结果,这真的让我很困惑:

编辑: 我以为我已经解决了... 为了解决(并进一步测试),我决定在数据库中重新创建 ProdPrice 表 - 我通过使用 MSSSMS 生成脚本并包含数据来做到这一点,因此我拥有该表的精确副本,称为 ProdPrice2。

我的系统中有 ProdPrice 和 ProdPrice2 实体(这确认我仍然连接到正确的数据库)。 ProdPrice 仍然只返回与原始唯一索引不冲突的记录。 ProdPrice2 - 返回所有记录!

哎呀,原来如此 - 然后我从系统中删除了 ProdPrice,留下了 ProdPrice2 - 我运行了系统,ProdPrice2 现在只有 6 条记录,而不是我之前的 12 条!

我重新添加了 ProdPrice。ProdPrice2 仍然有 6 条记录,ProdPrice 现在有所有预期的记录。

我被难住了!!!!这真的阻碍了我的发展!在解决此问题之前我无法继续!

【问题讨论】:

  • 所以我从 edml 中删除了 ProdPrice 表和“从数据库更新模型” - 没用 什么没用? ProdPrice 表是否没有重新输入到类模型中。它有和以前一样的钥匙吗?如果是后者,我怀疑您只是针对错误的数据库进行了更新。检查 edmx 项目中的连接字符串。
  • @GertArnold 我想我应该说“没有解决问题”而不是“没有用”,我已经更新了我的问题。这不是连接字符串问题。我在测试期间添加了更多表格,这些更改反映在我的项目中。谢谢。

标签: c# entity-framework


【解决方案1】:

经过几天的努力和@AlbertoMonteiro 的一些出色帮助和建议后,我无法得到我所期望的行为。

在继续尝试各种事情和调试之后,我最终找到了一些我忘记的代码......!

原来问题的答案

Entity Framework Database First 是否知道数据库中设置的唯一键?

是,没有实体框架数据库首先不知道数据库中设置的唯一键。

然而,EF 在创建实体列表时会使用任何重写的 Equals() 函数。在我的代码中,我重写了 Equals 函数以模仿数据库中的唯一键,这意味着当我添加到价格列表时,我可以匹配“重复项”并将它们合并在一起。这段代码导致我的价格列表没有完全填充,因为我已经改变了我希望系统工作的方式。

答案是我是个白痴,我应该记得我写的代码。我将把这个问题留在这里,因为它可能会阻止某人在未来失去几天的发展。

【讨论】:

    【解决方案2】:

    这个问题中的实体框架,不用担心你删除的唯一键,当你选择某些东西时,这个唯一键也不会过滤数据,唯一键可以防止你不能添加具有相同唯一键的另一行.

    同样,这个唯一的密钥不是问题,EF 也不关心它。

    但是您可以调试从 EF 生成的 SQL 并检查为什么您只能从属性 ProdPrices 获得 6 个项目。

    我们可以使用上下文中的 Database.Log 属性来分析生成的 SQL。

    你说你有这个代码行Product product = db.Products.Find(id);

    让我们稍微修改一下来分析sql。

    //Add breakpoint in this line
    Product product = db.Products.Find(id); 
    db.Database.Log = sql => System.Diagnostics.Debug.WriteLine(sql);
    var prodPrices = product.ProdPrices.ToList();
    //Next line
    
    • 在第一行添加断点(查找产品)
    • F10(跳过)3次
    • 在创建 prodPrices 之后,断点现在必须位于下一行代码中
    • 打开输出窗口
    • 从 ProdPrice 生成的 SQL 应该在那里

    分析 SQL 查询,在 SSMS 中执行并检查结果。

    我在我的机器上试过了,但效果很好:

    public class Program
    {
        public static void Main(string[] args)
        {
            using (var ctx = new MyClass())
            {
                if (!ctx.Products.Any())
                {
                    var product = new Product
                    {
                        ProdPrices = new List<ProdPrice>
                        {
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,27)},
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,28)},
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,28)},
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,29)},
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,29)},
                            new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,30,19,08,38)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,06)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,06)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,07)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,07)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,08)},
                            new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,08)},
                        }
                    };
    
                    ctx.Products.Add(product);
                    ctx.SaveChanges();
                    Console.WriteLine("Saved");
                }
            }
            using (var ctx = new MyClass())
            {
                var product = ctx.Products.Find(1);
                var count = product.ProdPrices.Count;
                Console.WriteLine(count);
            }
        }
    }
    
    public class MyClass : DbContext
    {
        public DbSet<Product> Products { get; set; }
        public DbSet<ProdPrice> ProdPrices { get; set; }
    }
    
    public class Product
    {
        [Key]
        public long ProdID { get; set; }
        public virtual ICollection<ProdPrice> ProdPrices { get; set; }
    }
    
    public class ProdPrice
    {
        public long ProdPriceID { get; set; }
        public bool PermanentlyDelete { get; set; }
        public DateTime DateCreated { get; set; }
    }
    

    【讨论】:

    • 嗨,试过了,SQL 是标准的选择语句 - 当我在 SSMS 中运行时,所有 12 行都返回!!!奇怪!
    • @Rick 你能发布生成的 SQL 吗?
    • 如果你运行product.ProdPrices.ToList().Count 值是多少?
    • 我得到的值是6
    • @Rick 这真的很奇怪。可以试试product.ProdPrices.Take(20).ToList().Count吗?
    【解决方案3】:

    您似乎只创建了一次 dbcontext,而且这也是静态的。

    当您对 db 进行 fetch/write 调用创建新上下文时,应该这样做。

    再次,

    创建一个包含 DbContext 的基类,并在每次需要与数据库通信时继承该类。

    类似的东西

    public class TheDBEntities
    {
        private TheDBEntities _context = new TheDBEntities();
    
        protected TheDBEntities Context { get; private set; }
    
        protected TheDataContext()
        {
    
            Context = _context;
        }
    
    
        protected void SaveChanges()
        {
            Context.SaveChanges();
        }
    
        protected void SaveChangesAsync()
        {
            Context.SaveChangesAsync();
        }
    
    
    }
    
    
    public class DBBusinessLayer: TheDBEntities
    {
        public DBBusinessLayer() { }
    
        public void GetData(int id)
        {
            // use it here Context.<yourentities>
        }
    }
    

    【讨论】:

    • 为什么要公开protected void SaveChangesAsync()?它只会同步运行并等同于SaveChanges()。将该方法标记为protected async Task SaveChangesAsync() 或根本不公开它。
    • 感谢您的建议 - @kai
    猜你喜欢
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-16
    • 2016-07-12
    • 1970-01-01
    相关资源
    最近更新 更多