Natasha入门(一)
什么是Natasha
Natasha是基于Roslyn 的动态程序构架库,说的直白一点就是将一个或多个cs文件进行动态编译并放入到正在运行的程序中去。例如我们有一个设备库,设备库中的设备会不定期的更新,那我们只需要将平台(展示和调用)完成,每次添加的时候使用Natasha生成设备类别和设备的调用代码,由平台统一调用。这样子就避免了每次调用都要重新生成和更新,每个设备模块都是单独的个体,个体出现bug不影响整个平台的正常工作。
Natasha使用场景
- 插件管理,开发一个平台,里面有各式各样的插件,插件可通过引用等方式动态添加
- 在线编码平台,在页面中输入代码,系统根据代码自动生成结果并展示
- 代码生成器,通过编写Entity或读取数据源中的数据来生成代码块,并通过Natasha动态编译到系统中
- 低代码平台
Natasha简单例子
-
下载最新版 Natasha源码 ,目前最新版本为v5.1.0.0
-
打开下载好的源码,编译src->CSharp->Natasha.CSharp项目,会生成如下两个dll
- Natasha.CSharp.dll
- Natasha.Domain.dll
-
创建一个空项目NatashaStudyApplication,然后再创建一个控制台项目NatashaStudyConsole,框架选择了.Net7。
-
需要额外使用NuGet添加三个引用
-
添加引用Microsoft.Extensions.DependencyModel,否则初始化时(NatashaInitializer.Preheating方法)会报错。
-
添加引用Microsoft.CodeAnalysis.CSharp,否则创建编译单元时会报错。
-
如果使用官网给出的例子,oop.Add方法会提示错误,需要额外安装Microsoft.CodeAnalysis.Common包。
-
-
在NatashaStudyConsols项目中创建dlls文件夹,将生成的Natasha.CSharp.dll和Natasha.Domain.dll都赋值到dlls文件夹中,为了防止丢失文件,我全部复制过来了
-
选择依赖项,点击右键添加项目引用,选择浏览,找到本项目的dlls文件夹,然后把两个dll都进行引入,引入后如图所示:
-
向Program中的Main中添加代码,官方给出的为声明一个class类,在使用该类时发现回报如下错误:
Predefined type 'System.Object' is not defined or imported
'object' does not contain a constructor that takes 0 arguments
该错误的意思是程序没有声明System.Object
-
方案一:基于Microsoft.Extensions.DependencyModel包,在PropertyGroup标签中添加
true -
方案二:添加System.Object(System.Runtime.dll),找到System.Runtime.dll,然后对其进行引用MetadataReference.CreateFromFile("/dlls/System.Runtime.dll")
-
-
本次的例子是基于官网的进行了补充
//得到的结果 string result = ""; //初始化 Natasha 编译组件及环境 NatashaInitializer.Preheating(); //创建编译单元,并指定程序集名 AssemblyCSharpBuilder oop = new AssemblyCSharpBuilder("myAssembly"); //编译单元使用从域管理分配出来的随机域 oop.Domain = DomainManagement.Random(); //增加代码到编译单元中 oop.Add(@"namespace HelloWorld{ public class Test{ public Test(){ Name = null; } public string Name; public void setName(string name){Name=name;} public string getName(){ return ""你好!""+ Name; } } }"); //获得Assembly var assembly = oop.GetAssembly(); // 利用反射实例化 var newInstance = assembly.CreateInstance("HelloWorld.Test"); //判断实例化是否成功 if (newInstance != null) { //获得setName方法 MethodInfo? setNamehod = newInstance.GetType().GetMethod("setName"); if (setNamehod != null) { //利用反射进行赋值 setNamehod.Invoke(newInstance, new object[] { "张三" }); } //获得getName方法 MethodInfo? getNameMethod = newInstance.GetType().GetMethod("getName"); if (getNameMethod != null) { // 获得getName的返回值 result = getNameMethod.Invoke(newInstance, null) as string; } } //打印 Console.WriteLine($"{result}");
-
执行结果结果如下
-
第二个例子使用官方例子
//在 NDomain1 域内创建一个委托 var func = NDelegate.CreateDomain("NDomain1").Func<string>("return \"Hello World!\";"); result = func(); func.DisposeDomain(); //打印 Console.WriteLine($"{result}");
-
执行结果结果如下