sdmap:比EF可控,比 Mybatis轻量, 比拼接sql友好的ORM/Sql管理方案

-- sdmap:比EF可控,比 Mybatis轻量, 比拼接sql友好的ORM/Sql管理方案
【官网】:https://github.com/sdcb/sdmap

应用场景

通常情况下,.net开发场景下,涉及sql时,如果使用ef框架,则有些sql最终生成什么样可能和预期不一样。 而mybatis,ibatis这些框架要么很久没有维护,要么只支持.net framework 而不支持.net core, 要么xml语法相对比较臃肿。 另外直接在代码中拼接sql字符串又显得很难维护。 涉及改动之类的时候容易出错。

基础资源

.net core/.net framework,   sdmap.dll,sdmap.ext.dll,antlr4.dll

使用须知

注意部署环境,注意.sdmap的资源文件的属性(内嵌),否则将无法随程序发布。

配置步骤

【概述: 为什么推荐使用sdmap来管理项目中的sql?】


  • 非常简单的语法来描述动态SQL;
  • 使用了Emit CIL来确保性能;
  • 有Visual Studio插件支持,实现了代码高亮、代码折叠、快速导航的特性;
  • 支持所有主流数据库,如MySQL、SQL Server、SQLite等(只要Dapper能支持);
  • 可以扩展支持非关系型数据库,如Neo4j
  • 同时支持.net framework,  .net core.  //这一点比mybatis,ibatis都要好

【使用示例】

<step1: 在.sdmap文件中配置一个简单的sql>


<step2: 在代码中调用上述sql并使用ORM自动映射为对象>


【注】上述 _db就是 IDbConnection对象的实现,可以是MySqlConnection之类的。


常见问题

  • sdmap中的sql明明正常,但提示sql某处错误
    【解决方案】sdmap官方存在一个漏洞,它使用不当会加载多个程序集目录下sdmap资源,如果namespace和sql名相同则会覆盖
  • 部分sdmap的sql正常,但另外一些提示对象为null异常
    【解决方案】排查where条件中字段名是否存在,不存在会报null错误,但不会给出具体描述
  • 编译发布后sdmap文件丢失了
    【解决方案】需要把sdmap文件设置为内嵌资源

快速入门

A)使用sdmap的步骤。

step1)通过nuget安装依赖包:sdmap.ext.Dapper

通常只需安装sdmap.ext.Dapper,因为它将自动安装所有依赖项,包括sdmap和sdmap.ext。

step2) 在项目中的某个目录,创建一个空文本文件,并将其重命名为.sdmap

在项目属性窗口中,将文件“生成操作”从“无”设置为“嵌入资源”。


step3)初始化sdmap目录(该程序表示sdmap所在的程序集):
SetEmbeddedSqlAssembly(typeof(Program).Assembly);
注:
在执行任何数据库操作之前,必须先运行此代码。
此代码只需要在每个进程中执行一次。
初始化sdmap还有许多其他方法,您应该根据需要选择其中一种:
SetSqlDirectory-从磁盘上的物理文件夹初始化
SetSqlDirectoryAndWatch-从磁盘上的物理文件夹初始化,并在编辑这些文件夹sdmap文件时监视更改
SetEmbeddedSqlAssembly-从单个程序集初始化,sdmap自动解析以.sdmap结尾的所有资源文件

SetEmbeddedSqlAssemblys-从多个程序集初始化,sdmap自动解析以.sdmap结尾的所有资源文件

高级,您可以编写自己的ISDmapEmitter。


step4) 将一些SQL语句写入sdmap文件,最小sdmap文件可能如下所示:
sql GetUserById

{
    SELECT * FROM [User] WHERE Id = @Id
}
注意:名称空间NS{…}不是必须的。

注: 如果要手动发出SQL语句,可能需要调用:

string finalSqlToExecute=DbConnectionExtensions.EmitSql(sqlMapId,parameterObject);




B)sdmap使用过程的关键类。 

b1)ISdmapEmiter的实现.


using sdmap.Compiler;
using sdmap.ext;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ConfigLab.App.DbContexts
{
	public class ConfigLabAppSqlEmiter : ISdmapEmiter
	{
		private SdmapCompiler _compiler = (SdmapCompiler)(object)new SdmapCompiler();

		public string Emit(string statementId, object parameters)
		{
			return GetCommandText(statementId, parameters);
		}

		public void AddAssembly(Assembly assembly)
		{
			foreach (string item in from x in assembly.GetManifestResourceNames()
				where x.EndsWith(".sdmap")
				select x)
			{
				using (StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream(item)))
				{
					_compiler.AddSourceCode(streamReader.ReadToEnd());
				}
			}
		}

		public static ConfigLabAppSqlEmiter CreateFrom(params Assembly[] assemblies)
		{
			ConfigLabAppSqlEmiter configLabAppSqlEmiter = new ConfigLabAppSqlEmiter();
			foreach (Assembly assembly in assemblies)
			{
				configLabAppSqlEmiter.AddAssembly(assembly);
			}
			return configLabAppSqlEmiter;
		}

		private string GetCommandText(string statementId, object parameters)
		{
			return _compiler.Emit(statementId, parameters);
		}
	}
}


b2)SdmapContext的实现.


using sdmap.ext;

namespace ConfigLab.App.DbContexts
{
	public class ConfigLabAppSdmapContext : SdmapContext
	{
		public ConfigLabAppSdmapContext(ISdmapEmiter maps)
			: this(maps)
		{
		}

		public ConfigLabAppSdmapContext()
			: this((ISdmapEmiter)(object)ConfigLabAppSqlEmiter.CreateFrom(typeof(UserResposibility).Assembly))
		{
		}
	}
}


参考资料