扯淡

这是一款高质量的.NET C#数据库访问框架(ORM)。查询接口借鉴 Linq。借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接查询、分组查询、聚合查询、插入数据、批量删除和更新等操作。

其实,早在两个月前,Chloe 就已经支持 Oracle 数据库了,只是LZ个人平时用 Oracle 不多,Oracle 较其他数据库稍微特别,因此,并没有及时发布,生怕 bug 连连。经过好几个月的沉淀,除了支持 Oracle 外,对框架内部代码结构也做了不少的调整,现在,实体也支持继承父类或接口,更加可喜可贺的是也支持了存储过程,包括 output 参数以及 Oracle 的 RefCurcor 返回结果集。与此同时,方便对 Chloe 的感兴趣的同学学习框架,官网也正式上线了。

导航

Chloe.ORM

事前准备

实体:

public enum Gender
{
    Man = 1,
    Woman
}

[Table("Users")]
public class User
{
    [Sequence("USERS_AUTOID")]
    public int Id { get; set; }
    public string Name { get; set; }
    public Gender? Gender { get; set; }
    public int? Age { get; set; }
    public int? CityId { get; set; }
    public DateTime? OpTime { get; set; }
}

public class City
{
    [Column(IsPrimaryKey = true)]
    public int Id { get; set; }
    public string Name { get; set; }
    public int ProvinceId { get; set; }
}

public class Province
{
    [Column(IsPrimaryKey = true)]
    public int Id { get; set; }
    public string Name { get; set; }
}

因为框架本身需要与具体的数据库驱动解耦,所以 OracleContext 构造函数需要一个 IDbConnectionFactory 的参数,IDbConnectionFactory 接口只有一个 CreateConnection() 方法,必须先建个类,实现 CreateConnection 方法:

public class OracleConnectionFactory : IDbConnectionFactory
{
    string _connString = null;
    public OracleConnectionFactory(string connString)
    {
        this._connString = connString;
    }
    public IDbConnection CreateConnection()
    {
        OracleConnection oracleConnection = new OracleConnection(this._connString);
        OracleConnectionDecorator conn = new OracleConnectionDecorator(oracleConnection);
        return conn;
    }
}

由于我用的是 Oracle.ManagedDataAccess 数据库驱动,OracleConnection 创建的 DbCommand 默认是以顺序方式绑定参数,所以,上述例子使用了装饰者模式对 OracleConnection 封装了一遍,主要就是修改 DbCommand 参数绑定方式。OracleConnectionDecorator 的定义在官网API文档和 Github 上的 demo 中都有,在这就不贴了,不然太占篇幅。

创建一个 DbContext:

string connString = "Your connection string";
OracleContext context = new OracleContext(new OracleConnectionFactory(connString));

再创建一个 IQuery<T>:

IQuery<User> q = context.Query<User>();

查询数据

基本查询

IQuery<User> q = context.Query<User>();

q.Where(a => a.Id == 1).FirstOrDefault();
/*
 * SELECT "USERS"."ID" AS "ID","USERS"."NAME" AS "NAME","USERS"."GENDER" AS "GENDER","USERS"."AGE" AS "AGE","USERS"."CITYID" AS "CITYID","USERS"."OPTIME" AS "OPTIME" FROM "USERS" "USERS" WHERE ("USERS"."ID" = 1 AND ROWNUM < 2)
 */

//可以选取指定的字段,支持返回匿名类型,也可以返回自定义类型
q.Where(a => a.Id == 1).Select(a => new { a.Id, a.Name }).FirstOrDefault();
/*
 * SELECT "USERS"."ID" AS "ID","USERS"."NAME" AS "NAME" FROM "USERS" "USERS" WHERE ("USERS"."ID" = 1 AND ROWNUM < 2)
 */

