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.Entity8.0.21
和 EF Core 3.1.x
完美配合。如果你尝试它,它也应该对你有用。如果您需要帮助,请随时将您的项目上传到 GitHub 并向我们提供链接。
还可以查看 EF Core 文档中的 Read-only properties。以上是关于EFCore 不会创建数据库中的所有列的主要内容,如果未能解决你的问题,请参考以下文章