C# ORM學習筆記:Dapper基本用法

来源:https://www.cnblogs.com/atomy/archive/2020/04/29/12784361.html
-Advertisement-
Play Games

一、基礎知識 1.1、Dapper簡介 Dapper是.NET下的一個micro ORM,它和Entity Framework或NHibnate不同,屬於輕量級並且是半自動的(實體類都要自己寫)。假如你喜歡原生的Sql語句,又喜歡ORM的簡單,那你一定會喜歡上Dapper這款ORM。 1.2、Dap ...


    一、基礎知識

    1.1、Dapper簡介

    Dapper是.NET下的一個micro ORM,它和Entity Framework或NHibnate不同,屬於輕量級並且是半自動的(實體類都要自己寫)。假如你喜歡原生的Sql語句,又喜歡ORM的簡單,那你一定會喜歡上Dapper這款ORM。

    1.2、Dapper優點

    1)輕量。只有一個文件(SqlMapper.cs)。

    2)速度快。Dapper的速度接近於IDataReader,取列表的數據超過了DataTable。

    3)支持多種資料庫。包括SQLite、SqlCe、Firebird、Oracle、MySQL、PostgreSQL、SQL Server。

    4)可以映射一對一、一對多、多對多等多種關係。

    5)性能高。通過Emit反射IDataReader的序列隊列,來快速地得到和產生對象。

    1.3、Dapper安裝

    此處使用Dapper擴展庫Dapper.SimpleCRUD,它也會預設安裝Dapper(依賴項):

    項目右鍵->管理 NuGet 程式包->Dapper.SimpleCRUD。

    二、數據準備

    2.1、數據表

    在SQL Server中創建4個數據表,分別是:Student(學生表)、Teacher(教師表)、Course(課程表)、Record(成績表)。