//分页
q.Where(a => a.Id > 0).OrderBy(a => a.Age).TakePage(1, 20).ToList();
/*
 * SELECT "T"."ID" AS "ID","T"."NAME" AS "NAME","T"."GENDER" AS "GENDER","T"."AGE" AS "AGE","T"."CITYID" AS "CITYID","T"."OPTIME" AS "OPTIME" FROM (SELECT "TTAKE"."ID" AS "ID","TTAKE"."NAME" AS "NAME","TTAKE"."GENDER" AS "GENDER","TTAKE"."AGE" AS "AGE","TTAKE"."CITYID" AS "CITYID","TTAKE"."OPTIME" AS "OPTIME",ROWNUM AS "ROW_NUMBER_0" FROM (SELECT "USERS"."ID" AS "ID","USERS"."NAME" AS "NAME","USERS"."GENDER" AS "GENDER","USERS"."AGE" AS "AGE","USERS"."CITYID" AS "CITYID","USERS"."OPTIME" AS "OPTIME" FROM "USERS" "USERS" WHERE "USERS"."ID" > 0 ORDER BY "USERS"."AGE" ASC) "TTAKE" WHERE ROWNUM < 21) "T" WHERE "T"."ROW_NUMBER_0" > 0
 */

连接查询

IQuery<User> users = context.Query<User>();
IQuery<City> cities = context.Query<City>();
IQuery<Province> provinces = context.Query<Province>();

//建立连接
IJoiningQuery<User, City> user_city = users.InnerJoin(cities, (user, city) => user.CityId == city.Id);
IJoiningQuery<User, City, Province> user_city_province = user_city.InnerJoin(provinces, (user, city, province) => city.ProvinceId == province.Id);

//查出一个用户及其隶属的城市和省份的所有信息,同样支持返回匿名类型,也可以返回自定义类型
var view = user_city_province.Select((user, city, province) => new { User = user, City = city, Province = province }).Where(a => a.User.Id == 1).ToList();
/*
 * SELECT "USERS"."ID" AS "ID","USERS"."NAME" AS "NAME","USERS"."GENDER" AS "GENDER","USERS"."AGE" AS "AGE","USERS"."CITYID" AS "CITYID","USERS"."OPTIME" AS "OPTIME","CITY"."ID" AS "ID0","CITY"."NAME" AS "NAME0","CITY"."PROVINCEID" AS "PROVINCEID","PROVINCE"."ID" AS "ID1","PROVINCE"."NAME" AS "NAME1" FROM "USERS" "USERS" INNER JOIN "CITY" "CITY" ON "USERS"."CITYID" = "CITY"."ID" INNER JOIN "PROVINCE" "PROVINCE" ON "CITY"."PROVINCEID" = "PROVINCE"."ID" WHERE "USERS"."ID" = 1
 */

//也可以只获取指定的字段信息:UserId,UserName,CityName,ProvinceName,这时,生成的 sql 只包含指定的字段
user_city_province.Select((user, city, province) => new { UserId = user.Id, UserName = user.Name, CityName = city.Name, ProvinceName = province.Name }).Where(a => a.UserId == 1).ToList();
/*
 * SELECT "USERS"."ID" AS "USERID","USERS"."NAME" AS "USERNAME","CITY"."NAME" AS "CITYNAME","PROVINCE"."NAME" AS "PROVINCENAME" FROM "USERS" "USERS" INNER JOIN "CITY" "CITY" ON "USERS"."CITYID" = "CITY"."ID" INNER JOIN "PROVINCE" "PROVINCE" ON "CITY"."PROVINCEID" = "PROVINCE"."ID" WHERE "USERS"."ID" = 1
 */

聚合函数

Chloe 的聚合查询拥有和 linq 差不多的接口,基本是一看就明白。

IQuery<User> q = context.Query<User>();

q.Select(a => AggregateFunctions.Count()).First();
/*
 * SELECT COUNT(1) AS "C" FROM "USERS" "USERS" WHERE ROWNUM < 2
 */

q.Select(a => new { Count = AggregateFunctions.Count(), LongCount = AggregateFunctions.LongCount(), Sum = AggregateFunctions.Sum(a.Age), Max = AggregateFunctions.Max(a.Age), Min = AggregateFunctions.Min(a.Age), Average = AggregateFunctions.Average(a.Age) }).First();
/*
 * SELECT COUNT(1) AS "COUNT",COUNT(1) AS "LONGCOUNT",SUM("USERS"."AGE") AS "SUM",MAX("USERS"."AGE") AS "MAX",MIN("USERS"."AGE") AS "MIN",AVG("USERS"."AGE") AS "AVERAGE" FROM "USERS" "USERS" WHERE ROWNUM < 2
 */

