0

我在关系的一侧有三个不同的定义良好的实体,Foo, Bar, Baz, 和一个单独的实体,这些都需要在另一侧绑定,我们称之为LogEntry。就 sql 而言,我的关系有效,但我不知道如何在 中定义它DbContext modelBuilder并让导航道具在使用.Include().

要求:

LogEntry必须与一个Foo, Bar, 或Baz

Foo, Bar, 或Baz可能只与零或一LogEntry有关

所需用途:

我想有一个导航属性两种方式。

.Include(x => x.LogEntry)...检索所有Foo, Bar, 或Bazs时

db.LogEntries.Include(x => x.Foo).Include(x => x.Bar).Include(x => x.Baz)...

我试过的:

我们使用代码优先,但我们从.sql脚本生成表。

--LogEntry.sql rough details

LogEntry UNIQUEIDENTIFIER NONCLUSTERED PRIMARY KEY NOT NULL,
FooID UNIQUEIDENTIFIER NULL
BarID UNIQUEIDENTIFIER NULL
BazID UNIQUEIDENTIFIER NULL

-- Other Fields, etc...

-- CONSTRAINT FKs to the Foo,Bar,Baz tables

-- CONSTRAINT CHECK (FooID IS NOT NULL AND BarID IS NULL AND BazID IS NULL or ...etc) 
-- to make sure each LogEntry is related to one entity regardless of type

-- CONSTRAINT UNIQUE for each Foo, Bar, Baz ID that isn't null

我尝试了几种不同的方法来定义与 的关系DbContext ModelBuilder,但是我尝试过的任何方法都没有在.Include()ed 时填充 nav 道具。

// Example
modelBuilder.Entity<Foo>
    .HasOptional(f => f.LogEntry)
    .WithRequired(l => l.Foo);

第一种方法是在每个Foo,Bar或上放置一个可为空的 FKBaz并在 , 或 nav 属性上放置 Lists of Foo,BarBaznav props LogEntry。我们将只依靠代码永远不会添加超过一个Foo,Bar或。这使得实体关系变得微不足道,但与它们的使用方式不匹配,并且会导致开发人员混淆。另一个问题是我们无法对跨 、 或 表的 FK实施唯一检查。BazLogEntryLogEntryFooBarBaz

总之:

有没有办法在实体框架中定义这种关系以获得双方的工作导航属性?

4

1 回答 1

0

1)你有解决方案。

Foo、Bar 和 Baz 应该有一个ICollection<LogEntry>Navigation 属性,而 LogEntry 应该有一个 Foo、Bar 和 Baz 的 Navigation 属性。这很烦人,但这是您选择的模型。

EF 代码优先不会为您生成检查约束,但您可以在迁移中添加它,或者管理数据库优先并从那里对实体进行逆向工程。

如果您不需要跨 Foo、Bar 和 Baz 查询 LogEntries,那么您可能更喜欢三个不同的 LogEntry 表。

2)您确定要对所有密钥使用 UNIQUEIDENTIFIER 吗?即使是LogEntry?这可能会很昂贵,尤其是如果您不使用NEWSEQUENTIALID()生成顺序值。

这是一个示例,包括客户端顺序 GUID 生成:

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


namespace ef62test
{
    public class SQLGuidUtil
    {
        [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
        static extern int UuidCreateSequential(out Guid guid);

        public static Guid NewSequentialId()
        {
            Guid guid;
            UuidCreateSequential(out guid);
            var s = guid.ToByteArray();
            var t = new byte[16];
            t[3] = s[0];
            t[2] = s[1];
            t[1] = s[2];
            t[0] = s[3];
            t[5] = s[4];
            t[4] = s[5];
            t[7] = s[6];
            t[6] = s[7];
            t[8] = s[8];
            t[9] = s[9];
            t[10] = s[10];
            t[11] = s[11];
            t[12] = s[12];
            t[13] = s[13];
            t[14] = s[14];
            t[15] = s[15];
            return new Guid(t);
        }
    }


    class Program
    {

        public class LogEntry
        {

            public Guid LogEntryId { get; set; } = SQLGuidUtil.NewSequentialId();

            public Guid? FooId { get; set; }
            public Guid? BarId { get; set; }
            public Guid? BazId { get; set; }
            public virtual Foo Foo { get; set; }
            public virtual Bar Bar { get; set; }
            public virtual Baz Baz { get; set; }
        }
        public class Foo
        {
            public Guid FooId { get; set; } = SQLGuidUtil.NewSequentialId();
            public ICollection<LogEntry> LogEntries { get; } = new HashSet<LogEntry>();
        }
        public class Bar
        {
            public Guid BarId { get; set; } = SQLGuidUtil.NewSequentialId();
            public ICollection<LogEntry> LogEntries { get; } = new HashSet<LogEntry>();
        }
        public class Baz
        {
            public Guid BazId { get; set; } = SQLGuidUtil.NewSequentialId();
            public ICollection<LogEntry> LogEntries { get; } = new HashSet<LogEntry>();
        }


        public class MyDbContext : DbContext
        {
            public MyDbContext(string constr) : base(constr)
            { }
            public DbSet<Foo> Foos { get; set; }
            public DbSet<Bar> Bars { get; set; }
            public DbSet<Baz> Bazs { get; set; }
            public DbSet<LogEntry> LogEntries { get; set; }
        }


        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());

            using (var db = new MyDbContext("Server=.;database=ef62test;integrated security=true"))
            {

                db.Database.Initialize(true);

                db.Database.Log = s => Console.WriteLine(s);

                var foo = new Foo();
                foo.LogEntries.Add(new LogEntry());

                var bar = new Bar();
                bar.LogEntries.Add(new LogEntry());

                var baz = new Baz();
                baz.LogEntries.Add(new LogEntry());

                db.Foos.Add(foo);
                db.Bars.Add(bar);
                db.Bazs.Add(baz);
                db.SaveChanges();
            }

            using (var db = new MyDbContext("Server=.;database=ef62test;integrated security=true"))
            {
                db.Configuration.LazyLoadingEnabled = false;
                db.Database.Log = s => Console.WriteLine(s);

                var logs = db.LogEntries
                           .Include(l => l.Foo)
                           .Include(l => l.Bar)
                           .Include(l => l.Baz)
                           .ToList();

                foreach (var log in logs)
                {
                    Console.WriteLine($"Foo:{log.Foo?.FooId} Bar:{log.Bar?.BarId} Baz:{log.Baz?.BazId}");
                }            

            }
            Console.WriteLine("Hit any key to exit.");
            Console.ReadKey();
        }
    }
}
于 2018-08-19T13:36:33.340 回答