[C#]LinqPad產生SP相對應的類別

介紹如何使用LinqPad產生相對應的Input和Result的DTO

前言

目前的專案因為是全部使用sp的方式,所以專案使用了目前最火的dapper來做,但dapper需要類別的對應,針對一堆table還有sp的類別建立,其實也是非常費工,已經有mrkt大師介紹過了,如何使用table產生相對應的dto,雖然真的很方便,但sp的input和output呢?這篇則是來說明一下如何使用LinqPad來產生SP的相對應類別。

大綱

  1. 目的
  2. 產生SP的input的類別
  3. 產生SP的result的類別
  4. 總結

1.目的

其實產生table或sp的result相對應的類別,對我來說吸引力並不是很大,因為如果我們用entity來做db first的方式,已經可以產生table和sp的result類別了,但是input怎麼辦呢?我們來看一下entity產出的相關類別吧,以一個table的專案來做示例。

下圖是table的

下圖則是entity產生的sp對應的類別,首先是Result,看來挺ok的

下圖則是input的參數,好像沒辦法直接複製貼上轉成對應的類別耶

難不成如果input有四十個欄位,我只能一個一個copy嗎?只好google大神了,只要有現成工具,絕對不自己造輪子,因為經過眾人試驗的工具,相信issue也會被修正的差不多,但我卻好像也找不著有類似的做法,最後為了追求工程師極致的美德,也就是如下圖這個字

最後只好自己研究來造輪子了,接著就說明一下囉。

2.產生SP的input的類別

其實也是沿用了原本大神的table產生的類別,然後再改造一下,變成了我最需要的產生sp的input的類別,可以看到LinqPad的圖示例

完美的產生了Input的語法,一切就是那麼的美好,複製貼上就完成了一個類別的對應,頂多是改改sp的名稱,沒code沒真相,以下是在linq pad上的原始碼

void Main()
{
	this.Connection.DumpInput("select * from INFORMATION_SCHEMA.PARAMETERS where specific_name = 'usp_EHR_ACTIVITY_SAVE'", "usp_EHR_ACTIVITY_SAVEDto").Dump();
	
}

public static class LINQPadExtensions
{
	public static string DumpInput(this IDbConnection connection, string sql, string spName)
	{
		if (connection.State != ConnectionState.Open)
			connection.Open();

		var cmd = connection.CreateCommand();
		cmd.CommandText = sql;
		using (var reader = cmd.ExecuteReader())
		{
			string typeName = string.Empty;
			string columnName = string.Empty;
			var builder = new StringBuilder();
			builder.AppendLine($"public class {spName}");
			builder.AppendLine("{");
			while (reader.Read())
			{
				typeName = ConvertTypeToCsharp(reader["DATA_TYPE"].ToString());
				columnName = reader["PARAMETER_NAME"].ToString();
				columnName = columnName.Replace('@', ' ');
				builder.AppendLine(string.Format("\tpublic {0} {1} {{get; set;}}", typeName, columnName));
			}
			builder.AppendLine("}");
			builder.AppendLine();
			return builder.ToString();
		}
	}


	private static string ConvertTypeToCsharp(string type)
	{
		var lowerType = type.ToLower();
		if (lowerType == "int") return "int";
		if (lowerType == "smallint") return "short";
		if (lowerType == "bigint") return "long";
		if (lowerType == "tinyint") return "byte";
		if (lowerType == "binary") return "byte[]";
		if (lowerType.Contains("char") || lowerType.Contains("text")) return "string";
		if (lowerType.Contains("date")) return "DateTime";
		if (lowerType.Contains("decimal") || lowerType.Contains("numeric")) return "decimal";
		if (lowerType == "float") return lowerType;
		if (lowerType.Contains("money")) return "double";
		if (lowerType == "bit") return "bool";
		return null;
	}
}

3.產生SP的result的類別

雖然Entity預設已經能幫忙產生Result的類別,但每次都要去更新entity也是挺麻煩的,所以乾脆再來做一個sp的result吧,圖示如下。

一樣沒code沒真相的概念,以下則是result的原始碼,一樣copy去linq pad改改您的sp名稱,就能取得result的類別對應。

void Main()
{
	this.Connection.DumpResult("SELECT * FROM sys.dm_exec_describe_first_result_set_for_object(OBJECT_ID('usp_EHR_NOTIFY_SAMPLE_GET_BY_SERIL_NO'),NULL)", "usp_EHR_ACTIVITY_GET_BY_ACTIVITY_TYPE_SERIL_NOResultDto").Dump();
}

public static class LINQPadExtensions
{
	public static string DumpResult(this IDbConnection connection, string sql, string spName)
	{
		if (connection.State != ConnectionState.Open)
			connection.Open();

		var cmd = connection.CreateCommand();
		cmd.CommandText = sql;
		using (var reader = cmd.ExecuteReader())
		{
			string typeName = string.Empty;
			string columnName = string.Empty;
			var builder = new StringBuilder();
			builder.AppendLine($"public class {spName}");
			builder.AppendLine("{");
			while (reader.Read())
			{
				typeName = ConvertTypeToCsharp(reader["system_type_name"].ToString());
				columnName = reader["name"].ToString();
				columnName = columnName.Replace('@', ' ');
				builder.AppendLine(string.Format("\tpublic {0} {1} {{get; set;}}", typeName, columnName));
			}
			builder.AppendLine("}");
			builder.AppendLine();
			return builder.ToString();
		}
	}

	private static string ConvertTypeToCsharp(string type)
	{
		var lowerType = type.ToLower();
		if (lowerType == "int") return "int";
		if (lowerType == "smallint") return "short";
		if (lowerType == "bigint") return "long";
		if (lowerType == "tinyint") return "byte";
		if (lowerType == "binary") return "byte[]";
		if (lowerType.Contains("char") || lowerType.Contains("text")) return "string";
		if (lowerType.Contains("date")) return "DateTime";
		if (lowerType.Contains("decimal") || lowerType.Contains("numeric")) return "decimal";
		if (lowerType == "float") return lowerType;
		if (lowerType.Contains("money")) return "double";
		if (lowerType == "bit") return "bool";
		return null;
	}
}

4.總結

雖然可以在LINQPAD上面直接產生類別是很方便,但並不是每個人都有裝LINQPAD也不是每個人會使用,而且為了讓團隊可以方便使用,我乾脆就直接寫成一個網站,然後放在區網供團隊成員方便產生相對應的類別,這個網站是用mvc6+angularjs做的,因為一切都是用nuget下載的,所以就不用擔心任何前端套件的問題了,另外再加上自動產生Insert和Update的sp語法,對我來說這也足夠了,最後提供上GITHUB連結,希望幫助到有使用的團隊。

https://github.com/kinanson/mssql-generate-poco