• EFCore Database-first深入研究


    EFCore Database-first深入研究

    使用Scaffold-DbContext从数据库生成实体

    说明文档:

    关于 Scaffold-DbContext 微软有官方说明文档

    https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/cli/powershell

    实践:

    不妨自己找一个现有的数据库试一试:

    LocalDB

    Scaffold-DbContext "Server=(localdb)mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables "Blog","Post" -ContextDir Context -Context BlogContext
    

    MySql

    Scaffold-DbContext "server=127.0.0.1;Port=3306;database=zejedb;uid=root;pwd=123456;Convert Zero Datetime=True;Allow Zero Datetime=True;Charset=utf8;Default Command Timeout =30000;" Pomelo.EntityFrameworkCore.Mysql -OutputDir Models -DataAnnotations
    

    路漫漫

    但是通过上述步骤生成的实体,跟自己想要的还是有那么一丢丢差距。

    • 比如我想通过数据库表注释、字段注释,给实体、属性加上注释貌似就没办法实现。
    • 比如我想统一继承基类,将创建时间、修改时间、创建人、修改人、Id放到基类,貌似也没办法实现。

    作为一个被抛弃的10年neter,对于自己未曾研究过源码耿耿于怀(其实n年前我研究过jquery的部分源码,那不算)。因此我毅然下载了EFCore的源码。

    源码地址:https://github.com/dotnet/efcore.git

    研究EFCore的源码

    以Scaffold-DbContext作为突破口:

    • 后来又看到这么几个类:
      • DatabaseModelFactory
      • SqlServerDatabaseModelFactory
      • DatabaseModel
    • 还下载了Pomelo.EntityFrameworkCore.MySql的源码
    • 等等

    皇天不负有心人:用了一个周末的时间,关于如何添加注释,我找到了最关键地方

    CSharpEntityTypeGenerator

    只要重写(Override)此类的GenerateClass,GenerateProperties两个虚拟方法便可

    吐槽&继续(痛苦并快乐着)

    微软的攻城狮细节怎么就不处理好呢?

        public class CSharpEntityTypeGenerator : ICSharpEntityTypeGenerator
        {
            private readonly ICSharpHelper _code;
    
            private IndentedStringBuilder _sb = null!;
            private bool _useDataAnnotations;
    

    关键方法:

        public virtual string WriteCode(IEntityType entityType, string @namespace, bool useDataAnnotations)
            {
                Check.NotNull(entityType, nameof(entityType));
                Check.NotNull(@namespace, nameof(@namespace));
    
                _sb = new IndentedStringBuilder();
                _useDataAnnotations = useDataAnnotations;
    
                _sb.AppendLine("using System;");
                _sb.AppendLine("using System.Collections.Generic;");
    
                if (_useDataAnnotations)
                {
                    _sb.AppendLine("using System.ComponentModel.DataAnnotations;");
                    _sb.AppendLine("using System.ComponentModel.DataAnnotations.Schema;");
                }
    
                foreach (var ns in entityType.GetProperties()
                    .SelectMany(p => p.ClrType.GetNamespaces())
                    .Where(ns => ns != "System" && ns != "System.Collections.Generic")
                    .Distinct()
                    .OrderBy(x => x, new NamespaceComparer()))
                {
                    _sb.AppendLine($"using {ns};");
                }
    
                _sb.AppendLine();
                _sb.AppendLine($"namespace {@namespace}");
                _sb.AppendLine("{");
    
                using (_sb.Indent())
                {
                    GenerateClass(entityType);
                }
    
                _sb.AppendLine("}");
    
                return _sb.ToString();
            }
    

    生成实体全然靠WriteCode方法,为什么sb不公开?(后来才知道,他们合并代码有这么一个校验:Public_inheritable_apis_should_be_virtual)
    无奈之下,我把整个类都拷贝出来,稍微修改,添加上关键代码

    • 类注释:
    var comment = entityType.FindAnnotation("Relational:Comment");
    if (comment != null && comment.Value != null)
    {
        _sb.AppendLine("///<summary>");
        _sb.AppendLine("///" + comment.Value.ToString());
        _sb.AppendLine("///</summary>");
    }
    
    • 实体注释:
    var comment = property.FindAnnotation("Relational:Comment");
    if (comment != null && comment.Value != null)
    {
        _sb.AppendLine("///<summary>");
        _sb.AppendLine("///" + comment.Value.ToString());
        _sb.AppendLine("///</summary>");
    }
    
    • 测试结果:
    总结

    生成实体的工具那么多,何必纠缠Scaffold-DbContext不放手?
    其实:
    1、个人有代码洁癖。
    2、有代码注释强迫症。
    3、给自己找个强迫自己看源码的理由。

    其实实现自动生成代码并带上注释的,个人已经有实现的办法,但是不完美,有些地方还是需要手动修改。纠缠Scaffold-DbContext是另外一种尝试罢了。

    放弃Scaffold-DbContext,使用T4模板

    为何放弃

    难以自定义

    • 无法带注释(上述几行代码已在github上推送了),不过Scaffold-DbContext要支持注释估计是猴年马月的事情了。
    • 不知道怎么继承基类等

    PackageReferences在T4模板下的水土不服

    使用CopyLocalLockFileAssemblies,将dll复制到bin目录下

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
      </PropertyGroup>
      <ItemGroup>
      </ItemGroup>
    </Project>
    

    T4模板中引用DLL

    <#@ assembly name="$(TargetDir)Microsoft.EntityFrameworkCore.dll" #>
    <#@ assembly name="$(TargetDir)Microsoft.EntityFrameworkCore.Design.dll" #>
    

    其实本人之前是使用自己写的Zeje.T4_.dll来生成的,现在计划基于Microsoft.EntityFrameworkCore.dll及其扩展类库进行研究,下个周末继续研究。

    作者:哲智(Zeje)
    出处:http://www.cnblogs.com/zeje/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    UE4智能指针:TUniquePtr
    浅析UE4垃圾回收
    UE4中资源的引用
    ELF文件基础
    【JVM】JVM和Java 体系架构
    【Java多线程】Java线程生命周期、线程同步、线程通信(二)
    【Java多线程】Java多线程基础(一)
    【Java面试题】MySQL索引底层为什么用到B+树
    【算法】B树、B+树详解
    【Kafka】 Kafka的简介与架构(三)
  • 原文地址:https://www.cnblogs.com/zeje/p/12184554.html
Copyright © 2020-2023  润新知