【问题标题】:EFCore does not create all the columns in the databaseEFCore 不会创建数据库中的所有列
【发布时间】:2020-07-27 12:36:31
【问题描述】:

我是 EFCore 新手,我不明白是否有必要在 OnModelCreating() 方法中指定所有属性,因为当我不需要时,它不会自动添加它们。

例如,我是否必须在OnModelCreating() 中为我的班级的每个属性添加这一行:

builder.Entity<Entity>().Property(p => p.PropertyName); 

似乎只有当我使用以前的方法包含它们时,EFcore 才会在数据库中创建列,我确信它不必是这样,因为我遵循了 Ms Docs 教程并且我不必这样做,但是这次我不行了

这是我的模型

    public class Block 
    {
        public int SequenceNumber {get ; private set;}
        public virtual BlockHeader Header {get ; private set;}
        public List<Transaction> Transactions {get ; private set;}

        public Block(List<Transaction> transactions,int sequencenumber, BlockHeader header)
        {
            Transactions= transactions;
            Header=header;        
            Header.SequenceNumber=sequencenumber;
            SequenceNumber=sequencenumber;
        }
        public Block(){
        }
    }


 public class BlockHeader
    {
        [MaxLength(256)]
        public byte[] Hash {get ;} 
        [MaxLength(256)]
        public byte[]  PreviousHash { get;}
        public DateTime timestamp { get; }
        public int version{ get; }
        [MaxLength(256)]
        public byte[] MerkleRoot{ get; }
        public int SequenceNumber {get ; set;}
        //this property is used only to configuire the one to one relationship
        public Block block{get;set;}
        public BlockHeader (byte[] previoushash,int sequencenumber,List<Transaction> transactions)
        {
            timestamp = DateTime.Now;
            PreviousHash =  previoushash;
            version = 1;
            MerkleRoot = ComputeMerkleRoot(transactions);
            Hash = ComputeBlockHash();
        }

        public BlockHeader(){

        }
}

这里是上下文类:

  class BlockchainDB : DbContext
        {
            public BlockchainDB()
            {
            }
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseMySQL("server=localhost;database = Blockchain ;user = root ; password = sdeath");
            }
            protected override void OnModelCreating(ModelBuilder builder){
                
                builder.Entity<Block>().HasKey(block => block.SequenceNumber);
                builder.Entity<Block>().HasOne<BlockHeader>(block => block.Header)
                .WithOne(header => header.block)
                .HasForeignKey<BlockHeader>(header => header.SequenceNumber);
                
                builder.Entity<BlockHeader>().Property(p => p.Hash).HasMaxLength(256);
                builder.Entity<BlockHeader>().HasKey(header => header.Hash);
                builder.Entity<BlockHeader>().Property(p => p.MerkleRoot); 
                builder.Entity<BlockHeader>().Property( p => p.PreviousHash); 
            }
            public DbSet<Block> Block {get; set;}
            public DbSet<BlockHeader> blockheader {get; set;}
        }

我错过了什么吗?

【问题讨论】:

  • 试试 { get;放; } 在所有属性上
  • @ErikEJ 如果我不想拥有二传手怎么办?
  • @ezio 问题是实体框架在映射数据库模型时需要它们。
  • @dimitar.d 所以这是一条死胡同!
  • @ezio 为什么没有二传手对你来说那么重要?这些对象只是 EF 为您映射的模型。考虑为您的应用程序创建单独的对象并根据需要对其进行建模。

标签: c# orm entity-framework-core


【解决方案1】:

