作者:梁利锋 标签:DotNet, DbEntry

1685天前 (阅读:2152)
总是有人问,为什么使用 DbEntry.Net 时会遇到 Can not find ObjectHandler 异常,一般我都会给出这个链接地址,后来,索性在异常信息里提供了这个链接,但是还是有人问……那么今天就全面说明一下,先说说解决方案,再说说原因、原理及其它。


解决方案


首先,遇到 Can not find ObjectHandler 异常,说明你使用的是 v4.0+,而且你没有安装 DbEntry.Net,而是直接引用了 DLL,这种方式在 v3.9 及以前版本是可以的,不过,在 v4.0+ 中,由于架构改变,必须安装 DbEntry.Net,而且使用其安装的 VS 模板 DbEntryClassLibrary 4.2 创建的类库项目,才可以正常编译运行,否则,必然遇到 Can not find ObjectHandler 异常。

有些杀毒软件会认为这个安装包有病毒,这一点可以放心,只要是从官网下载的安装包,就不会有病毒,至于为什么会报毒,后面会解释。

对于 Vista/Win7/Win8 的用户而言,安装 DbEntry.Net 后,DbEntryClassLibrary 4.2 的模板默认是关闭状态,需要先在 VS 的菜单 Tools -> Extention Manager 窗口中打开,才能在新建窗口中找到它。

如果以前安装过 DbEntry.Net v4.0+,在安装新版本时,要先移除老版本,特别的,对于 VS 模板,卸载程序无法自动处理,只能用户手工删除,同样的,也是在 VS 的菜单 Tools -> Extention Manager 窗口中操作。

在安装目录中,有一个 DbEntryClassLibrary.vsix 就是 VS 模板的安装文件,如果安装程序没有正确安装此模板,或者你不小心删除了它,都可以手动执行这个文件,进行安装。vsix 文件本质上是一个 zip 文件,所以如果你想看看这个安装包里究竟是什么,可以用任何可以打开 zip 文件的工具打开它。

最后,DbEntry.Net 只需要安装在开发机上,部署机上无需安装,只要你引用到的 dll 都复制过去就可以了。(比如,Mono.Cecil.dll 就不用复制到部署机)


为什么 v4.0+ 改变了架构


对于大多数希望马上开始使用 DbEntry.Net 的用户,知道上面的解决方案就可以了,下面要说的是,为什么在 DbEntry.Net 4.0+ 改用了安装文件的方式。

在 v3.9 及以前的版本中,DbEntry.Net 使用 Emit 的方式生成 Model 的子类,并且实际操作的都是这个子类。

而在 v4.0+ 中,改用了 Mono.Cecil 直接修改 Model 类,实际操作的也就是你写的 Model 类。

好处是什么呢?

首先,Model 的属性,不再需要写成 abstract 的;

然后,新建 Model,不再需要 Model.New() 的方式,而可以写成 new Model(),对于手写的代码,这两种方式其实差别不大,只是习惯问题而已,但是,有很多框架,会在其内部 new Model,这时,新的方式就可以完美的和这些框架合作,而不需要 hack 进那些框架,只为了修改新建 Model 的代码;

再者,因为没有运行时动态生成代码,所以对于某些因为权限问题,不允许动态生成代码的服务器,就不需要特殊处理了。(在 v3.9 里,是单独提供了一个命令行程序,把动态生成的代码写入一个 dll 里)

另外,因为在编译时就进行了 Model 的检查,所以很多以前在运行时才会遇到的异常,现在在编译时就能发现;(举例来说,HasOne/HasMany和BelongsTo必须成对出现,现在在编译期就能发现)

还有就是,因为动态生成这个步骤,在编译时就做完了,所以,运行代码时,会使程序启动速度快一些;

而且,因为针对每一个 Model 生成了其独有的 ObjectHandler,来代替以前代码里的反射调用,所以,运行速度也有所提高。(ObjectHandler 在 v3.9- 里就有,v4.0+ 对它进行了增强)