var count = q.Count();
/*
 * SELECT COUNT(1) AS "C" FROM "USERS" "USERS"
 */

var longCount = q.LongCount();
/*
 * SELECT COUNT(1) AS "C" FROM "USERS" "USERS"
 */

var sum = q.Sum(a => a.Age);
/*
 * SELECT SUM("USERS"."AGE") AS "C" FROM "USERS" "USERS"
 */

var max = q.Max(a => a.Age);
/*
 * SELECT MAX("USERS"."AGE") AS "C" FROM "USERS" "USERS"
 */

var min = q.Min(a => a.Age);
/*
 * SELECT MIN("USERS"."AGE") AS "C" FROM "USERS" "USERS"
 */

var avg = q.Average(a => a.Age);
/*
 * SELECT AVG("USERS"."AGE") AS "C" FROM "USERS" "USERS"
 */

分组查询

Chloe 的分组查询功能,可以像写 sql 一样支持 Having 和 Select。

IQuery<User> q = context.Query<User>();

IGroupingQuery<User> g = q.Where(a => a.Id > 0).GroupBy(a => a.Age);

g = g.Having(a => a.Age > 1 && AggregateFunctions.Count() > 0);

g.Select(a => new { a.Age, Count = AggregateFunctions.Count(), Sum = AggregateFunctions.Sum(a.Age), Max = AggregateFunctions.Max(a.Age), Min = AggregateFunctions.Min(a.Age), Avg = AggregateFunctions.Average(a.Age) }).ToList();
/*
 * SELECT "USERS"."AGE" AS "AGE",COUNT(1) AS "COUNT",SUM("USERS"."AGE") AS "SUM",MAX("USERS"."AGE") AS "MAX",MIN("USERS"."AGE") AS "MIN",AVG("USERS"."AGE") AS "AVG" FROM "USERS" "USERS" WHERE "USERS"."ID" > 0 GROUP BY "USERS"."AGE" HAVING ("USERS"."AGE" > 1 AND COUNT(1) > 0)
 */

插入数据

方式1

以 lambda 表达式树的方式插入:

此种方式插入的好处是,可以指定列插入,就像写 sql 一样简单。
同时,该方式插入返回表主键值。如果实体主键是自增列(序列),返回值就会是自增值。

/* User 实体打了序列标签,会自动获取序列值。返回主键 Id */
int id = (int)context.Insert<User>(() => new User() { Name = "lu", Age = 18, Gender = Gender.Man, CityId = 1, OpTime = DateTime.Now });
/*
 * SELECT "USERS_AUTOID"."NEXTVAL" FROM "DUAL"
 * Int32 :P_0 = 14;
   INSERT INTO "USERS"("NAME","AGE","GENDER","CITYID","OPTIME","ID") VALUES(N'lu',18,1,1,SYSTIMESTAMP,:P_0)
 */

方式2

以实体的方式插入:

该方式插入,如果一个实体存在自增列,会自动将自增列设置到相应的属性上。

User user = new User();
user.Name = "lu";
user.Age = 18;
user.Gender = Gender.Man;
user.CityId = 1;
user.OpTime = DateTime.Now;

//会自动将自增 Id 设置到 user 的 Id 属性上
user = context.Insert(user);
/*
 * SELECT "USERS_AUTOID"."NEXTVAL" FROM "DUAL"
 * Int32 :P_0 = 15;
   String :P_1 = 'lu';
   Int32 :P_2 = 1;
   Int32 :P_3 = 18;
   DateTime :P_4 = '2016/9/5 9:16:59';
   INSERT INTO "USERS"("ID","NAME","GENDER","AGE","CITYID","OPTIME") VALUES(:P_0,:P_1,:P_2,:P_3,:P_2,:P_4)
 */

更新数据

方式1

以 lambda 表达式树的方式更新:

该方式解决的问题是:1.指定列更新;2.批量更新;3.支持类似 Age=Age + 100 这样更新字段。