这是一个完整的示例控制台项目,它尽可能使用数据注释而不是 Fluent API(这似乎是您正在寻找的)。我冒昧地对模型进行了一些小改动:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class Block
    {
        [Key]
        public int SequenceNumber { get; private set; }
        
        public BlockHeader Header { get; private set; }
        public ICollection<Transaction> Transactions { get; private set; } = new HashSet<Transaction>();

        public Block(int sequenceNumber, BlockHeader header, ICollection<Transaction> transactions)
        {
            Transactions = transactions;
            Header = header;
            Header.SequenceNumber = sequenceNumber;
            SequenceNumber = sequenceNumber;
        }

        private Block() // <-- can be made private 
        {
        }
    }

    public class BlockHeader
    {
        [Key]
        public int SequenceNumber { get; internal set; }

        [MaxLength(256)]
        public byte[] Hash { get; private set; }

        [MaxLength(256)]
        public byte[] PreviousHash { get; private set; }

        public DateTime TimeStamp { get; private set; }
        public int Version { get; private set; }

        [MaxLength(256)]
        public byte[] MerkleRoot { get; private set; }

        public Block Block { get; private set; }

        public BlockHeader(byte[] previousHash, ICollection<Transaction> transactions)
        {
            Hash = ComputeBlockHash();
            PreviousHash = previousHash;
            TimeStamp = DateTime.Now;
            Version = 1;
            MerkleRoot = ComputeMerkleRoot(transactions);
        }

        private BlockHeader() // <-- can be made private 
        {
        }

        // Stub:
        private byte[] ComputeMerkleRoot(ICollection<Transaction> transactions)
            => Array.Empty<byte>();

        // Stub:
        private byte[] ComputeBlockHash()
            => Array.Empty<byte>();
    }

    public class Transaction
    {
        [Key]
        public int TransactionNumber { get; set; }
        public int SequenceNumber { get; set; }

        public Block Block { get; set; }
    }

    public class BlockchainDB : DbContext
    {
        public DbSet<Block> Blocks { get; set; }
        public DbSet<BlockHeader> BlockHeaders { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql(
                    "server=127.0.0.1;port=3306;user=root;password=;database=So63115500",
                    b => b.ServerVersion("8.0.20-mysql"))
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Block>(
                entity =>
                {
                    entity.HasOne(e => e.Header)
                        .WithOne(e => e.Block)
                        .HasForeignKey<BlockHeader>(e => e.SequenceNumber);
                });

            builder.Entity<Transaction>(
                entity =>
                {
                    entity.HasOne(e => e.Block)
                        .WithMany(e => e.Transactions)
                        .HasForeignKey(e => e.SequenceNumber);
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using (var context = new BlockchainDB())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                //
                // Let's add some sample data:
                //
                
                var transactionsBlock1 = new List<Transaction>
                {
                    new Transaction {TransactionNumber = 1},
                    new Transaction {TransactionNumber = 2},
                };
                
                var transactionsBlock2 = new List<Transaction>
                {
                    new Transaction {TransactionNumber = 3},
                    new Transaction {TransactionNumber = 4},
                };
                
                context.Blocks.AddRange(
                    new Block(1, new BlockHeader(Array.Empty<byte>(), transactionsBlock1), transactionsBlock1),
                    new Block(2, new BlockHeader(Array.Empty<byte>(), transactionsBlock2), transactionsBlock2));

                context.SaveChanges();
            }
            
            using (var context = new BlockchainDB())
            {
                var blocksWithHeaderAndTransactions = context.Blocks
                    .Include(b => b.Header)
                    .Include(b => b.Transactions)
                    .ToList();

                Debug.Assert(blocksWithHeaderAndTransactions.Count == 2);
                Debug.Assert(blocksWithHeaderAndTransactions[0].SequenceNumber == 1);
                Debug.Assert(blocksWithHeaderAndTransactions[0].Header != null);
                Debug.Assert(blocksWithHeaderAndTransactions[0].Header.SequenceNumber == 1);
                Debug.Assert(blocksWithHeaderAndTransactions[0].Transactions.Count == 2);
                Debug.Assert(blocksWithHeaderAndTransactions[0].Transactions.First().SequenceNumber == 1);
                Debug.Assert(blocksWithHeaderAndTransactions[0].Transactions.First().TransactionNumber == 1);
            }
        }
    }
}

我使用Pomelo.EntityFrameworkCore.MySql 作为 EF Core 提供程序(并使用.UseMySql() 代替 .UseMySQL()),与 Oracle 自己的提供程序相比,它速度更快、支持更好、功能集更丰富。 (完全披露,我是 Pomelo 提供商的首席开发人员。)

似乎只有当我使用以前的方法包含它们时,EFcore 才会在数据库中创建列,我确信它不必是这样,因为我遵循了 Ms Docs 教程并且我不必这样做,但是这次我不行了

尝试使用 { get;放; } 在所有属性上 – ErikEJ

@ErikEJ 如果我不想有二传手怎么办?

EF Core 也适用于具有 private 设置器的属性。您也可以改用Backing Fields

您似乎非常关心您的属性和方法的可见性。您可能有兴趣知道,模型类的默认构造函数不需要公开。

【讨论】:

  • okey 所以据我了解,当我使用 pomelo 时,我不必在 OnModelCreating 方法中指定每个属性,所以 pomelo 会默认创建这些列,即使它们有私有设置器?顺便说一句,非常感谢您花时间回答我的问题!
  • 您实际上可以使用 any 提供程序执行此操作,因为这是 EF Core 功能。所以这也适用于甲骨文自己的供应商。 Pomelo 参考资料只是我的一个旁注,旨在为您指明将 MySQL 与 EF Core 结合使用的更好方向。它与您的实际问题无关,但我在答案的示例中使用了它(.UseMySql() 代码与 Pomelo 相关,与 Oracle 无关;例如,Oracle 没有 .ServerVersion() 选项)。
  • 当我使用 oracle 提供程序尝试它时,它不会添加任何具有私有 setter 的属性,除非我专门添加它 OnModelCreating 这样的方法:``` builder.Entity() .Property(p => p.PropertyName);```,所以从你的回答来看,它应该可以在没有明确指定的情况下工作!!
  • 我发布的示例代码与 MySql.Data.EntityFrameworkCore 8.0.21 和 EF Core 3.1.x 完美配合。如果你尝试它,它也应该对你有用。如果您需要帮助,请随时将您的项目上传到 GitHub 并向我们提供链接。
  • 还可以查看 EF Core 文档中的 Read-only properties
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
  • 2019-07-11
  • 2017-09-10
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多