EFCore 不会创建数据库中的所有列

Posted

技术标签:

【中文标题】EFCore 不会创建数据库中的所有列【英文标题】:EFCore does not create all the columns in the database 【发布时间】: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 blockget;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 为您映射的模型。考虑为您的应用程序创建单独的对象并根据需要对其进行建模。 【参考方案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。

以上是关于EFCore 不会创建数据库中的所有列的主要内容,如果未能解决你的问题,请参考以下文章

如何将同一列添加到 EF Core 中的所有实体?

当您还在 EFCore 3.1.11 中选择其他对象列表时,左连接不会带来所有结果

EF Core 无法创建数据库 :( [关闭]

Ssas 多维数据集向导不会为事实表列创建度量

迭代地绘制ggplot中的所有列

在 MS Access 中使用表 1 中的所有数据和列创建表,并添加表 2 中的行