【问题标题】:Why would a datetime prevent a navigation property from getting loaded?为什么日期时间会阻止导航属性被加载?
【发布时间】:2012-08-02 11:40:43
【问题描述】:

我正在使用实体框架 4.3.1,使用 DbContext POCO 方法针对 SQL Server 2012 数据库。

我在数据库中只有两个表,它们看起来像这样:

注意:数据库中根本没有指定外键 - 我只是在模型中强制执行关系(我无法更改数据库)。

它们每个都有一行数据,如下所示:

我执行了以下查询以确保连接可以正常工作:

现在,我有以下实体:

public class Two
{
    public long TwoId { get; set; }
    public string OneId { get; set; }
    public virtual One One { get; set; }
}

public class One
{
    public string OneId { get; set; }
    public DateTime DeliveryDate { get; set; }
    public virtual ICollection<Two> Twos { get; private set; }

    public void AddTwo(Two two)
    {
        if (two == null)
            throw new ArgumentNullException("two");

        if (Twos == null)
            Twos = new List<Two>();

        if (!Twos.Contains(two))
            Twos.Add(two);

        two.One = this;
    }
}

这是上下文:

public class TestContext : DbContext
{
    public TestContext(string conectionString)
        : base(conectionString)
    {
        Configuration.LazyLoadingEnabled = true;
        Ones = Set<One>();
        Twos = Set<Two>();
    }
    public DbSet<One> Ones { get; private set; }
    public DbSet<Two> Twos { get; private set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        var one = modelBuilder.Entity<One>();
        one.ToTable("One");
        one.HasKey(d => d.OneId);

        var two = modelBuilder.Entity<Two>();
        two.ToTable("Two");
        two.HasKey(d => new
                            {
                                d.OneId,
                                d.TwoId
                            });
        two.Property(p => p.TwoId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        two.HasRequired(t => t.One)
            .WithMany(o => o.Twos)
            .HasForeignKey(o => o.OneId);
        base.OnModelCreating(modelBuilder);
    }
}

当我运行这段代码时,Why is this printed? 打印到我的控制台 - 我不希望你可以看到应该填写导航属性(我什至明确包含它):

using (var ctx = new TestContext(@"......"))
{
    const string oneId = "111348718";
    var one = ctx.Ones.Single(o => o.OneId.Equals(oneId));

    if (one != null)
    {
        var sdi = ctx
            .Twos
            .Include(s => s.One)
            .Single(s => s.OneId.Equals(oneId));

        if (sdi.One == null)
        {
            Console.WriteLine("Why is this printed?");
        }
        else
        {
            Console.WriteLine("This is what I expect");
        }
    }
}

现在,这是非常奇怪的一点:如果我只是从 One 类中注释掉 DeliveryDate 属性,它就可以正常工作(我将 This is what I expect 打印到控制台)。

这里出了什么问题,我该如何解决?

注意:如果我查看 one 变量上的 DeliveryDate 属性,它已正确设置为预期值,因此数据库中的日期必须是正确的,并且实体框架完全能够实现它.

更多信息:它是一个日期这一事实并不重要 - 如果是,说一个 nvarchar 它仍然失败 - 似乎任何简单的属性都会导致整个事情崩溃 - 这感觉就像对我来说是一个错误......

【问题讨论】:

    标签: c# ef-code-first sql-server-2012 navigation-properties entity-framework-4.3.1


    【解决方案1】:

    好的,所以我已经深入了解了,我认为这是实体框架中的一个错误,但我可以解决它。原来是这样的……

    Two 表中的 OneId 列末尾有一个空格。

    是的 - 想一想 - 这有多可怕!?