context.Update<User>(a => a.Id == 1, a => new User() { Name = a.Name, Age = a.Age + 100, Gender = Gender.Man, OpTime = DateTime.Now });
/*
 * UPDATE "USERS" SET "NAME"="USERS"."NAME","AGE"=("USERS"."AGE" + 100),"GENDER"=1,"OPTIME"=SYSTIMESTAMP WHERE "USERS"."ID" = 1
 */

//批量更新
//给所有女性年轻 10 岁
context.Update<User>(a => a.Gender == Gender.Woman, a => new User() { Age = a.Age - 10, OpTime = DateTime.Now });
/*
 * UPDATE "USERS" SET "AGE"=("USERS"."AGE" - 10),"OPTIME"=SYSTIMESTAMP WHERE "USERS"."GENDER" = 2
 */

方式2

以实体的方式更新:

User user = new User();
user.Id = 1;
user.Name = "lu";
user.Age = 28;
user.Gender = Gender.Man;
user.OpTime = DateTime.Now;

context.Update(user); //会更新所有映射的字段
/*
 * String :P_0 = 'lu';
   Int32 :P_1 = 1;
   Int32 :P_2 = 28;
   Nullable<Int32> :P_3 = NULL;
   DateTime :P_4 = '2016/9/5 9:20:07';
   UPDATE "USERS" SET "NAME"=:P_0,"GENDER"=:P_1,"AGE"=:P_2,"CITYID"=:P_3,"OPTIME"=:P_4 WHERE "USERS"."ID" = :P_1
 */

/*
 * 支持只更新属性值已变的属性
 */

context.TrackEntity(user);//在上下文中跟踪实体
user.Name = user.Name + "";
context.Update(user);//这时只会更新被修改的字段
/*
 * String :P_0 = 'lu1';
   Int32 :P_1 = 1;
   UPDATE "USERS" SET "NAME"=:P_0 WHERE "USERS"."ID" = :P_1
 */

删除数据

方式1

以 lambda 表达式树的方式删除:

context.Delete<User>(a => a.Id == 1);
/*
 * DELETE FROM "USERS" WHERE "USERS"."ID" = 1
 */

//批量删除
//删除所有不男不女的用户
context.Delete<User>(a => a.Gender == null);
/*
 * DELETE FROM "USERS" WHERE "USERS"."GENDER" IS NULL
 */

方式2

以实体的方式删除:

User user = new User();
user.Id = 1;
context.Delete(user);
/*
 * Int32 :P_0 = 1;
   DELETE FROM "USERS" WHERE "USERS"."ID" = :P_0
 */

存储过程

通过存储过程获取一个 User 信息:

Oracle 数据库中,如果一个存储过程需要返回结果集,需要借助 RefCursor output 参数特性。用法如下:

/* 必须先自定义 RefCursor 参数 */
OracleParameter p_cur = new OracleParameter();
p_cur.ParameterName = "p_cur";
p_cur.OracleDbType = OracleDbType.RefCursor;
p_cur.Direction = ParameterDirection.Output;

DbParam refCursorParam = new DbParam();
/* 将自定义 RefCursor 参数设置到 DbParam 的 ExplicitParameter 属性 */
refCursorParam.ExplicitParameter = p_cur;

DbParam id = new DbParam("id", 1);
User user = context.SqlQuery<User>("Proc_GetUser", CommandType.StoredProcedure, id,refCursorParam).FirstOrDefault();

通过存储过程的 output 参数获取一个用户的 name:

DbParam id = new DbParam("id", 1);
DbParam outputName = new DbParam("name", null, typeof(string)) { Direction = ParamDirection.Output };
context.Session.ExecuteNonQuery("Proc_GetUserName", CommandType.StoredProcedure, id, outputName);

支持函数

IQuery<User> q = context.Query<User>();

var space = new char[] { ' ' };

