实体解析是工具的最重要的功能之一,它可以解析用户的实体,提取出实体的属性和关系。本篇文档将详细说明实体解析相关的内容,你将了解
EntityFrameworkCore.Design
解析DbContext和实体EntityInfo
实体解析使用了Roslyn来解析C#代码中的实体类。通过分析代码中的类定义、属性和方法,工具可以提取出实体的结构信息。
主要的解析逻辑在EntityParseHelper
中实现.
EntityFrameworkCore.Design
解析DbContext和实体这种方案在实现上稍微有些复杂,但理论上能够获取关于数据库上下文和实体的各种信息,以增加后续代码生成的智能化。
具体步骤:
EntityFramework
。DbContext
的基类,如ContextBase
。ContextBase
的非abstract的数据库上下文。DbContext
的Model
属性获取实体模型,返回一个字典。涉及到的实现类
DbContextAnalysisHelper.cs
,使用Roslyn解析目标程序集,获取DbContext信息DbContextAnalyzer.cs
,创建DbContext
实例,提供FrozenDictionary<string, IModel>
类型的字典DbContextParseHelper
,根据字典来获取对应实体的信息,提供EntityInfo
。这里的核心是如何创建目标程序集的DbContext
实例,我们既没有通过启用Host,从而获取依赖注入的服务,也没有使用IDesignTimeDbContextFactory
(目标程序可能不提供,提供会影响ef工具行为),而是直接使用Activator.CreateInstance
来创建实例。
这里的核心在于如何构建DbContext
所需要的参数DbContextOptionsBuilder
。可以直接使用工具中的Sqlite相关程序集,来构建该参数,
我们的目的是获取实体模型信息,并不需要使用跟目标程序一致的数据库提供程序。你会看到以下代码:
// use tool's sqlite assembly var sqliteAssembly = Assembly.Load("Microsoft.EntityFrameworkCore.Sqlite"); var sqliteExtensionsType = sqliteAssembly.GetType( "Microsoft.EntityFrameworkCore.SqliteDbContextOptionsBuilderExtensions" ); if (sqliteExtensionsType != null) { var useSqliteMethod = sqliteExtensionsType.GetMethod( "UseSqlite", [optionsBuilderType, typeof(string), typeof(Action<object>)] ); useSqliteMethod?.Invoke(null, [optionsBuilder, "DataSource=temp", null]); } var options = optionsBuilder.Options; dbContextInstance = Activator.CreateInstance(contextType, options) as DbContext;
使用Roslyn
进行语义和语法解析,是对源代码内容进行解析,能够获取到用户代码的原始表达。
使用EntityFrameworkCore.Design
解析DbContext
和实体,本质上是对编译后的程序集进行反射,获取到编译后的实体信息,但一些原始的代码信息可能无法获取,如:
public UserType UserType { get; set; } = UserType.Normal;
其中的= UserType.Normal;
无法通过反射获取到。
编译会去除一些内容,如注释,条件编译等信息。
通过结合Roslyn
和EntityFrameworkCore.Design
,我们可以获取到更全面的实体信息。
EntityInfo
是实体解析后的数据结构,它包含了实体的各类信息,如以下所示:
public string? ModuleName { get; set; } /// <summary> /// the db context name /// </summary> public string? DbContextName { get; set; } public string? DbContextSpaceName { get; set; } /// <summary> /// 类名 /// </summary> [MaxLength(100)] public required string Name { get; set; } /// <summary> /// 命名空间 /// </summary> [MaxLength(100)] public required string NamespaceName { get; set; } /// <summary> /// 程序集名称 /// </summary> [MaxLength(100)] public string? AssemblyName { get; set; } /// <summary> /// 类注释 /// </summary> [MaxLength(300)] public string? Comment { get; set; } /// <summary> /// 类注释 /// </summary> [MaxLength(100)] public string? Summary { get; set; } public List<PropertyInfo> PropertyInfos { get; set; } = []; /// <summary> /// 导航属性 /// </summary> public List<EntityNavigation> Navigations { get; set; } = [];
其中PropertyInfo
是实体包含的属性信息,EntityNavigation
是实体的导航属性信息。
PropertyInfo
是实体属性的详细信息,主要提供代码生成时的各类无数据。
/// <summary> /// 类型 /// </summary> [MaxLength(100)] public required string Type { get; set; } /// <summary> /// 名称 /// </summary> [MaxLength(100)] public required string Name { get; set; } [MaxLength(100)] public string? DisplayName { get; set; } /// <summary> /// 是否是数组 /// </summary> public bool IsList { get; set; } public bool IsPublic { get; set; } = true; public bool IsForeignKey { get; set; } /// <summary> /// 是否为导航属性 /// </summary> public bool IsNavigation { get; set; } public bool IsJsonIgnore { get; set; } /// <summary> /// 导航属性类名称 /// </summary> [MaxLength(100)] public string? NavigationName { get; set; } public bool IsComplexType { get; set; } /// <summary> /// 导航属性的对应关系 /// </summary> public bool? HasMany { get; set; } public bool IsEnum { get; set; } /// <summary> /// 是否包括set方法 /// </summary> public bool HasSet { get; set; } = true; [MaxLength(100)] public string? AttributeText { get; set; } /// <summary> /// xml comment /// </summary> [MaxLength(500)] public string? CommentXml { get; set; } /// <summary> /// comment summary /// </summary> [MaxLength(200)] public string? CommentSummary { get; set; } /// <summary> /// 是否必须 /// </summary> public bool IsRequired { get; set; } /// <summary> /// 可空? /// </summary> public bool IsNullable { get; set; } public int? MinLength { get; set; } public int? MaxLength { get; set; } public bool IsDecimal { get; set; } /// <summary> /// 默认值 /// </summary> [MaxLength(100)] public string DefaultValue { get; set; } = string.Empty; public bool IsShadow { get; set; } public bool IsIndex { get; set; }
EntityNavigation
是实体的导航属性信息,主要用于描述实体之间的关系。
public required string ForeignKey { get; set; } public required string Name { get; set; } public required string Type { get; set; } public bool IsCollection { get; set; } public string? Summary { get; set; } public bool IsRequired { get; set; } public bool IsUnique { get; set; } public bool IsSkipNavigation { get; set; } public bool IsOwnership { get; set; } public List<PropertyInfo> ForeignKeyProperties { get; set; } = []; public EntityInfo? EntityInfo { get; set; }