7

SQL Server 2012 データベースに対して DbContext POCO アプローチを使用して、Entity Framework 4.3.1 を使用しています。

データベースには 2 つのテーブルしかなく、次のようになります。

テーブル

注: データベースには外部キーがまったく指定されていません。モデルで関係を強制しているだけです (データベースを変更することはできません)。

それぞれに、次のような 1 行のデータがあります。

データ

結合が機能することを確認するために、次のクエリを実行しました。

検証

現在、次のエンティティがあります。

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");
        }
    }
}

さて、これは本当に奇妙なビットです。クラスDeliveryDateからプロパティをコメントアウトするだけで、正常に機能します(コンソールに出力されます)。OneThis is what I expect

ここで何が問題で、どうすれば解決できますか?

注:変数のDeliveryDateプロパティを見るoneと、期待値に正しく設定されているため、日付はデータベースで問題なく、エンティティ フレームワークはそれを完全に具現化できます。

詳細情報: 日付であるという事実は問題ではありません - もしそうであれば、nvarchar はまだ失敗します - 単純なプロパティがすべてを失敗させるようです - これは私にはバグのように感じます...

4

1 に答える 1

2

これは Entity Framework のバグだと思いますが、回避できます。これがそれです...

Two テーブルの OneId 列には、最後に 1 つのスペースがありました。

うん - 考えてみて - それはなんて恐ろしいことでしょう!?

LTRIM(RTRIM(そこで、データベース内のすべての nvarchar 列に対して実行する SQL スクリプトを作成しましたが、すべて正常に動作しています。

さて、SQLサーバーがスペースを気にしないことを考えると(質問の結合を使用したクエリで証明されているように)、特にここでは完全に奇妙で一貫性がないため、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)

ここで、ac# コンソール アプリケーションを作成し、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");
            }
        }
    }
}

次に、次のいずれかを実行します。

  • クラスのSomeIntプロパティをコメントアウトしますOne
  • データベースのスペースを切り取ります ( UPDATE Two SET OneId = RTRIM(OneId))。

どちらでも機能します (明らかに、トリミングは唯一の合理的な実際の修正です)。

于 2012-08-02T15:05:23.140 に答える