DateTime startTime = DateTime.Now;
DateTime endTime = startTime.AddDays(1);
var ret = q.Select(a => new
     {
         Id = a.Id,

         String_Length = (int?)a.Name.Length,//LENGTH("USERS"."NAME")
         Substring = a.Name.Substring(0),//SUBSTR("USERS"."NAME",0 + 1,LENGTH("USERS"."NAME"))
         Substring1 = a.Name.Substring(1),//SUBSTR("USERS"."NAME",1 + 1,LENGTH("USERS"."NAME"))
         Substring1_2 = a.Name.Substring(1, 2),//SUBSTR("USERS"."NAME",1 + 1,2)
         ToLower = a.Name.ToLower(),//LOWER("USERS"."NAME")
         ToUpper = a.Name.ToUpper(),//UPPER("USERS"."NAME")
         IsNullOrEmpty = string.IsNullOrEmpty(a.Name),//too long
         Contains = (bool?)a.Name.Contains("s"),//
         Trim = a.Name.Trim(),//TRIM("USERS"."NAME")
         TrimStart = a.Name.TrimStart(space),//LTRIM("USERS"."NAME")
         TrimEnd = a.Name.TrimEnd(space),//RTRIM("USERS"."NAME")
         StartsWith = (bool?)a.Name.StartsWith("s"),//
         EndsWith = (bool?)a.Name.EndsWith("s"),//

         /* oracle is not supported DbFunctions.Diffxx. */
         //DiffYears = DbFunctions.DiffYears(startTime, endTime),//
         //DiffMonths = DbFunctions.DiffMonths(startTime, endTime),//
         //DiffDays = DbFunctions.DiffDays(startTime, endTime),//
         //DiffHours = DbFunctions.DiffHours(startTime, endTime),//
         //DiffMinutes = DbFunctions.DiffMinutes(startTime, endTime),//
         //DiffSeconds = DbFunctions.DiffSeconds(startTime, endTime),//
         //DiffMilliseconds = DbFunctions.DiffMilliseconds(startTime, endTime),//
         //DiffMicroseconds = DbFunctions.DiffMicroseconds(startTime, endTime),//

         /* ((CAST(:P_0 AS DATE)-CAST(:P_1 AS DATE)) * 86400000 + CAST(TO_CHAR(CAST(:P_0 AS TIMESTAMP),'ff3') AS NUMBER) - CAST(TO_CHAR(CAST(:P_1 AS TIMESTAMP),'ff3') AS NUMBER)) / 86400000 */
         SubtractTotalDays = endTime.Subtract(startTime).TotalDays,//
         SubtractTotalHours = endTime.Subtract(startTime).TotalHours,//...
         SubtractTotalMinutes = endTime.Subtract(startTime).TotalMinutes,//...
         SubtractTotalSeconds = endTime.Subtract(startTime).TotalSeconds,//...
         SubtractTotalMilliseconds = endTime.Subtract(startTime).TotalMilliseconds,//...

         AddYears = startTime.AddYears(1),//ADD_MONTHS(:P_0,12 * 1)
         AddMonths = startTime.AddMonths(1),//ADD_MONTHS(:P_0,1)
         AddDays = startTime.AddDays(1),//(:P_0 + 1)
         AddHours = startTime.AddHours(1),//(:P_0 + NUMTODSINTERVAL(1,'HOUR'))
         AddMinutes = startTime.AddMinutes(2),//(:P_0 + NUMTODSINTERVAL(2,'MINUTE'))
         AddSeconds = startTime.AddSeconds(120),//(:P_0 + NUMTODSINTERVAL(120,'SECOND'))
         //AddMilliseconds = startTime.AddMilliseconds(20000),//不支持

         Now = DateTime.Now,//SYSTIMESTAMP
         UtcNow = DateTime.UtcNow,//SYS_EXTRACT_UTC(SYSTIMESTAMP)
         Today = DateTime.Today,//TRUNC(SYSDATE,'DD')
         Date = DateTime.Now.Date,//TRUNC(SYSTIMESTAMP,'DD')
         Year = DateTime.Now.Year,//CAST(TO_CHAR(SYSTIMESTAMP,'yyyy') AS NUMBER)
         Month = DateTime.Now.Month,//CAST(TO_CHAR(SYSTIMESTAMP,'mm') AS NUMBER)
         Day = DateTime.Now.Day,//CAST(TO_CHAR(SYSTIMESTAMP,'dd') AS NUMBER)
         Hour = DateTime.Now.Hour,//CAST(TO_CHAR(SYSTIMESTAMP,'hh24') AS NUMBER)
         Minute = DateTime.Now.Minute,//CAST(TO_CHAR(SYSTIMESTAMP,'mi') AS NUMBER)
         Second = DateTime.Now.Second,//CAST(TO_CHAR(SYSTIMESTAMP,'ss') AS NUMBER)
         Millisecond = DateTime.Now.Millisecond,//CAST(TO_CHAR(SYSTIMESTAMP,'ff3') AS NUMBER)
         DayOfWeek = DateTime.Now.DayOfWeek,//(CAST(TO_CHAR(SYSTIMESTAMP,'D') AS NUMBER) - 1)

         Int_Parse = "),//CAST(N'1' AS NUMBER)
         Int16_Parse = Int16.Parse(""),//CAST(N'11' AS NUMBER)
         Long_Parse = "),//CAST(N'2' AS NUMBER)
         Double_Parse = "),//CAST(N'3' AS BINARY_DOUBLE)
         Float_Parse = "),//CAST(N'4' AS BINARY_FLOAT)
         Decimal_Parse = "),//CAST(N'5' AS NUMBER)
         //Guid_Parse = Guid.Parse("D544BC4C-739E-4CD3-A3D3-7BF803FCE179"),//不支持

         Bool_Parse = "),//
         DateTime_Parse = DateTime.Parse("1992-1-16"),//TO_TIMESTAMP(N'1992-1-16','yyyy-mm-dd hh24:mi:ssxff')

         B = a.Age == null ? false : a.Age > 1,
     }).ToList();