--學生表
CREATE TABLE [dbo].[Student](
    [StudentID] [INT] IDENTITY(1,1) NOT NULL,
    [Name] [NVARCHAR](50) NULL,
    [Age] [SMALLINT] NULL,
    [Gender] [NVARCHAR](10) NULL,
 CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED 
(
    [StudentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

--教師表
CREATE TABLE [dbo].[Teacher](
    [TeacherID] [INT] IDENTITY(1,1) NOT NULL,
    [Name] [NVARCHAR](50) NULL,
 CONSTRAINT [PK_Teacher] PRIMARY KEY CLUSTERED 
(
    [TeacherID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

--課程表
CREATE TABLE [dbo].[Course](
    [CourseID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
    [TeacherID] [int] NULL,
 CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED 
(
    [CourseID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

--成績表
CREATE TABLE [dbo].[Record](
    [StudentID] [INT] NOT NULL,
    [CourseID] [INT] NOT NULL,
    [Score] [NUMERIC](8, 2) NULL,
 CONSTRAINT [PK_Score] PRIMARY KEY CLUSTERED 
(
    [StudentID] ASC,
    [CourseID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

--學生表數據插入
INSERT INTO Student (Name,Age,Gender)
SELECT N'劉一',18,N'female'
UNION
SELECT N'陳二',19,N'female'
UNION
SELECT N'張三',18,N'male'
UNION
SELECT N'李四',19,N'male'
UNION
SELECT N'王五',18,N'male'
UNION
SELECT N'趙六',19,N'male'
UNION
SELECT N'孫七',19,N'female'

--教師表數據插入
INSERT INTO Teacher (Name)
SELECT N'周八'
UNION
SELECT N'吳九'
UNION
SELECT N'鄭十'

--課程表數據插入
INSERT INTO Course (Name,TeacherID)
SELECT N'離散數學',1
UNION
SELECT N'程式設計',2
UNION
SELECT N'數據結構',3

--成績表數據插入
INSERT INTO Record (StudentID,CourseID,Score )
SELECT 1,1,90
UNION
SELECT 2,1,91
UNION
SELECT 3,1,89
UNION
SELECT 4,1,75
UNION
SELECT 5,1,96
UNION
SELECT 6,1,78
UNION
SELECT 7,1,83
UNION
SELECT 1,2,86
UNION
SELECT 2,2,92
UNION
SELECT 3,2,77
UNION
SELECT 4,2,71
UNION
SELECT 5,2,66
UNION
SELECT 6,2,87
UNION
SELECT 7,2,93
UNION
SELECT 1,3,81
UNION
SELECT 2,3,90
UNION
SELECT 3,3,88
UNION
SELECT 4,3,82
UNION
SELECT 5,3,93
UNION
SELECT 6,3,91
UNION
SELECT 7,3,84
View Code

    2.2、實體類

    Dapper的實體映射:

    1)屬性不編輯,用[Editable(false)]這個特性標記,預設是true。

    2)類名到表名的映射,用[Table("TableName")]特性,TableName對應物理數據表名稱。

    3)主鍵映射,如果您的實體類中有Id屬性,Dapper會預設此屬性為主鍵,否則要為作為主鍵的屬性添加[Key]特性。

    由上可知,如Student表,其實體類應該生成下麵這個樣子:

using System;
using System.Collections.Generic;
using System.Text;
using Dapper;

namespace LinkTo.Test.ConsoleDapper
{
    [Table("Student")]
    [Serializable]
    public class Student
    {
        [Key]
        public int? StudentID {get; set;}

        public string Name {get; set;}

        public short? Age {get; set;}

        public string Gender {get; set;}
    }
}
View Code

    2.3、使用T4模板生成實體類

    2.3.1、T4Code文件夾的文本模板

<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.Data.SqlClient"#>
<#+
    #region T4Code
    /// <summary>
    /// 資料庫架構介面
    /// </summary>
    public interface IDBSchema : IDisposable
    {
        List<string> GetTableList();
        DataTable GetTableMetadata(string tableName);
    }

    /// <summary>
    /// 資料庫架構工廠
    /// </summary>
    public class DBSchemaFactory
    {
        static readonly string DatabaseType = "SqlServer";
        public static IDBSchema GetDBSchema()
        {
            IDBSchema dbSchema;
            switch (DatabaseType) 
            {
                case "SqlServer":
                    {
                        dbSchema =new SqlServerSchema();
                        break;
                    }
                default: 
                    {
                        throw new ArgumentException("The input argument of DatabaseType is invalid.");
                    }
            }
            return dbSchema;
        }
    }

    /// <summary>
    /// SqlServer
    /// </summary>
    public class SqlServerSchema : IDBSchema
    {
        public string ConnectionString = "Server=.;Database=Test;Uid=sa;Pwd=********;";
        public SqlConnection conn;

        public SqlServerSchema()
        {
            conn = new SqlConnection(ConnectionString);
            conn.Open();
        }

        public List<string> GetTableList()
        {
            List<string> list = new List<string>();
            string commandText = "SELECT NAME TABLE_NAME FROM SYSOBJECTS WHERE XTYPE='U' ORDER BY NAME";

            using(SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (dr.Read())
                    {
                        list.Add(dr["TABLE_NAME"].ToString());
                    }
                }
            }

            return list;
        }
        
        public DataTable GetTableMetadata(string tableName)
        {
            string commandText=string.Format
                (
                    "SELECT A.NAME TABLE_NAME,B.NAME FIELD_NAME,C.NAME DATATYPE,ISNULL(B.PREC,0) LENGTH, "+
                        "CONVERT(BIT,CASE WHEN NOT F.ID IS NULL THEN 1 ELSE 0 END) ISKEY, "+
                        "CONVERT(BIT,CASE WHEN COLUMNPROPERTY(B.ID,B.NAME,'ISIDENTITY') = 1 THEN 1 ELSE 0 END) AS ISIDENTITY, "+
                        "CONVERT(BIT,B.ISNULLABLE) ISNULLABLE "+
                    "FROM SYSOBJECTS A INNER JOIN SYSCOLUMNS B ON A.ID=B.ID INNER JOIN SYSTYPES C ON B.XTYPE=C.XUSERTYPE "+
                        "LEFT JOIN SYSOBJECTS D ON B.ID=D.PARENT_OBJ AND D.XTYPE='PK' "+
                        "LEFT JOIN SYSINDEXES E ON B.ID=E.ID AND D.NAME=E.NAME "+
                        "LEFT JOIN SYSINDEXKEYS F ON B.ID=F.ID AND B.COLID=F.COLID AND E.INDID=F.INDID "+
                    "WHERE A.XTYPE='U' AND A.NAME='{0}' "+
                    "ORDER BY A.NAME,B.COLORDER", tableName
                );

            using(SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                da.Fill(ds,"Schema");
                return ds.Tables[0];
            }
        }

        public void Dispose()
        {
            if (conn != null)
            {
                conn.Close();
            }
        }
    }
    #endregion
#>
DBSchema.ttinclude
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>

<#+
// T4 Template Block manager for handling multiple file outputs more easily.
// Copyright (c) Microsoft Corporation.All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (MS-PL)

// Manager class records the various blocks so it can split them up
class Manager
{
    public struct Block
    {
        public string Name;
        public int Start, Length;
    }

    public List<Block> blocks = new List<Block>();
    public Block currentBlock;
    public Block footerBlock = new Block();
    public Block headerBlock = new Block();
    public ITextTemplatingEngineHost host;
    public ManagementStrategy strategy;
    public StringBuilder template;
    public string OutputPath { get; set; }

    public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader)
    {
        this.host = host;
        this.template = template;
        OutputPath = string.Empty;
        strategy = ManagementStrategy.Create(host);
    }

    public void StartBlock(string name)
    {
        currentBlock = new Block { Name = name, Start = template.Length };
    }

    public void StartFooter()
    {
        footerBlock.Start = template.Length;
    }

    public void EndFooter()
    {
        footerBlock.Length = template.Length - footerBlock.Start;
    }

    public void StartHeader()
    {
        headerBlock.Start = template.Length;
    }

    public void EndHeader()
    {
        headerBlock.Length = template.Length - headerBlock.Start;
    }    

    public void EndBlock()
    {
        currentBlock.Length = template.Length - currentBlock.Start;
        blocks.Add(currentBlock);
    }

    public void Process(bool split)
    {
        string header = template.ToString(headerBlock.Start, headerBlock.Length);
        string footer = template.ToString(footerBlock.Start, footerBlock.Length);
        blocks.Reverse();
        foreach(Block block in blocks) {
            string fileName = Path.Combine(OutputPath, block.Name);
            if (split) {
                string content = header + template.ToString(block.Start, block.Length) + footer;
                strategy.CreateFile(fileName, content);
                template.Remove(block.Start, block.Length);
            } else {
                strategy.DeleteFile(fileName);
            }
        }
    }
}

class ManagementStrategy
{
    internal static ManagementStrategy Create(ITextTemplatingEngineHost host)
    {
        return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
    }

    internal ManagementStrategy(ITextTemplatingEngineHost host) { }

    internal virtual void CreateFile(string fileName, string content)
    {
        File.WriteAllText(fileName, content);
    }

    internal virtual void DeleteFile(string fileName)
    {
        if (File.Exists(fileName))
            File.Delete(fileName);
    }
}

class VSManagementStrategy : ManagementStrategy
{
    private EnvDTE.ProjectItem templateProjectItem;

    internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host)
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)host;
        if (hostServiceProvider == null)
            throw new ArgumentNullException("Could not obtain hostServiceProvider");

        EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
        if (dte == null)
            throw new ArgumentNullException("Could not obtain DTE from host");

        templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
    }

    internal override void CreateFile(string fileName, string content)
    {
        base.CreateFile(fileName, content);
        ((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
    }

    internal override void DeleteFile(string fileName)
    {
        ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
    }

    private void FindAndDeleteFile(string fileName)
    {
        foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
        {
            if (projectItem.get_FileNames(0) == fileName)
            {
                projectItem.Delete();
                return;
            }
        }
    }
}
#>
MultiDocument.ttinclude

    DBSchema.ttinclude主要實現了資料庫工廠的功能。註:請將資料庫連接字元串改成您自己的。

    MultiDocument.ttinclude主要實現了多文檔的功能。

    2.3.2、生成實體類的文本模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="T4Code/DBSchema.ttinclude"#>
<#@ include file="T4Code/MultiDocument.ttinclude"#>
<# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<#
    //System.Diagnostics.Debugger.Launch();//調試
    var dbSchema = DBSchemaFactory.GetDBSchema();
    List<string> tableList = dbSchema.GetTableList();
    foreach (string tableName in tableList)
    {
        manager.StartBlock(tableName+".cs");
        DataTable table = dbSchema.GetTableMetadata(tableName);

        //獲取主鍵
        string strKey = string.Empty;
        foreach (DataRow dataRow in table.Rows)
        {
            if ((bool)dataRow["ISKEY"] == true)
            {
                strKey = dataRow["FIELD_NAME"].ToString();
                break;
            }
        }
        
#>
//-------------------------------------------------------------------------------
// 此代碼由T4模板MultiModelAuto自動生成
// 生成時間 <#= DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") #>
// 對此文件的更改可能會導致不正確的行為,並且如果重新生成代碼,這些更改將會丟失。
//-------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using Dapper;

namespace LinkTo.Test.ConsoleDapper
{
    [Table("<#= tableName #>")]
    [Serializable]
    public class <#= tableName #>
    {
<#
        foreach (DataRow dataRow in table.Rows)
        {
            //獲取數據類型
            string dbDataType = dataRow["DATATYPE"].ToString();
            string dataType = string.Empty;
                    
            switch (dbDataType)
            {
                case "decimal":
                case "numeric":
                case "money":
                case "smallmoney":
                    dataType = "decimal?";
                    break;
                case "char":
                case "nchar":
                case "varchar":
                case "nvarchar":
                case "text":
                case "ntext":
                    dataType = "string";
                    break;
                case "uniqueidentifier":
                    dataType = "Guid?";
                    break;
                case "bit":
                    dataType = "bool?";
                    break;
                case "real":
                    dataType = "Single?";
                    break;
                case "bigint":
                    dataType = "long?";
                    break;
                case "int":
                    dataType 
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • String 字元串 一個字元串是由一系列字元組成的。其中每個字元等同於一個位元組。這意味著 PHP 只能支持 256 的字元集,因此不支持 Unicode 。 語法 一個字元串可以用 4 種方式表達: 單引號 雙引號 單引號 一個最簡單的字元串是用單引號包起來的。可以再前面加反斜杠(\)來轉義。例如 ...
  • 我覺得我生活在這世上二十多年裡,去過最多的餐廳就是肯德基小時候逢生日必去,現在長大了,肯德基成了我的日常零食下班後從門前路過餓了便會進去點分黃金雞塊或者小吃拼盤早上路過,會買杯咖啡。主要快捷美味且飽腹,而且到處都是總是會路過,現在只要一餓,心心念念便是肯德基的味道 環境介紹 python 3.6 p ...
  • [TOC] 前言 今天在公司無事,就搞了下文件上傳的東東,方便自己的學習,和今後的使用,目前只支持單文件上傳。下麵是代碼部分,也可以訪問我的 "Gitee" 或 "Github" ,不想看代碼的童鞋可以直接 "下載]。。。(算了,討厭百度雲,如果有需要的話留言)代碼使用,使用方法請[點擊" 代碼 使 ...
  • 效果: 批量查詢指定關鍵字 & 指定目錄下PDF文件中的文本,並導出文件路徑和關鍵字所在文本行。 下載: 鏈接: https://pan.baidu.com/s/1sK2OMMgGX26l7PiMPqKXkA 提取碼: 67h4 使用: 雙擊exe,輸入pdf所在目錄,空格,關鍵字 ...
  • 一.實現思路 本文講解如何使用python實現一個簡單的模板引擎, 支持傳入變數, 使用if判斷和for迴圈語句, 最終能達到下麵這樣的效果: 渲染前的文本: <h1>{{title}}</h1> <p>十以內的奇數:</p> <ul> {% for i in range(10) %} {% if ...
  • 前言 本文主要講的是Asp.Net Core的啟動過程,幫助大家掌握應用程式的關鍵配置點。 1、創建項目 1.1、用Visual Studio 2019 創建WebApi項目。 這裡面可以看到有兩個關鍵的類。 一個Program,一個stsrtup Program裡面有一個Main函數,Main函數 ...
  • 一、首先去https://www.docker.com/products/docker-desktop下載Windows版本的Docker Desktop並安裝(需要win10專業版以上操作系統,並啟用CPU虛擬化和安裝Hvper-V)。 二、新建一個.NetCore3.1的API項目,在創建的時候 ...
  • Hashtable.Synchronized(new HashTable())將HashTable封裝成一個線程安全的SyncHashTable。 但該方法在枚舉整個集合時本質上不是一個線程安全的過程,即使某個集合已經同步,其他線程仍可以修改該集合,這會導致枚舉數引發異常。 若要確保枚舉過程中的線程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...