.Net?Core下使用Dapper的方法
一、前言
關(guān)于什么是Dapper(詳細(xì)入口),在此不做贅述;本文僅對(duì)Dapper在.Net Core中的使用作扼要說(shuō)明,所陳代碼以示例講解為主,乃拋磚引玉,開發(fā)者可根據(jù)自身需要進(jìn)行擴(kuò)展和調(diào)整;其中如有疏漏之處,望不吝斧正。
不了解Dapper的朋友可以看這篇文章:ORM框架之Dapper簡(jiǎn)介和性能測(cè)試
二、Dapper環(huán)境搭建
當(dāng)前以.Net Core WebAPI或MVC項(xiàng)目為例,框架版本為.NET 5.0,相關(guān)NuGet包引用如下:
Install-Package Dapper
Install-Package Dapper.Contrib
Install-Package Dapper.SqlBuilder
Install-Package System.Data.SqlClient
其中Dapper.Contrib和Dapper.SqlBuilder為Dapper的擴(kuò)展,當(dāng)然,Dapper的擴(kuò)展還有如Dapper.Rainbow等其他包,根據(jù)自身需要引用,對(duì)相關(guān)引用作下說(shuō)明:
- Dapper:不言而喻;
- Dapper.Contrib:可使用對(duì)象進(jìn)行數(shù)據(jù)表的增刪改查,免卻SQL語(yǔ)句的編寫;
- Dapper.SqlBuilder:可以方便動(dòng)態(tài)構(gòu)建SQL語(yǔ)句,如Join、SELECT、Where、OrderBy等等;
- System.Data.SqlClient:由于示例數(shù)據(jù)庫(kù)為Sql Server,如MySql則引用MySql.Data;
對(duì)于Dapper.Contrib實(shí)體配置選項(xiàng),以Product類為例,作扼要說(shuō)明如下:
[Table("Product")] public class Product { [Key] public int Id { get; set; } public string Name{ get; set; } public string Description { get; set; } public decimal Price { get; set; } public DateTime CreateTime { get; set; } }
對(duì)于實(shí)體配置項(xiàng),有如下幾個(gè)主要項(xiàng):
- Table:指定數(shù)據(jù)庫(kù)表名,可忽略;
- Key:指定為自動(dòng)增長(zhǎng)主鍵;
- ExplicitKey:指定非自動(dòng)增長(zhǎng)主鍵,如guid;
- Computed:計(jì)算列屬性,Insert、Update操作將忽略此列;
- Write:是否可寫入,true/false,如[Write(false)],false時(shí)Insert、Update操作將忽略此列,比如可擴(kuò)展局部類作數(shù)據(jù)表額外查詢字段使用;
對(duì)于數(shù)據(jù)表對(duì)象實(shí)體,可結(jié)合T4模板生成即可。
三、Dapper封裝
關(guān)于Dapper數(shù)據(jù)訪問(wèn),這里參考Github上的某示例(入口:https://github.com/EloreTec/UnitOfWorkWithDapper),作修改調(diào)整封裝如下:
定義DapperDBContext類
public abstract class DapperDBContext : IContext { private IDbConnection _connection; private IDbTransaction _transaction; private int? _commandTimeout = null; private readonly DapperDBContextOptions _options; public bool IsTransactionStarted { get; private set; } protected abstract IDbConnection CreateConnection(string connectionString); protected DapperDBContext(IOptions<DapperDBContextOptions> optionsAccessor) { _options = optionsAccessor.Value; _connection = CreateConnection(_options.Configuration); _connection.Open(); DebugPrint("Connection started."); } #region Transaction public void BeginTransaction() { if (IsTransactionStarted) throw new InvalidOperationException("Transaction is already started."); _transaction = _connection.BeginTransaction(); IsTransactionStarted = true; DebugPrint("Transaction started."); } public void Commit() { if (!IsTransactionStarted) throw new InvalidOperationException("No transaction started."); _transaction.Commit(); _transaction = null; IsTransactionStarted = false; DebugPrint("Transaction committed."); } public void Rollback() { if (!IsTransactionStarted) throw new InvalidOperationException("No transaction started."); _transaction.Rollback(); _transaction.Dispose(); _transaction = null; IsTransactionStarted = false; DebugPrint("Transaction rollbacked and disposed."); } #endregion Transaction #region Dapper.Contrib.Extensions public async Task<T> GetAsync<T>(int id) where T : class, new() { return await _connection.GetAsync<T>(id, _transaction, _commandTimeout); } public async Task<T> GetAsync<T>(string id) where T : class, new() { return await _connection.GetAsync<T>(id, _transaction, _commandTimeout); } public async Task<IEnumerable<T>> GetAllAsync<T>() where T : class, new() { return await _connection.GetAllAsync<T>(); } public long Insert<T>(T model) where T : class, new() { return _connection.Insert<T>(model, _transaction, _commandTimeout); } public async Task<int> InsertAsync<T>(T model) where T : class, new() { return await _connection.InsertAsync<T>(model, _transaction, _commandTimeout); } public bool Update<T>(T model) where T : class, new() { return _connection.Update<T>(model, _transaction, _commandTimeout); } public async Task<bool> UpdateAsync<T>(T model) where T : class, new() { return await _connection.UpdateAsync<T>(model, _transaction, _commandTimeout); } public async Task<Page<T>> PageAsync<T>(long pageIndex, long pageSize, string sql, object param = null) { DapperPage.BuildPageQueries((pageIndex - 1) * pageSize, pageSize, sql, out string sqlCount, out string sqlPage); var result = new Page<T> { CurrentPage = pageIndex, ItemsPerPage = pageSize, TotalItems = await _connection.ExecuteScalarAsync<long>(sqlCount, param) }; result.TotalPages = result.TotalItems / pageSize; if ((result.TotalItems % pageSize) != 0) result.TotalPages++; result.Items = await _connection.QueryAsync<T>(sqlPage, param); return result; } #endregion #region Dapper Execute & Query public int ExecuteScalar(string sql, object param = null, CommandType commandType = CommandType.Text) { return _connection.ExecuteScalar<int>(sql, param, _transaction, _commandTimeout, commandType); } public async Task<int> ExecuteScalarAsync(string sql, object param = null, CommandType commandType = CommandType.Text) { return await _connection.ExecuteScalarAsync<int>(sql, param, _transaction, _commandTimeout, commandType); } public int Execute(string sql, object param = null, CommandType commandType = CommandType.Text) { return _connection.Execute(sql, param, _transaction, _commandTimeout, commandType); } public async Task<int> ExecuteAsync(string sql, object param = null, CommandType commandType = CommandType.Text) { return await _connection.ExecuteAsync(sql, param, _transaction, _commandTimeout, commandType); } public IEnumerable<T> Query<T>(string sql, object param = null, CommandType commandType = CommandType.Text) { return _connection.Query<T>(sql, param, _transaction, true, _commandTimeout, commandType); } public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, CommandType commandType = CommandType.Text) { return await _connection.QueryAsync<T>(sql, param, _transaction, _commandTimeout, commandType); } public T QueryFirstOrDefault<T>(string sql, object param = null, CommandType commandType = CommandType.Text) { return _connection.QueryFirstOrDefault<T>(sql, param, _transaction, _commandTimeout, commandType); } public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, CommandType commandType = CommandType.Text) { return await _connection.QueryFirstOrDefaultAsync<T>(sql, param, _transaction, _commandTimeout, commandType); } public IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, string splitOn = "Id", CommandType commandType = CommandType.Text) { return _connection.Query(sql, map, param, _transaction, true, splitOn, _commandTimeout, commandType); } public async Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, string splitOn = "Id", CommandType commandType = CommandType.Text) { return await _connection.QueryAsync(sql, map, param, _transaction, true, splitOn, _commandTimeout, commandType); } public async Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param = null, CommandType commandType = CommandType.Text) { return await _connection.QueryMultipleAsync(sql, param, _transaction, _commandTimeout, commandType); } #endregion Dapper Execute & Query public void Dispose() { if (IsTransactionStarted) Rollback(); _connection.Close(); _connection.Dispose(); _connection = null; DebugPrint("Connection closed and disposed."); } private void DebugPrint(string message) { #if DEBUG Debug.Print(">>> UnitOfWorkWithDapper - Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, message); #endif } }
以上代碼涵蓋了Dapper訪問(wèn)數(shù)據(jù)庫(kù)的基本操作,分同步和異步,其中大部分不作贅述,著重說(shuō)下分頁(yè)部分;
異步分頁(yè)構(gòu)建(PageAsync)
分頁(yè)這里為方便調(diào)用,只需傳入要查詢的Sql語(yǔ)句(如:SELECT * FROM Table,必須帶Order BY)、頁(yè)索引、頁(yè)大小即可;
至于具體如何構(gòu)建的,這里參照某小型ORM工具PetaPoco,抽取相關(guān)代碼如下,有興趣的同學(xué)也可以自行改造:
public class Page<T> { /// <summary> /// The current page number contained in this page of result set /// </summary> public long CurrentPage { get; set; } /// <summary> /// The total number of pages in the full result set /// </summary> public long TotalPages { get; set; } /// <summary> /// The total number of records in the full result set /// </summary> public long TotalItems { get; set; } /// <summary> /// The number of items per page /// </summary> public long ItemsPerPage { get; set; } /// <summary> /// The actual records on this page /// </summary> public IEnumerable<T> Items { get; set; } //public List<T> Items { get; set; } } public class DapperPage { public static void BuildPageQueries(long skip, long take, string sql, out string sqlCount, out string sqlPage) { // Split the SQL if (!PagingHelper.SplitSQL(sql, out PagingHelper.SQLParts parts)) throw new Exception("Unable to parse SQL statement for paged query"); sqlPage = BuildPageSql.BuildPageQuery(skip, take, parts); sqlCount = parts.sqlCount; } } static class BuildPageSql { public static string BuildPageQuery(long skip, long take, PagingHelper.SQLParts parts) { parts.sqlSelectRemoved = PagingHelper.rxOrderBy.Replace(parts.sqlSelectRemoved, "", 1); if (PagingHelper.rxDistinct.IsMatch(parts.sqlSelectRemoved)) { parts.sqlSelectRemoved = "peta_inner.* FROM (SELECT " + parts.sqlSelectRemoved + ") peta_inner"; } var sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>{2} AND peta_rn<={3}", parts.sqlOrderBy ?? "ORDER BY (SELECT NULL)", parts.sqlSelectRemoved, skip, skip + take); //args = args.Concat(new object[] { skip, skip + take }).ToArray(); return sqlPage; } //SqlServer 2012及以上 public static string BuildPageQuery2(long skip, long take, PagingHelper.SQLParts parts) { parts.sqlSelectRemoved = PagingHelper.rxOrderBy.Replace(parts.sqlSelectRemoved, "", 1); if (PagingHelper.rxDistinct.IsMatch(parts.sqlSelectRemoved)) { parts.sqlSelectRemoved = "peta_inner.* FROM (SELECT " + parts.sqlSelectRemoved + ") peta_inner"; } var sqlOrderBy = parts.sqlOrderBy ?? "ORDER BY (SELECT NULL)"; var sqlPage = $"SELECT {parts.sqlSelectRemoved} {sqlOrderBy} OFFSET {skip} ROWS FETCH NEXT {take} ROWS ONLY"; return sqlPage; } } static class PagingHelper { public struct SQLParts { public string sql; public string sqlCount; public string sqlSelectRemoved; public string sqlOrderBy; } public static bool SplitSQL(string sql, out SQLParts parts) { parts.sql = sql; parts.sqlSelectRemoved = null; parts.sqlCount = null; parts.sqlOrderBy = null; // Extract the columns from "SELECT <whatever> FROM" var m = rxColumns.Match(sql); if (!m.Success) return false; // Save column list and replace with COUNT(*) Group g = m.Groups[1]; parts.sqlSelectRemoved = sql.Substring(g.Index); if (rxDistinct.IsMatch(parts.sqlSelectRemoved)) parts.sqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length); else parts.sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length); // Look for the last "ORDER BY <whatever>" clause not part of a ROW_NUMBER expression m = rxOrderBy.Match(parts.sqlCount); if (!m.Success) { parts.sqlOrderBy = null; } else { g = m.Groups[0]; parts.sqlOrderBy = g.ToString(); parts.sqlCount = parts.sqlCount.Substring(0, g.Index) + parts.sqlCount.Substring(g.Index + g.Length); } return true; } public static Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled); public static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?!.*?(?:\)|\s+)AS\s)(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled); public static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled); }
對(duì)于構(gòu)建分頁(yè)語(yǔ)句,分別示例BuildPageQuery和BuildPageQuery2,前者為通過(guò)ROW_NUMBER進(jìn)行分頁(yè)(針對(duì)SqlServer2005、2008),后者通過(guò)OFFSET、FETCH分頁(yè)(針對(duì)SqlServer2012及以上版本),相關(guān)輔助操作類一覽便知,如果使用MySql數(shù)據(jù)庫(kù),可酌情自行封裝;
至于Where查詢的進(jìn)一步封裝,有興趣的也可兌Dapper lamada查詢進(jìn)行擴(kuò)展。
定義工作單元與事務(wù)
public interface IUnitOfWork : IDisposable { void SaveChanges(); } public interface IUnitOfWorkFactory { IUnitOfWork Create(); } public class UnitOfWork : IUnitOfWork { private readonly IContext _context; public UnitOfWork(IContext context) { _context = context; _context.BeginTransaction(); } public void SaveChanges() { if (!_context.IsTransactionStarted) throw new InvalidOperationException("Transaction have already been commited or disposed."); _context.Commit(); } public void Dispose() { if (_context.IsTransactionStarted) _context.Rollback(); } } public class DapperUnitOfWorkFactory : IUnitOfWorkFactory { private readonly DapperDBContext _context; public DapperUnitOfWorkFactory(DapperDBContext context) { _context = context; } public IUnitOfWork Create() { return new UnitOfWork(_context); } }
定義數(shù)據(jù)倉(cāng)儲(chǔ)
#region Product public partial interface IProductRepository { Task<Product> GetAsync(int id); Task<IEnumerable<Product>> GetAllAsync(); long Insert(Product model); Task<int> InsertAsync(Product model); bool Update(Product model); Task<bool> UpdateAsync(Product model); int Count(string where, object param = null); Task<int> CountAsync(string where, object param = null); bool Exists(string where, object param = null); Task<bool> ExistsAsync(string where, object param = null); Product FirstOrDefault(string where, object param = null); Task<Product> FirstOrDefaultAsync(string where, object param = null); T FirstOrDefault<T>(string sql, object param = null); Task<T> FirstOrDefaultAsync<T>(string sql, object param = null); IEnumerable<Product> Fetch(SqlBuilder where); Task<IEnumerable<Product>> FetchAsync(SqlBuilder where); IEnumerable<T> Fetch<T>(string sql, SqlBuilder where, bool orderBy = true); Task<IEnumerable<T>> FetchAsync<T>(string sql, SqlBuilder where, bool orderBy = true); Task<Page<Product>> PageAsync(long pageIndex, long pageSize, SqlBuilder builder); Task<Page<T>> PageAsync<T>(string sql, long pageIndex, long pageSize, SqlBuilder builder); Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param = null); } public partial class ProductRepository : IProductRepository { private readonly DapperDBContext _context; public ProductRepository(DapperDBContext context) { _context = context; } public async Task<Product> GetAsync(int id) { return await _context.GetAsync<Product>(id); } public async Task<IEnumerable<Product>> GetAllAsync() { return await _context.GetAllAsync<Product>(); } public long Insert(Product model) { return _context.Insert<Product>(model); } public async Task<int> InsertAsync(Product model) { return await _context.InsertAsync<Product>(model); } public bool Update(Product model) { return _context.Update<Product>(model); } public async Task<bool> UpdateAsync(Product model) { return await _context.UpdateAsync<Product>(model); } public int Count(string where, object param = null) { string strSql = $"SELECT COUNT(1) FROM Product {where}"; return _context.ExecuteScalar(strSql, param); } public async Task<int> CountAsync(string where, object param = null) { string strSql = $"SELECT COUNT(1) FROM Product {where}"; return await _context.ExecuteScalarAsync(strSql, param); } public bool Exists(string where, object param = null) { string strSql = $"SELECT TOP 1 1 FROM Product {where}"; var count = _context.ExecuteScalar(strSql, param); return count > 0; } public async Task<bool> ExistsAsync(string where, object param = null) { string strSql = $"SELECT TOP 1 1 FROM Product {where}"; var count = await _context.ExecuteScalarAsync(strSql, param); return count > 0; } public Product FirstOrDefault(string where, object param = null) { string strSql = $"SELECT TOP 1 * FROM Product {where}"; return _context.QueryFirstOrDefault<Product>(strSql, param); } public async Task<Product> FirstOrDefaultAsync(string where, object param = null) { string strSql = $"SELECT TOP 1 * FROM Product {where}"; return await _context.QueryFirstOrDefaultAsync<Product>(strSql, param); } public T FirstOrDefault<T>(string sql, object param = null) { return _context.QueryFirstOrDefault<T>(sql, param); } public async Task<T> FirstOrDefaultAsync<T>(string sql, object param = null) { return await _context.QueryFirstOrDefaultAsync<T>(sql, param); } public IEnumerable<Product> Fetch(SqlBuilder where) { var strSql = where.AddTemplate(@"SELECT * FROM Product /**where**/ /**orderby**/"); return _context.Query<Product>(strSql.RawSql, strSql.Parameters); } public async Task<IEnumerable<Product>> FetchAsync(SqlBuilder where) { var strSql = where.AddTemplate(@"SELECT * FROM Product /**where**/ /**orderby**/"); return await _context.QueryAsync<Product>(strSql.RawSql, strSql.Parameters); } public IEnumerable<T> Fetch<T>(string sql, SqlBuilder where, bool orderBy = true) { var _sql = orderBy ? $"{sql} /**where**/ /**orderby**/" : $"{sql} /**where**/"; var strSql = where.AddTemplate(_sql); return _context.Query<T>(strSql.RawSql, strSql.Parameters); } public async Task<IEnumerable<T>> FetchAsync<T>(string sql, SqlBuilder where, bool orderBy = true) { var _sql = orderBy ? $"{sql} /**where**/ /**orderby**/" : $"{sql} /**where**/"; var strSql = where.AddTemplate(_sql); return await _context.QueryAsync<T>(strSql.RawSql, strSql.Parameters); } public async Task<Page<Product>> PageAsync(long pageIndex, long pageSize, SqlBuilder builder) { var strSql = "SELECT * FROM Product"; return await PageAsync<Product>(strSql, pageIndex, pageSize, builder); } public async Task<Page<T>> PageAsync<T>(string sql, long pageIndex, long pageSize, SqlBuilder builder) { var strSql = builder.AddTemplate($"{sql} /**where**/ /**orderby**/"); return await _context.PageAsync<T>(pageIndex, pageSize, strSql.RawSql, strSql.Parameters); } public async Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param = null) { return await _context.QueryMultipleAsync(sql, param); } } #endregion
根據(jù)自身需要進(jìn)行調(diào)整或擴(kuò)展,一般借助T4模板生成
數(shù)據(jù)庫(kù)連接
通過(guò)Ioptions模式讀取配置文件appsettings中連接字符串
public class MyDBContext : DapperDBContext { public MyDBContext(IOptions<DapperDBContextOptions> optionsAccessor) : base(optionsAccessor) { } protected override IDbConnection CreateConnection(string connectionString) { IDbConnection conn = new SqlConnection(connectionString); return conn; } }
四、Dapper使用
Startup.cs注入并讀取數(shù)據(jù)庫(kù)連接字符串
{ "SQLConnString": "Data Source=(local);Initial Catalog=database;Persist Security Info=True;User ID=sa;Password=123456;MultipleActiveResultSets=True;", "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" }
services.AddDapperDBContext<MyDBContext>(options => { options.Configuration = Configuration["SQLConnString"]; });
簡(jiǎn)單示例WebAPI或Net Core MVC下的調(diào)用示例:
public class ProductController : BaseController { private readonly IProductRepository _productRepository; public ProductController( IProductRepository productRepository ) { _productRepository = productRepository; } //商品列表 [HttpGet] public async Task<IActionResult> ProductList(DateTime? startDate, DateTime? endDate, int id = 1, int productStatus = 0, string keyword = "") { var model = new ProductModels(); var builder = new Dapper.SqlBuilder(); builder.Where("ProductStatus!=@ProductStatus", new { ProductStatus = productStatus }); if (startDate.HasValue) { builder.Where("CreateTime>=@startDate", new { startDate = startDate.Value}); } if (endDate.HasValue) { builder.Where("CreateTime<@endDate", new { endDate = endDate.Value.AddDays(1)}); } if (!string.IsNullOrWhiteSpace(keyword)) { builder.Where("Name LIKE @keyword", new { keyword = $"%{StringHelper.ReplaceSql(keyword)}%" }); } builder.OrderBy("SortNum DESC,CreateTime DESC"); var list = await _productRepository.PageAsync(id, PageSize, builder); model.ProductList = new PagedList<Product>(list.Items, id, PageSize, list.TotalItems); if (Request.IsAjaxRequest()) return PartialView("_ProductList", model.ProductList); return View(model); } //添加商品 [HttpPost] public async Task<int> AddProduct(ProductModels model) { return await _productRepository.InsertAsync(model); } }
public partial interface IProductService { Task<bool> AddProduct(Product productInfo, List<ProductStock> skuList); } public class ProductService: IProductService { private readonly DapperDBContext _context; private readonly IUnitOfWorkFactory _uowFactory; public ProductService(DapperDBContext context, IUnitOfWorkFactory uowFactory) { _context = context; _uowFactory = uowFactory; } /// <summary> /// 添加商品 /// </summary> /// <param name="productInfo"></param> /// <param name="skuList"></param> /// <returns></returns> public async Task<bool> AddProduct(Product productInfo, List<ProductStock> skuList) { var result = false; using (var uow = _uowFactory.Create()) { //添加產(chǎn)品 await _context.InsertAsync(productInfo); //添加Sku庫(kù)存售價(jià) //await _context.InsertAsync(skuList); uow.SaveChanges(); result = true; } return result; } }
以上所述是小編給大家介紹的.Net Core下使用Dapper的方法,希望對(duì)大家有所幫助。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- 輕量級(jí)ORM框架Dapper應(yīng)用之Dapper支持存儲(chǔ)過(guò)程
- 輕量級(jí)ORM框架Dapper應(yīng)用之實(shí)現(xiàn)Join操作
- 輕量級(jí)ORM框架Dapper應(yīng)用之返回多個(gè)結(jié)果集
- 輕量級(jí)ORM框架Dapper應(yīng)用之實(shí)現(xiàn)In操作
- 輕量級(jí)ORM框架Dapper應(yīng)用之實(shí)現(xiàn)CURD操作
- 輕量級(jí)ORM框架Dapper應(yīng)用之安裝Dapper
- ORM框架之Dapper簡(jiǎn)介和性能測(cè)試
- 輕量級(jí)ORM框架Dapper應(yīng)用支持操作函數(shù)和事物
相關(guān)文章
《解剖PetShop》之六:PetShop之表示層設(shè)計(jì)
表示層(Presentation Layer)通俗的講就是界面層,也就是展示給客戶的網(wǎng)頁(yè),本文主要講解PetShop4.0的表示層設(shè)計(jì),需要的朋友可以參考下。2016-05-05《解剖PetShop》之五:PetShop之業(yè)務(wù)邏輯層設(shè)計(jì)
業(yè)務(wù)邏輯層(Business Logic Layer)無(wú)疑是系統(tǒng)架構(gòu)中體現(xiàn)核心價(jià)值的部分,本文主要講解PetShop4.0的業(yè)務(wù)邏輯層設(shè)計(jì),需要的朋友可以參考下。2016-05-05ASP.NET 2.0中的數(shù)據(jù)操作之八:使用兩個(gè)DropDownList過(guò)濾的主/從報(bào)表
本文主要介紹在ASP.NET 2.0中如何如何將DropDownList和另一個(gè)DropDownList控件關(guān)聯(lián),選擇產(chǎn)品分類和具體的產(chǎn)品時(shí),使用DetailsView顯示產(chǎn)品的詳細(xì)信息。2016-05-05解讀ASP.NET 5 & MVC6系列教程(11):Routing路由
這篇文章主要介紹了ASP.NET 5 Routing路由的用法,雖然ASP.NET 5 和MVC6的路由使用方式很簡(jiǎn)單,但是相關(guān)的使用規(guī)則卻很復(fù)雜,大家使用的時(shí)候需要多加注意。2016-06-06解讀ASP.NET 5 & MVC6系列教程(8):Session與Caching
這篇文章主要介紹了ASP.NET 5 中的Session與Caching配置和使用,需要的朋友可以參考下2016-06-06ASP.NET MVC4入門教程(四):添加一個(gè)模型
本文主要介紹在MVC4中如何添加一個(gè)模型(Model),這個(gè)類負(fù)責(zé)和數(shù)據(jù)庫(kù)交互,主要處理一些增刪改查的操作。2016-04-04