Mono.Cecil 是什么


Mono.Cecil 是 Mono 下的一个开源、免费的工具库,使用这个库,可以对已存在的程序集进行读取,改动,再重新写入等操作,而且支持 snk 签名。当然,操作的是 IL 语言。

有一些病毒、木马,也使用了 Mono.Cecil 对目标用户的电脑上已存在的.Net程序集进行改写,来达到传播以及隐藏自身的目的。而一些懒惰,或者说宣称广谱的杀毒软件,就很简单粗暴的,只要发现要安装的文件中含有 Mono.Cecil 就直接报毒,这也就是有些用户的杀毒软件说 DbEntry 的安装包有毒的原因。

Mono.Cecil 有一个很宽松的许可协议,宽松到基本上无论怎么做都可以,具体的协议文件,在 DbEntry 的 docs\English\cecil_LICENSE.txt 中可以看到。

事实上,DbEntry 中使用的 Mono.Cecil 并不完全是官方版本,因为在使用中发现,至少我用的那个版本,在一些细节上是有 bug 的,所以做了一些小改动修正了。当然,这种改动在 Mono.Cecil 的许可协议中也是允许的。

既然是 Mono.Cecil,当然也是支持 Mono,进而也是支持 Linux 的,只是目前,DbEntry 没有提供 Linux/MonoDevelop 的安装包。


使用 Mono.Cecil 为什么就需要安装程序


这是因为,DbEntry.Net 需要在 VS 编译后,马上执行 Leafing.Processor.exe 对新生成的 Assembly 进行处理,处理后的程序集才能正常运行,当然,用户也可以自己手工执行这个命令行程序,只是会很麻烦,而且容易忘记。所以,DbEntry.Net 在安装时,设置了一个环境变量 DbEntryPath 指向 DbEntry 的安装路径,而 VS 的模板,就是普通的 ClassLibrary 模板做了一些修改,其中最重要的是,对于 *.csproj 文件,在其最后,添加了如下一段:

  <UsingTask TaskName="ProcessorTask" AssemblyFile="$(DbEntryPath)\Leafing.MSBuild.dll" />
  <Target Name="AfterCompile">
    <ProcessorTask KeyFile="$(KeyOriginatorFile)" ProjectDir="$(ProjectDir)" AssemblyName="@(IntermediateAssembly)" ReferenceFiles="@(ReferencePath)" />
  </Target>


Leafing.MSBuild.dll 是 MSBuild 扩展,加上这一段,就会在编译完成后,调用 Leafing.MSBuild.dll,进而调用 Leafing.Processor.exe 对程序集进行处理,以完成编译,它可以自动判断程序集是否使用了 snk 进行签名,如果签名了的话,会自动调用原 snk 文件,对于改写后的程序集进行签名。

大家应该注意到了,Leafing.MSBuild.dll 前面是路径 $(DbEntryPath),就是安装时设置的那个路径的环境变量。所以,如果你希望使用多个版本的 DbEntry,比如总是使用最新源代码编译的 DbEntry,其实是可以自己修改这里,指向特定的 Leafing.MSBuild.dll 的路径的。对于我来说,一般在 *.sln 所在目录,再建立一个 Imports 目录,把新编译出的文件复制到里面,而 Models 所在的 csproj 相关部分改成如下:

  <UsingTask TaskName="ProcessorTask" AssemblyFile="$(SolutionDir)\Imports\Leafing.MSBuild.dll" />
  <Target Name="AfterCompile">
    <ProcessorTask KeyFile="$(KeyOriginatorFile)" ProjectDir="$(ProjectDir)" AssemblyName="@(IntermediateAssembly)" ReferenceFiles="@(ReferencePath)" />
  </Target>


这样,这个项目,就是用 Imports 里的 DbEntry 版本进行编译的了。