坎坎坷坷

支持 Oracle,一开始我是拒(畏)绝(惧)的,这货太奇葩了- -。后来想想,反正迟早都得要支持,干脆把它给干了吧,免得“夜长梦多”!不过 Oracle 是真奇葩,烦!比如,Oracle 不能直接在存储过程里直接执行 Select sql 返回结果集,必须得依赖它那个神马 RefCurcor 参数,这个我真的万万没想到,后来一位园友提醒了才留意这个特性! 再一个,Oracle 不支持 bool 类型,Oracle.ManagedDataAccess 这个驱动的 DataReader 也不支持 GetBoolean 方法,同时 Oracle.ManagedDataAccess 创建的 DbCommand 默认是是以顺序方式绑定参数,因此,又不得不对 DataReader 和 DbCommand 包装一遍才能用。如果真的要细数起来,Oracle 的糟点连起来估计能绕地球一圈!

结语

把 Oracle 给支持了,心中的石头也终于落下,生活轻松了许多。作为众多 ORM 中为数不多能支持 Oracle 的一枚成员,感兴趣的可以关注一波。或许,Chloe 真能给你带来不一样的感觉!更多详细用法敬请参照官网API文档。

技术教程或心得我倒不是很擅长写,我只想把日常开发的一些干货分享给大家,您的推荐是我分享的最大动力。如果觉得 Chloe 这个开源项目不错,望大家给个赞,也可以上 Github 关注或收藏(star)一下,以便能及时收到更新通知。同时,Chloe 官网以及基于 NFine 改造的后台后续也会放出,有期待的同学可以点个关注,也欢迎广大C#同胞入群交流,畅谈.NET复兴大计。最后,感谢大家阅读至此!

Chloe.ORM 完全开源,遵循 Apache2.0 协议,托管于 GitHub,地址:https://github.com/shuxinqin/Chloe

官网:http://www.52chloe.com
官网后台:http://www.52chloe.com:82

