【EF Core】“DB First”方案下用编程方式生成数据库模型代码
<p>大伙伴们只要学过三天 EF Core 一定知道,.NET SDK 有一个 dotnet-ef 工具(需要安装),可以用来创建/迁移数据库、生成模型代码、优化模型和查询代码等。必要时还能生一个单独的 exe,可以运行它来更新数据库结构。</p><p>不过,按照官方的设计思路,肯定不会把所有功能都堆在 exe 项目中的,这不,dotnet-ef 只是做个封装,可以通过命令行执行罢了,其实核心功能是写在 Design 包里面(Nuget 包名:Microsoft.EntityFrameworkCore.Design)。于是,咱们可以开发自己的 EF 辅助工具。比如,你可以把命令行操作的功能搞成窗口图形化操作。当然,这些功能仅限开发者使用,用户一般不需要(不一般的用户除外)。</p>
<p>如此,在 DB First 方案(先有数据库)中,咱们可以把生成实体类以及 dbContext 类的功能直接写到项目代码中,然后加上一个条件编译,在需要生成代码时开启一下编译符号,运行一个项目就能生成实体模型了。其他情况下把条件编译符号注释掉就可以。</p>
<p>这个功能就有点像 Sugar 的玩法。老周在某些项目中就是这么干的。不过老周更喜欢 EF,理由有:</p>
<p>1、EF Core 更灵活。</p>
<p>2、EF Core 的表达式树翻译功能比 Sugar 完善,功能更多。</p>
<p>3、有官方支持的优先用原则,没有才考虑第三方。</p>
<p>好了,不扯废话了,咱们开始!</p>
<h2>一、基础知识</h2>
<p>首先,咱们要明确功能:数据库已经有了,可能是你创建的,可能是别人创建的。很多团队都会把搞数据库,写存储过程的单独一堆人去干,然后,项目的非数据库部分另一堆人去做。所以,小型数据库才考虑用 EF Core 去创建,复杂的数据库还是先创建数据库好一些。咱们要做的就是<span style="color: rgba(255, 0, 0, 1)"><strong>根据现有的数据库和表,直接生成实体类和 DbContext 的派生类</strong></span>。</p>
<p>在分析思路之前,既然大伙儿都是玩 .NET 的,那就坚守这个原则:<strong><span style="color: rgba(0, 0, 255, 1)">处处都是服务容器和依赖注入</span></strong>。</p>
<p>好,有这个思想准备,咱们才能讲知识点。咱们来认识几个新朋友,熟悉一下,以后才能好好利用他们,嗯,朋友是拿来利用的。</p>
<p>第一位,本名 <strong><span style="color: rgba(128, 128, 0, 1)">IDatabaseModelFactory</span></strong>。他的绝活本领是爆库。你要从数据库生成实体,那你得知道数据库里有哪些表,表中有哪些列,哪个是主键,列的类型是什么……没事,这些信息交给这位朋友就行。</p>
<p>调用以下方法,你能得到一个 DatabaseModel 对象。</p>
<div class="cnblogs_code">
<pre>DatabaseModel Create(<span style="color: rgba(0, 0, 255, 1)">string</span> connectionString, DatabaseModelFactoryOptions options);</pre>
</div>
<p>第一个参数就是连接字符串,这个不用介绍了吧。要爆库你得知道库在哪里吧。第二个参数是选项类,用来配置相关参数的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> DatabaseModelFactoryOptions(IEnumerable<<span style="color: rgba(0, 0, 255, 1)">string</span>>? tables = <span style="color: rgba(0, 0, 255, 1)">null</span>, IEnumerable<<span style="color: rgba(0, 0, 255, 1)">string</span>>? schemas = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
Tables </span>= tables ??<span style="color: rgba(0, 0, 0, 1)"> [];
Schemas </span>= schemas ??<span style="color: rgba(0, 0, 0, 1)"> [];
}</span></pre>
</div>
<p>上述是它的构造函数,Tables 是你要告诉朋友,你想要哪些表;Schemas 表示你要的架构,比如 dbo。这两个参数都可以是 null,如果是 null 表示要全库爆。</p>
<p>Create 方法返回的 DatabaseModel 对象包含数据库名、数据库中表、列、主键、外键、索引等相关信息。</p>
<p>好了,拿到数据库信息了,轮到第二位朋友出场—— <strong><span style="color: rgba(128, 128, 0, 1)">IScaffoldingModelFactory</span></strong>。他的绝活是加工,把你从数据库中爆出来的信息处理后,直接返回一个 IModel。对,就是 EF Core 中用的数据库模型,所以,如果你不打算生成代码,这时候你完全可以把这个 IModel 作为 DbContext 的外部模型使用。还记得老周写过使用外部模型的水文吗?在 DbContext 选项配置时,用 UseModel 方法。</p>
<p>但,建议你不要这么干,你想想每次运行程序都要爆一次数据库再转换为 EF Core 模型,既浪费性能也没啥实际意义。所以,这个生成的 IModel 还要进一步处理。</p>
<p>第三位朋友叫 <strong><span style="color: rgba(128, 128, 0, 1)">IModelCodeGeneratorSelector</span></strong>。他是一名零件选配师,他会根据你的需要帮你找到合适的专属文员(代码生成器)。这位朋友有一个 Select 方法,调用后进行筛选。</p>
<div class="cnblogs_code">
<pre>
IModelCodeGenerator Select(</span><span style="color: rgba(0, 0, 255, 1)">string</span>?<span style="color: rgba(0, 0, 0, 1)"> language);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注意,上面的方法过时,而下面的方法又反过去调用它</span>
<span style="color: rgba(0, 0, 0, 1)">IModelCodeGenerator Select(ModelCodeGenerationOptions options)
</span><span style="color: rgba(0, 0, 255, 1)">#pragma</span> warning disable CS0618 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Type or member is obsolete</span>
=><span style="color: rgba(0, 0, 0, 1)"> Select(options.Language);
</span><span style="color: rgba(0, 0, 255, 1)">#pragma</span> warning restore CS0618</pre>
</div>
<p>Select 的旧版本已被标记为过时,但下面的方法又调用它。这是什么骚操作?这是官方团队的兼容操作。过时的方法只有一个字符串参数,表示生成的代码语言(“VB”,“C#”)。而新方法的参数是一个 ModelCodeGenerationOptions 选项类,可以配置更多东西。</p>
<p>Select 方法帮你选好了心仪的文员妹妹,她叫 <strong><span style="color: rgba(128, 128, 0, 1)">IModelCodeGenerator</span></strong>。她虽然学历不高,但很勤奋很务实,你可以相信她。调用她的 GenerateModel 方法,她会帮你生成代码。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ScaffoldedModel GenerateModel(
IModel model,
ModelCodeGenerationOptions options);</span></pre>
</div>
<p>model 参数就是第二位朋友 IScaffoldingModelFactory 帮你生成的模型;options 参数是选项,和 IModelCodeGeneratorSelector.Select 方法用的是同一个。</p>
<p>到这里基本工作就完成了,返回的 ScaffoldedModel 对象中已经包含代码,以及代码要存放的路径了。不过,这些目前还在内存中,未真正写入磁盘文件。程序退出后就没了。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ScaffoldedModel
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> dbContext 类的代码,以及文件路径</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> ScaffoldedFile ContextFile { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 附加文件,通常是实体类的代码以及文件路径,每个实体类占一个文件</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> IList<ScaffoldedFile> AdditionalFiles { <span style="color: rgba(0, 0, 255, 1)">get</span>; } = <span style="color: rgba(0, 0, 255, 1)">new</span> List<ScaffoldedFile><span style="color: rgba(0, 0, 0, 1)">();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> ScaffoldedFile(<span style="color: rgba(0, 0, 255, 1)">string</span> path, <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> code)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代码要存入的文件路径</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Path { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } =<span style="color: rgba(0, 0, 0, 1)"> path;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 已生成的代码</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Code { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } =<span style="color: rgba(0, 0, 0, 1)"> code;
}</span></pre>
</div>
<p>总结一下,流程如下:</p>
<p><span style="color: rgba(0, 0, 128, 1)">A、获取数据库信息;</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">B、生成设计时模型;</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">C、生成代码;</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">D、保存代码。</span></p>
<p>你一定会抱怨了,这过程有点复杂。别急,还没完呢,继续往下看,简单的来了。</p>
<p>上面提到的几位朋友,你一个个地告诉他们干什么是有些麻烦的,所以,把他们组成一个团队,设立一名管理者,有事只要跟他们的老大说行了。这位由不民主制度任命的老大叫 <span style="color: rgba(0, 128, 0, 1)"><strong>IReverseEngineerScaffolder</strong></span>(位于 Microsoft.EntityFrameworkCore.Scaffolding 命名空间),默认实现类是 ReverseEngineerScaffolder(位于 Microsoft.EntityFrameworkCore.Scaffolding.Internal 命名空间)。虽然这个类是 public 的,但官方团队让它藏在 Internal 命名空间下,这表明:在功能上是不希望外部代码访问的。在使用时,咱们的确不用访问该类,而是通过 IReverseEngineerScaffolder 接口来调用。</p>
<p>前面介绍的几位朋友,吃过几回饭后你可以忘记,现在你只要记住 IReverseEngineerScaffolder 即可。有事找他。</p>
<p>IReverseEngineerScaffolder 接口定义了两个方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ScaffoldedModel ScaffoldModel(
</span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> connectionString,
DatabaseModelFactoryOptions databaseOptions,
ModelReverseEngineerOptions modelOptions,
ModelCodeGenerationOptions codeOptions);
SavedModelFiles Save(
ScaffoldedModel scaffoldedModel,
</span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> outputDir,
</span><span style="color: rgba(0, 0, 255, 1)">bool</span> overwriteFiles);</pre>
</div>
<p>ScaffoldModel 方法根据数据库生成代码,Save 方法把代码写入文件。这样一来,是不是变得简单了?只要一个服务接口,调用两个方法成员就完事了。</p>
<p> </p>
<h2>二、如何使用</h2>
<p>既然处处是注入,那就得先初始化服务集合。Design 库提供了两个扩展方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> IServiceCollection AddDbContextDesignTimeServices(
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> IServiceCollection services,
DbContext context);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> IServiceCollection AddEntityFrameworkDesignTimeServices(
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> IServiceCollection services,
IOperationReporter</span>? reporter = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
Func</span><IServiceProvider>? applicationServiceProviderAccessor = <span style="color: rgba(0, 0, 255, 1)">null</span>);</pre>
</div>
<p>第一个扩展方法在生成实体代码时不需要,它的作用是把现有 DbContext 实例中的服务添加到服务容器中。咱们今天要实现的功能要用到第二个方法,它会添加设计时的一些基础服务,包括我们上面提到的几位新朋友。</p>
<p>当然,这是设计时的基础服务,不包括 EF 核心服务。核心服务一般可以通过实现 IDesignTimeServices 接口来添加。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> IDesignTimeServices
{
</span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> ConfigureDesignTimeServices(IServiceCollection serviceCollection);
}</span></pre>
</div>
<p>实现接口时,在 ConfigureDesignTimeServices 方法中,向服务容器添加需要的服务。</p>
<p>然后,在程序集级别使用 特性指定你实现 IDesignTimeServices 接口的类的完整名称(连同命名空间)。其他工具(如 dotnet-ef,或你自己实现的工具)可以通过反射获取它,动态实例化并调用 ConfigureDesignTimeServices 方法。当然,你不用反射,直接在代码中 new 也可以的。</p>
<p>其实,你完全可以偷懒,不用去实现 IDesignTimeServices 接口,因为每种数据库的提供者都会实现专用的类。比如 SQL Server 的提供者,会有一个 SqlServerDesignTimeServices 类,并且应用 特性。</p>
<div class="cnblogs_code">
<pre></pre>
</div>
<p>对于 SQLite 数据库,会有一个 SqliteDesignTimeServices 类,同样也会在程序集上应用 特性。</p>
<div class="cnblogs_code">
<pre></pre>
</div>
<p>这些默认实现的设计时服务提供类默认会把 EF 的核心服务、关系数据库相关、数据库专用的服务全部添加到服务容器,你不需要额外去处理。</p>
<p>当所有需要的服务都添加到容器后,生成 ServiceProvider。然后你直接从服务容器中获取 IReverseEngineerScaffolder 接口,配置好相关参数(如输出目录、DbContext 类的名称等),先调用 ScaffoldModel 方法生成代码,再调用 Save 方法写入文件就行了。</p>
<p> </p>
<h2>三、实例演示</h2>
<p>光说不练,惨过失恋。前文已介绍完所有基础知识了,该练练手了。</p>
<p>老周以 SQL Server 来演示,先创建数据库,以及两张表。一张表是客户表,一张是照片表。因为这是一家照相馆的信息管理系统。一位客户可以有多张照片,所以是“一对多”的关系(别问我怎么没有多对多,你一张照片给多个客户?这么有分享精神的吗?除非是大合照)。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">USE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">master</span><span style="color: rgba(255, 0, 0, 1)">]</span>
<span style="color: rgba(0, 0, 255, 1)">GO</span>
<span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">DATABASE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">SomeDB</span><span style="color: rgba(255, 0, 0, 1)">]</span>
<span style="color: rgba(0, 0, 255, 1)">GO</span>
<span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">dbo</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_customers</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">cust_id</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">INT</span> <span style="color: rgba(255, 0, 255, 1)">IDENTITY</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">12</span>)<span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">age</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">INT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">address</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">100</span>) <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">phone</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">CHAR</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">11</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">email</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">64</span>)<span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">remark</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 255, 1)">NTEXT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 0, 255, 1)">CONSTRAINT</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">PK_customers</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span> <span style="color: rgba(0, 0, 255, 1)">CLUSTERED</span> (<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">cust_id</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">ASC</span><span style="color: rgba(0, 0, 0, 1)">)
);
</span><span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">dbo</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_photos</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Id</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">INT</span> <span style="color: rgba(255, 0, 255, 1)">IDENTITY</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tags</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">30</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">dpi</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">REAL</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> ((<span style="color: rgba(128, 0, 0, 1); font-weight: bold">300.0</span>)) <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">width</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">FLOAT</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">53</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">height</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 255, 1)">FLOAT</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">53</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">cust_id</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">INT</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> ((<span style="color: rgba(128, 0, 0, 1); font-weight: bold">0</span>)) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 0, 255, 1)">CONSTRAINT</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">PK_photos</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span> <span style="color: rgba(0, 0, 255, 1)">CLUSTERED</span> (<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Id</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">ASC</span><span style="color: rgba(0, 0, 0, 1)">),
</span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">CONSTRAINT</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">FK_photos_custs</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">FOREIGN</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span> (<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">cust_id</span><span style="color: rgba(255, 0, 0, 1)">]</span>) <span style="color: rgba(0, 0, 255, 1)">REFERENCES</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">dbo</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_customers</span><span style="color: rgba(255, 0, 0, 1)">]</span> (<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">cust_id</span><span style="color: rgba(255, 0, 0, 1)">]</span>) <span style="color: rgba(0, 0, 255, 1)">ON</span> <span style="color: rgba(0, 0, 255, 1)">DELETE</span> <span style="color: rgba(0, 0, 255, 1)">CASCADE</span> <span style="color: rgba(0, 0, 255, 1)">ON</span> <span style="color: rgba(0, 0, 255, 1)">UPDATE</span> <span style="color: rgba(0, 0, 255, 1)">CASCADE</span></span><span style="color: rgba(0, 0, 0, 1)">
);</span></pre>
</div>
<p>注意外键是在 tb_photos 表中定义的,这里外键不唯一,要是搞唯一了就变成“一对一”关系了。</p>
<p>创建一个最简单的.NET项目(控制台),你需要向项目添加以下 nuget 包:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">ItemGroup</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">PackageReference </span><span style="color: rgba(255, 0, 0, 1)">Include</span><span style="color: rgba(0, 0, 255, 1)">="Microsoft.EntityFrameworkCore.Design"</span><span style="color: rgba(255, 0, 0, 1)"> Version</span><span style="color: rgba(0, 0, 255, 1)">="10.0.0"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">PrivateAssets</span><span style="color: rgba(0, 0, 255, 1)">></span>all<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">PrivateAssets</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="background-color: rgba(255, 255, 0, 1)"><strong><span style="color: rgba(0, 128, 0, 1)"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets></span></strong></span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">PackageReference</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">PackageReference </span><span style="color: rgba(255, 0, 0, 1)">Include</span><span style="color: rgba(0, 0, 255, 1)">="Microsoft.EntityFrameworkCore.SqlServer"</span><span style="color: rgba(255, 0, 0, 1)"> Version</span><span style="color: rgba(0, 0, 255, 1)">="10.0.0"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">ItemGroup</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p>严重注意:Design 是开发工具包,默认是不让你的代码访问的。当然,如果你考虑用反射的方法调用,那无所谓。这里咱们没必要去反射,应该直接访问,所以,在 PackageReference 元素下,把 IncludeAssets 整个节点注释掉,这样就能直接访问了。</p>
<p>其他的包都常规操作了,老周这里用的是 SQL Server就用 Sqlserver 包,你用的如果是 SQLite 那就 Sqlite 包。这个就不多说了,都懂的。</p>
<p>咱们并不是每次运行程序都需要生成代码的,除非是有改动(通常不会改,小改动的话也不用重新生成,直接手动改代码就行),所以定义一个符号,当需要生成实体代码时启用,毕竟这是为开发者服务的功能,不是面向最终用户。</p>
<div class="cnblogs_code">
<pre><strong><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">#define</span> GEN_CODES</span></strong><span style="color: rgba(0, 0, 0, 1)">
……
</span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
{
</span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">#if</span> GEN_CODES</span><span style="color: rgba(0, 0, 0, 1)">
GenModelCodes();
</span><span style="color: rgba(0, 0, 255, 1); background-color: rgba(255, 255, 0, 1)">#endif</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>下面我们把注意力集中到 GenModelCodes 方法上。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">#if</span> GEN_CODES
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> GenModelCodes()
{
……
}<br><span style="color: rgba(0, 0, 255, 1)">#endif</span></span></pre>
</div>
<p>用常量定义一些基本参数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">// 连接字符串</span>
<span style="color: rgba(0, 0, 255, 1)">const string</span> connectStr = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=SomeDB";
<span style="color: rgba(0, 128, 0, 1)">// 输出目录</span>
<span style="color: rgba(0, 0, 255, 1)">const string</span> outputDir = "..\\..\\..\\DBModels";
<span style="color: rgba(0, 128, 0, 1)">// 命名空间</span>
<span style="color: rgba(0, 0, 255, 1)">const string</span> myNamespace = "DB";
<span style="color: rgba(0, 128, 0, 1)">// 数据库上下文名称</span>
<span style="color: rgba(0, 0, 255, 1)">const string</span> contextName = "SomeDbContext";</pre>
</div>
<p>outputDir 常量指定的是输出目录,可以用相对路径(相对于当前程序),老周这里用了三个 ..,即往上跳三层目录。你猜猜是啥目录?(项目根目录)</p>
<p>对于命名空间,可以有两个,一个是 DbContext 派生类所在命名空间,一个是实体类所在命名空间。当然,老周这里只用了一个 DB,意思就是它们都位于 DB 命名空间内。</p>
<p>contextName 常量指示生成的 DbContext 子类的名字,这里老周给了它一个风雅的名字 SomeDbContext。</p>
<p>接着,咱们需要配置三个选项类(上文介绍过了,虽然名字有点臭长,但不用死记,大概记得就行)。</p>
<div class="cnblogs_code">
<pre>DatabaseModelFactoryOptions dmfacOpts = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">(
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选择你要的表</span>
tables: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_customers</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_photos</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选择你要的架构</span>
schemas: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">dbo</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]
);
ModelCodeGenerationOptions modgenOpt </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Language </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C#</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 语言可以不设置,默认 C#</span>
ContextDir = <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
ModelNamespace </span>=<span style="color: rgba(0, 0, 0, 1)"> myNamespace,
ContextNamespace </span>=<span style="color: rgba(0, 0, 0, 1)"> myNamespace,
ContextName </span>=<span style="color: rgba(0, 0, 0, 1)"> contextName,
UseDataAnnotations </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
UseNullableReferenceTypes </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
};
ModelReverseEngineerOptions modreverOpt </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
UseDatabaseNames </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
NoPluralize </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
};</span></pre>
</div>
<p>1、DatabaseModelFactoryOptions 选项:配置一下我们需要用的表和架构,其实这里可以全 null,毕竟咱们全部生成。</p>
<p>2、ModelCodeGenerationOptions 选项:生成代码相关。</p>
<ol>
<li>Language 属性可以忽略的,默认就是 C#;</li>
<li>ContextDir 属性可以为 dbContext 类指定一个子目录(相对于 outputDir),空字符串表示只放在 outputDir 下,不用单独子目录;</li>
<li>ModelNamespace 属性指定实体类代码的命名空间;</li>
<li>ContextNamespace 属性指定 dbContext 类的命名空间。可以与实体类在同一命名空间;</li>
<li>ContextName 属性指定 dbcontext 类的类名;</li>
<li>UseDataAnnotations 属性表示用不用数据批注来配置模型,false 表示用 ModelBuilder 来配置,重写 DbContext.OnModelCreating 方法;</li>
<li>UseNullableReferenceTypes 属性配置用不用可以为 null 类型,比如 string?、int?;</li>
<li>ConnectionString 属性是连接字符串,不用配置,因为 IReverseEngineerScaffolder.ScaffoldModel 方法的第一个参数就是连接字符串;</li>
<li>ProjectDir 属性是 .NET 项目所在目录,这里不用配置,因为连 DbContext 都没有,这里用不上。</li>
</ol>
<p>3、ModelReverseEngineerOptions 选项:UseDatabaseNames 表示是否用数据库原有的名字,即实体属性等命名与数据库中一样;如果不用,那么会生成 C# 命名风格的名称,如 Name、TbCustomer 等。NoPluralize 表示禁用复数,这个主要是 dbcontext 类中 DbSet 类型属性的命名,如 Students、Customers 等,false 表示不禁用。</p>
<p>下一步就是服务容器配置了。</p>
<div class="cnblogs_code">
<pre>ServiceCollection services = <span style="color: rgba(0, 0, 255, 1)">new</span>();</pre>
</div>
<p>添加设计时基础服务。</p>
<div class="cnblogs_code">
<pre>services.AddEntityFrameworkDesignTimeServices();</pre>
</div>
<p>然后就是获取 IDesignTimeServices 服务了。有两种方法:</p>
<p>第一种方法,我们是知道的,面向 SQL Server 的设计时服务类叫 SqlServerDesignTimeServices。对,直接 new 一下就好,最简单。</p>
<div class="cnblogs_code">
<pre>IDesignTimeServices designtimeSvc = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SqlServerDesignTimeServices();
designtimeSvc.<strong><span style="background-color: rgba(255, 255, 0, 1)">ConfigureDesignTimeServices(services)</span></strong>;</span></pre>
</div>
<p>记得调用 ConfigureDesignTimeServices 方法,否则白干活。SqlServerDesignTimeServices 类虽然声明上是 public,但功能意义上是内部类型,编译器会发出 EF1001 警告。在使用类之前的任意位置禁用这个警告。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">#pragma</span> warning disable EF1001</pre>
</div>
<p>第二种方法是运用反射,代码虽然多一点,但不用禁用 EF1001 警告。</p>
<div class="cnblogs_code">
<pre>Assembly sqlserverProvdAssm = <span style="color: rgba(0, 0, 255, 1); background-color: rgba(255, 255, 0, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">(SqlServerServiceCollectionExtensions).Assembly</span>;
DesignTimeProviderServicesAttribute</span>? attr = sqlserverProvdAssm.<span style="background-color: rgba(255, 255, 0, 1)">GetCustomAttribute<DesignTimeProviderServicesAttribute></span><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (attr == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">这个程序集不对劲!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
Type</span>? designSvcType =<span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)"> sqlserverProvdAssm.GetType(attr.TypeName)</span>;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (designSvcType == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">闹鬼了,居然找不到类型</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建实例</span>
IDesignTimeServices designtimeSvc = (IDesignTimeServices)<span style="background-color: rgba(255, 255, 0, 1)">Activator.CreateInstance(designSvcType)!</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用方法配置服务</span>
designtimeSvc.<span style="background-color: rgba(255, 255, 0, 1)">ConfigureDesignTimeServices(services)</span>;</pre>
</div>
<p>由于我们项目已经引用了 Microsoft.EntityFrameworkCore.SqlServer 包,所以不需要再 Load 程序集了,它已经 Load 了。所以你在这个程序集中随便选个公共类,获取其 Type,就能得到 Assembly 了。我选的是 SqlServerServiceCollectionExtensions 类,是个定义扩展方法的类。</p>
<p>获取到程序集后,拿到 DesignTimeProviderServices 特性。</p>
<div class="cnblogs_code">
<pre>DesignTimeProviderServicesAttribute? attr = sqlserverProvdAssm.GetCustomAttribute<DesignTimeProviderServicesAttribute>();</pre>
</div>
<p>这个特性实例的 TypeName 属性就是 SqlServerDesignTimeServices 类的全名。然后用 Activator.CreateInstance 方法动态创建其实例,赋值给 IDesignTimeServices 接口类型的变量,最后调用 ConfigureDesignTimeServices 方法就行了。</p>
<p> </p>
<p>配置完服务容器后,生成一下服务 Provider。</p>
<div class="cnblogs_code">
<pre>IServiceProvider serviceProvider = services.BuildServiceProvider();</pre>
</div>
<p>接下来,见证奇迹的时候到了。从服务容器中获取 IReverseEngineerScaffolder。</p>
<div class="cnblogs_code">
<pre>IReverseEngineerScaffolder scaffolder = serviceProvider.GetRequiredService<IReverseEngineerScaffolder>();</pre>
</div>
<p>轻松地调用 ScaffoldModel 方法。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> scaffModel =<span style="color: rgba(0, 0, 0, 1)"> scaffolder.ScaffoldModel(
connectionString: connectStr,
databaseOptions: dmfacOpts,
modelOptions: modreverOpt,
codeOptions: modgenOpt
);</span></pre>
</div>
<p>代码已经生成,要保存到文件。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">if</span> (scaffModel != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> res =<span style="color: rgba(0, 0, 0, 1)"> scaffolder.<span style="background-color: rgba(255, 255, 0, 1)">Save(
</span></span> <span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">scaffoldedModel: scaffModel,
</span></span> <span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">outputDir: outputDir,
</span></span> <span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">overwriteFiles: </span></span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">true</span> </span> // 覆盖文件
<span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">)</span>;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (res <span style="color: rgba(0, 0, 255, 1)">is</span> not <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">dbContext路径:{0}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, res.ContextFile);
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">实体路径:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">string</span> f <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> res.AdditionalFiles)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{0}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, f);
}
}
}</span></pre>
</div>
<p>整个 GenModelCodes 方法的代码如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">#if</span> GEN_CODES
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> GenModelCodes()
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接字符串</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> connectStr = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=SomeDB</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出目录</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> outputDir = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">..\\..\\..\\DBModels</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 命名空间</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> myNamespace = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">DB</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 数据库上下文名称</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> contextName = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SomeDbContext</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 准备选项</span>
DatabaseModelFactoryOptions dmfacOpts = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">(
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选择你要的表</span>
tables: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_customers</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_photos</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选择你要的架构</span>
schemas: [<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">dbo</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]
);
ModelCodeGenerationOptions modgenOpt </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Language </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C#</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 语言可以不设置,默认 C#</span>
ContextDir = <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
ModelNamespace </span>=<span style="color: rgba(0, 0, 0, 1)"> myNamespace,
ContextNamespace </span>=<span style="color: rgba(0, 0, 0, 1)"> myNamespace,
ContextName </span>=<span style="color: rgba(0, 0, 0, 1)"> contextName,
UseDataAnnotations </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
UseNullableReferenceTypes </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
};
ModelReverseEngineerOptions modreverOpt </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
UseDatabaseNames </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
NoPluralize </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 服务集合</span>
ServiceCollection services = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1、设计时基础服务</span>
<span style="color: rgba(0, 0, 0, 1)"> services.AddEntityFrameworkDesignTimeServices();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2、数据库提供的设计时服务,它已包含框架基础服务
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 直接实例化</span>
IDesignTimeServices designtimeSvc = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SqlServerDesignTimeServices();
designtimeSvc.ConfigureDesignTimeServices(services);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或使用反射</span>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
Assembly sqlserverProvdAssm = typeof(SqlServerServiceCollectionExtensions).Assembly;
DesignTimeProviderServicesAttribute? attr = sqlserverProvdAssm.GetCustomAttribute<DesignTimeProviderServicesAttribute>();
if (attr == null)
{
Console.WriteLine("这个程序集不对劲!");
return;
}
Type? designSvcType = sqlserverProvdAssm.GetType(attr.TypeName);
if (designSvcType == null)
{
Console.WriteLine("闹鬼了,居然找不到类型");
return;
}
// 创建实例
IDesignTimeServices designtimeSvc = (IDesignTimeServices)Activator.CreateInstance(designSvcType)!;
// 调用方法配置服务
designtimeSvc.ConfigureDesignTimeServices(services);
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构建服务</span>
IServiceProvider serviceProvider =<span style="color: rgba(0, 0, 0, 1)"> services.BuildServiceProvider();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 生成代码</span>
IReverseEngineerScaffolder scaffolder = serviceProvider.GetRequiredService<IReverseEngineerScaffolder><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">var</span> scaffModel =<span style="color: rgba(0, 0, 0, 1)"> scaffolder.ScaffoldModel(
connectionString: connectStr,
databaseOptions: dmfacOpts,
modelOptions: modreverOpt,
codeOptions: modgenOpt
);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 完事后还得保存</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (scaffModel != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> res =<span style="color: rgba(0, 0, 0, 1)"> scaffolder.Save(
scaffoldedModel: scaffModel,
outputDir: outputDir,
overwriteFiles: </span><span style="color: rgba(0, 0, 255, 1)">true</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 覆盖文件</span>
<span style="color: rgba(0, 0, 0, 1)"> );
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (res <span style="color: rgba(0, 0, 255, 1)">is</span> not <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">dbContext路径:{0}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, res.ContextFile);
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">实体路径:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">string</span> f <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> res.AdditionalFiles)
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{0}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, f);
}
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">#endif</span></pre>
</div>
<p>现在,你可以运行一下试试(连接字符串记得改一下,别照抄)。然后你会得到以下宝藏:</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202512/367389-20251207110830799-1540889423.png" alt="image" width="176" height="76" loading="lazy"></p>
<p>看看生成的代码。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Generic;
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> DB;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> tb_customer
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> cust_id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span>? age { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span>? address { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> phone { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span>? email { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span>? remark { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> ICollection<tb_photo> tb_photos { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">new</span> List<tb_photo><span style="color: rgba(0, 0, 0, 1)">();
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">**********************************************************</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Generic;
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> DB;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> tb_photo
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> tags { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">float</span>? dpi { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">double</span> width { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">double</span> height { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> cust_id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> tb_customer cust { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Generic;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.EntityFrameworkCore;
</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> DB;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> SomeDbContext : DbContext
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> SomeDbContext()
{
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> SomeDbContext(DbContextOptions<SomeDbContext><span style="color: rgba(0, 0, 0, 1)"> options)
: </span><span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">(options)
{
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> <span style="background-color: rgba(255, 255, 0, 1)">DbSet<tb_customer> tb_customers { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span></span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> <span style="background-color: rgba(255, 255, 0, 1)">DbSet<tb_photo> tb_photos { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span></span> <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
</span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">#warning</span> To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see </span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://go.microsoft.com/fwlink/?LinkId=723263.</span>
</span> => optionsBuilder.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=SomeDB");
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity</span><tb_customer>(entity =><span style="color: rgba(0, 0, 0, 1)">
{
entity.HasKey(e </span>=> e.cust_id).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_customers</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
entity.Property(e </span>=> e.address).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">);
entity.Property(e </span>=> e.email).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">64</span><span style="color: rgba(0, 0, 0, 1)">);
entity.Property(e </span>=> e.name).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">12</span><span style="color: rgba(0, 0, 0, 1)">);
entity.Property(e </span>=><span style="color: rgba(0, 0, 0, 1)"> e.phone)
.HasMaxLength(</span><span style="color: rgba(128, 0, 128, 1)">11</span><span style="color: rgba(0, 0, 0, 1)">)
.IsUnicode(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
.IsFixedLength();
entity.Property(e </span>=> e.remark).HasColumnType(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ntext</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});
modelBuilder.Entity</span><tb_photo>(entity =><span style="color: rgba(0, 0, 0, 1)">
{
entity.HasKey(e </span>=> e.Id).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_photos</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
entity.Property(e </span>=><span style="color: rgba(0, 0, 0, 1)"> e.dpi).HasDefaultValue(300f);
entity.Property(e </span>=> e.tags).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">);
entity.HasOne(d </span>=> d.cust).WithMany(p =><span style="color: rgba(0, 0, 0, 1)"> p.tb_photos)
.HasForeignKey(d </span>=><span style="color: rgba(0, 0, 0, 1)"> d.cust_id)
.HasConstraintName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FK_photos_custs</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});
OnModelCreatingPartial(modelBuilder);
}
</span><span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreatingPartial(ModelBuilder modelBuilder);
}</span></pre>
</div>
<p>看看 SomeDbContext 类的 tb_customers 和 tb_photos 属性,ModelReverseEngineerOptions 选项类中的 NoPluralize 属性配置的就是这里(属性命名使用复数,当然,如果你生成的是中文名,那无所谓)。</p>
<p>重写 OnConfiguring 方法,配置数据库连接的地方有个警告。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
</span><span style="color: rgba(0, 0, 255, 1)">#warning</span> To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see </span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://go.microsoft.com/fwlink/?LinkId=723263.</span>
=> optionsBuilder.UseSqlServer(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=SomeDB</span><span style="color: rgba(128, 0, 0, 1)">"</span>);</pre>
</div>
<p>意思是提醒你不要把连接字符串硬编码,这个后面咱们可以自己改代码,使用配置文件中的连接字符串。</p>
<p> </p>
<p>好了,今天的话题聊到这儿。下一篇咱们聊 Code First 方案下,用编程方式去生成迁移代码,并迁移数据库。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/19315455
頁:
[1]