当然,要注意,所有包含 Model 的项目,都要增加上面的配置。对于 DbEntry 安装的 VS 模板来说,建立的是 DLL 项目,其实,Leafing.MSBuild.dll 和 Leafing.Processor.exe 也是可以对 EXE 文件进行处理的,只要在 csproj 中增加相应的配置即可。

使用 VS 模板,对我来说是一个选择,另外一个选择是类似 PostSharp 那样,修改 machine 配置的方式,那样,所有通过 MSBuild 编译的项目,都会被 Leafing.Processor.exe 处理,这样就不需要对于 csproj 文件进行任何修改,只是我觉得,让 DbEntry 处理所有项目,而不管其是否使用了 DbEntry,有点儿浪费资源,而且也就无法在不同的项目使用不同的 DbEntry 版本了。所以,最终选择的是 VS 模板。

对于使用最新源代码编译的 DbEntry 的用户来说,能使用不同版本的 DbEntry 其实是挺重要的,DbEntry 不同版本间的改动,有时候会造成原来运行能正常编译的项目,需要改动后才能正常编译,虽然改动一般不大,但是也许会一地鸡毛。所以,对于以前的项目来说,在不在意新特性,也没有遇到新版本修正的bug时,也许使用老版本的 DbEntry 是比较理智的选择;而新项目,使用新版本更好,除了新特性,新版本还包括Bug修正,当然更合适。

对于大多数只想尽快使用 DbEntry,而对于使用最新源代码编译版不感冒的用户,当然一个安装程序是最简单,而且也是最不容易出现歧义的。


Leafing.Processor.exe 具体做了什么?


Leafing.Processor.exe 里的代码,对于不熟悉 IL 的人来说,是非常晦涩的,那么,它具体都做了什么呢?

下面就用反编译 Leafing.Extra 中的 LeafingLog 为例,给大家看看吧。

首先是对于 Model 的修改:



再看看传说中的 ObjectHandler 长什么样:



当然,这两个截图只是展示了一小部分代码,有兴趣的话,可以自行反编译看看,比 Leafing.Processor 的代码容易理解的多 :D


怎么编译 DbEntry 最新源代码


在编译 DbEntry 最新源代码之前,首先要确认一下,在 src 目录的父目录下,是否有一个 bin 目录,如果没有,手动建立一个。

DbEntry 的源代码,包含两个解决方案,MSBuild.sln 和 DbEntry.Net.sln。

其中 MSBuild.sln 是用来编译 Leafing.MSBuild.dll 的,这个需要先编译,不过编译过程没什么可说的,建议直接把它的 Debug 和 Release 都编译出来。因为在 Leafing.MSBuild 里,其实对于 Debug 和 Release 是有不同的处理的。

而 DbEntry.Net.sln 就是 DbEntry 的主体,有点儿绕的是,这个解决方案,不止包含 Leafing.Core 和 Leafing.Data,而且包含 Leafing.Processor 和 Leafing.Extra、Leafing.UnitTest,而 Leafing.Processor 需要引用 Leafing.Data,上面也说过,Leafing.Extra 是需要使用 Leafing.Processor 处理后,才能正常运行的。另外,Leafing.Processor 处理 Leafing.Extra 是需要 Leafing.MSBuild 启动的,而上面说了,Leafing.MSBuild 在 Debug 和 Release 下的方式是特殊处理的,而 VS 加载 Leafing.MSBuild.dll 后无法自动改成另一个版本,所以,如果从 Debug 改成 Release(或者相反),改了配置后,不要马上编译,需要完全退出 VS,再重新启动 VS,打开 DbEntry.Net.sln,然后才能正确编译。

Release 版本编译后,会自动把程序集复制到上面说的那个 bin 目录,这里面就是大家需要复制到 Imports 目录的所有文件了。

This posntig knocked my socks off(非注册用户) 2015-1-18 18:49:37

This posntig knocked my socks off