    所以我敲了一个 sql 脚本,该脚本在数据库中的每个 nvarchar 列上执行了一个 LTRIM(RTRIM( 并且一切正常。

    现在,鉴于 SQL Server 不关心空格(正如问题中带有 join 的查询所证明的那样),我不太喜欢 Entity Framework 确实关心,尤其是因为它在这里完全奇怪且不一致。当然,我调用Single 来获取填充的sdi 变量的点应该已经抛出,或者导航属性应该已经填充 - 一个或另一个,但这种行为真的很混乱(在我看来) - 什么有一些任意财产和鱼的价格有关系??

    为了完整起见,这里真的很简单地重现了这一点。创建一个新数据库,然后在其上运行此 SQL 脚本:

    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [dbo].[One](
        [OneId] [nvarchar](10) NOT NULL,
        [SomeInt] [int] NOT NULL,
     CONSTRAINT [PK_Delivery] PRIMARY KEY CLUSTERED 
    (
        [OneId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    
    CREATE TABLE [dbo].[Two](
        [TwoId] [int] NOT NULL,
        [OneId] [nvarchar](10) NOT NULL,
     CONSTRAINT [PK_DeliveryItem] PRIMARY KEY CLUSTERED 
    (
        [TwoId] ASC,
        [OneId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    INSERT INTO Two(TwoId, OneId) VALUES (1, '1 ')
    INSERT INTO One(OneId, SomeInt) VALUES ('1', 1.0)
    

    现在创建一个 c# 控制台应用程序,添加对 EntityFramework(版本 4.3.1)和 System.Data.Entity 程序集的引用,将这段代码放入并运行它 - 它将打印SHOULD NOT BE PRINTED!!!

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity;
    using System.Linq;
    
    namespace EFTest
    {
        public class Two
        {
            public int TwoId { get; set; }
            public string OneId { get; set; }
            public virtual One One { get; set; }
        }
    
        public class One
        {
            public string OneId { get; set; }
            public virtual ICollection<Two> Twos { get; private set; }
    
            // Comment out this property and it will work
            public int SomeInt { get; set; }
    
            public void AddTwo(Two two)
            {
                if (two == null)
                    throw new ArgumentNullException("two");
    
                if (Twos == null)
                    Twos = new List<Two>();
    
                if (!Twos.Contains(two))
                    Twos.Add(two);
    
                two.One = this;
            }
        }
    
        public class Context : DbContext
        {
            public Context(string connectionString)
                : base(connectionString)
            {
                Configuration.LazyLoadingEnabled = true;
                Ones = Set<One>();
                Twos = Set<Two>();
            }
    
            public DbSet<One> Ones { get; private set; }
            public DbSet<Two> Twos { get; private set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder
                    .Entity<One>()
                    .HasKey(d => d.OneId)
                    .ToTable("One");
    
                var two = modelBuilder.Entity<Two>();
                two.ToTable("Two");
                two.HasKey(d => new
                                    {
                                        d.OneId,
                                        d.TwoId
                                    });
    
                two.Property(d => d.TwoId)
                    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    
                two.HasRequired(m => m.One)
                    .WithMany(t => t.Twos)
                    .HasForeignKey(d => d.OneId);
    
                base.OnModelCreating(modelBuilder);
            }
        }
    
        internal class Program
        {
            private static void Main()
            {
                using (var ctx = new Context(@"your connection string"))
                {
                    const string oneId = "1";
                    var one = ctx.Ones.Single(o => o.OneId.Equals(oneId));
    
                    if (one == null)
                    {
                        Console.WriteLine("No row with one ID in the database");
                        return;
                    }
    
                    var two = ctx
                        .Twos
                        .Include(s => s.One)
                        .Single(s => s.OneId.Equals(oneId));
    
                    Console.WriteLine(two.One == null
                                          ? "SHOULD NOT BE PRINTED!!!"
                                          : "SHOULD BE PRINTED");
                }
            }
        }
    }
    

    然后执行以下操作之一:

    • 注释掉One 类的SomeInt 属性
    • 删除数据库中的空间 (UPDATE Two SET OneId = RTRIM(OneId))。

    两者都可以(显然修剪是现实生活中唯一合理的修复方法)。

    【讨论】:

      猜你喜欢
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-16
      • 2016-12-19
      • 2021-11-26
      • 1970-01-01
      相关资源
      最近更新 更多