我需要在至少两个项目中按IIncrementalGenerator
创建代码,这些项目引用引用SourceCodeGenerator
项目的同一库。
我的解决方案,进一步澄清:
MySolution
|->DesktopApp
| |->ref:Library
|
|->Library
| |->ref:SourceGenerator
|
|->WebApi
| |->ref:Library
|
|->SourceGenerator
在库中,我为我在应用程序中使用的所有对象定义了接口,我在接口中定义的许多属性都是 Id,我通过一个属性标记它们,我在其中领导底层对象的IType
。
属性:
[System.AttributeUsage(System.AttributeTargets.Property)]
public class NavigationPropertyAttribute : System.Attribute
{
public System.Type NavigationPropertyType { get; }
public NavigationPropertyAttribute(System.Type type) => NavigationPropertyType = type;
}
这就是这样的界面的样子:
public interface IFoo
{
int Id { get; set; }
[NavigationProperty(typeof(IBar))]
int BarId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ParentId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ChildId { get; set; }
}
在这两个项目中,一个是桌面应用程序,另一个是使用 EF Core 的 Web API,我有实现这些接口的部分类。
namespace IGTryout.Main;
public partial class Foo : IFoo
{
public int Id { get; set; }
public int BarId { get; set; }
public int ParentId { get; set; }
public int ChildId { get; set; }
}
我现在需要的,也是我正在努力解决的地方,是使用IIncrementalGenerator
创建一个分部类,其中包含基于Id
、Name
和NavigationPropertyAttribute
Type
的属性。
public partial class Foo
{
private Bar bar;
public Bar Bar => bar ??= GetValue<Bar>();
private Foo parent;
public Foo Parent => parent ??= GetValue<Foo>();
private Foo child;
public Foo Child => child ??= GetValue<Foo>();
}
(GetValue<T>()
是一个扩展,它获取[CallerMemberName]
,通过反射解析匹配Id
,并通过类型和 id 解析从缓存中返回对象)
在 Web API 项目中,我也在实现接口,但以通用方式创建NavigationProperties
Foo { get; set; }
为了满足 EF Core 的需求,我还在相关对象中创建InversePropertyCollection
,以便 EF Core 根据需要设置外键。
所有这些都已完成,因此我可以尽可能TransportObject
地使用这些接口,在将它们从 Web API 发送到我的桌面应用程序并反向发送时,开销尽可能少。
现在到我遇到的问题:
为了触发对接口所做的所有更改,我从库项目中引用了我的SourceCodeGenerator
项目,但这样做,我找不到任何机会在 Web API 程序集或桌面应用程序程序集中创建代码。
我之前确实通过使用通用SourceGenerator
并通过桌面应用程序和 Web API 引用SourceCodeGenerator
项目来解决此问题,并通过调用我的编译ReferencedAssemblySymbols
SourceModule
并迭代到相关项目来访问我的库项目中的接口,因为它被两者引用, 但是这个相当笨拙的解决方案是不可能的,因为会失去所有的可能性,这导致我首先切换到IIncrementalGenerator
。
下面是为什么这不适用于类库项目的答案。 然而,在捆绑这个答案时,我了解到共享项目的存在,它基于对它们如何工作的粗略观察,可能会启用增量生成器在共享源代码上的行为,请参阅下面的想法。
问题所在
问题中提出的内容不适用于类库项目有两个不同的原因。一是使用源生成器的方式不是为工作而设计的,二是即使它们按你想要的方式工作,生成的代码也不会按预期运行。
源生成器挂钩到编译
根据设计,源生成器挂接到编译。项目的目的是提供一个独特的编译单元。使用解决方案对项目进行分组和对项目进行排序是一种便利,但不会导致单个项目合并到一个编译中。一旦一个类库项目对下游项目可见,它就不再是一堆源代码,它只是一个dll。
不同项目中的分部类
该示例是在三个不同的项目中使用部分创建一个分部类,因此即使您让源生成器在正确的项目中制作文件,您最终也不会得到您想要的,这是 WebApp 版本的Foo
和桌面版本的Foo
。相反,您最终会得到 3 个版本的Foo
库版本,具有 Id 属性的库版本、具有导航自动属性的 WebApp 版本和带有GetValue
调用的桌面版本。桌面和 Web 应用程序版本都缺少 ID 属性。查看此问题/答案
共享项目
共享项目可以在这里提供帮助吗?或。共享项目的目的是共享源代码,而不是编译单元,因此对源代码的更改应该在引用该共享源代码的任何项目中触发增量生成器,因为该源代码现在是该项目编译的一部分。 您需要了解类库和共享项目之间的差异,以及它们如何影响您的程序,因为现在代码在不同的上下文中被多次编译,因此您实际上最终可以从同一源获得完全不同的功能。(即,像全局使用这样的功能可能会导致命名空间解析导致 Type 按名称与完全不同的程序集中的 Type 匹配,例如,您有一个属性Document {get;set;}
在一个项目中解析为AutoCAD.Document
,但在另一个项目中解析为Microsoft.CodeAnalysis.Document
。