高品质开源工具Chloe.ORM:支持存储过程与Oracle的更多相关文章

  1. [转]高品质开源工具Chloe.ORM:支持存储过程与Oracle

    本文转自:http://www.cnblogs.com/so9527/p/6131177.html 扯淡 这是一款高质量的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq.借助 lamb ...

  2. C#开发的高性能EXCEL导入、导出工具DataPie(支持MSSQL、ORACLE、ACCESS,附源码下载地址)[转]

    转自:http://www.cnblogs.com/yfl8910/archive/2012/05/19/2509194.html 作为财务数据核算人员,面对大量的业务与财务数据,借助于传统的EXCE ...

  3. [开源].NET数据库访问框架Chloe.ORM

    扯淡 13年毕业之际,进入第一家公司实习,接触了 EntityFramework,当时就觉得这东西太牛了,访问数据库都可以做得这么轻松.优雅!毕竟那时还年轻,没见过世面.工作之前为了拿个实习机会混个工 ...

  4. [开源]无sql之旅-Chloe.ORM之增删查改

    扯淡 这是一款轻量.高效的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq(但不支持 Linq).借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接查询.分组查询. ...

  5. 轻量级ORM框架Dapper应用六:Dapper支持存储过程

    在Entity Framework中讲解了EF如何支持存储过程,同样,Dapper也支持存储过程,只需要在Query()方法的CommandType中标记使用的是存储过程就可以了.在Users表上面创 ...

  6. 类EF框架Chloe.ORM升级:只为更完美

    扯淡 Chloe.ORM:一款轻量.高效的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq(但不支持 Linq).借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接 ...

  7. GitHub 开源工具整理

    技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应用,关注软件开发领域 ...

  8. 开源的.Net ORM微型框架SuperHelper

    SuperHelper——灵活通用的.开源的.Net ORM微型框架 SuperHelper是博主利用业余时间编写的一个ORM微型框架,除了可以提高开发效率,与其它ORM框架相比,博主更加喜欢Supe ...

  9. SuperHelper——灵活通用的、开源的.Net ORM微型框架

    SuperHelper是博主利用业余时间编写的一个ORM微型框架,除了可以提高开发效率,与其它ORM框架相比,博主更加喜欢SuperHelper的使用简单.适用范围广的特点. 简介 SuperHelp ...

随机推荐

  1. SpingMVC 核心技术帮助文档

    声明:本篇文档主要是用于参考帮助文档,没有实例,但几乎包含了SpringMVC 4.2版本的所有核心技术,当前最新版本是4.3,4.2的版本已经经是很新的了,所以非常值得大家一读,对于读完这篇文档感觉 ...

  2. 原生JS实现全屏切换以及导航栏滑动隐藏及显示——重构前

    思路分析: 向后滚动鼠标滚轮,页面向下全屏切换:向前滚动滚轮,页面向上全屏切换.切换过程为动画效果. 第一屏时,导航栏固定在页面顶部,切换到第二屏时,导航条向左滑动隐藏.切换回第一屏时,导航栏向右滑动 ...

  3. 简单封装分页功能pageView.js

    分页是一个很简单,通用的功能.作为一个有经验的前端开发人员,有义务把代码中类似这样公共的基础性的东西抽象出来,一来是改善代码的整体质量,更重要的是为了将来做类似的功能或者类似的项目,能减少不必要的重复 ...

  4. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)

    前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...

  5. [jQuery]jQuery DataTables插件自定义Ajax分页实现

    前言 昨天在博客园的博问上帮一位园友解决了一个问题,我觉得有必要记录一下,万一有人也遇上了呢. 问题描述 园友是做前端的,产品经理要求他使用jQuery DataTables插件显示一个列表,要实现分 ...

  6. C# - 多线程 之 锁系统

    lock 关键字, Monitor 监控器, using System.Threading: // 提供同步访问对象的机制. public static class Monitor { public ...

  7. 模型浏览器【Model Browser】【EF基础系列6】

    We have created our first Entity Data Model for School database in the previous section. The visual ...

  8. 课堂Java小程序(加减乘除与验证码)

    一.编写一个程序,用户输入两个数,求出其加减乘除,并用消息框 显示计算结果. 1.设计思想:从键盘输入两个数字和运算符,然后计算.将输入的数字及运算符由字符型转换为整型,再用if判断输入的运算符,根据 ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 5.轮询通讯模式开发及注意事项。附:网友制作的类库说明(CHM)

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  10. IoC组件~Autofac将多实现一次注入,根据别名Resove实例

    回到目录 对于IoC容器来说,性能最好的莫过于Autofac了,而对于灵活度来说,它也是值得称赞的,为了考虑系统的性能,我们经常是在系统初始化于将所有依赖注册到容器里,当需要于根据别名把实现拿出来,然 ...