简介

FreeSql 是一个功能强大的 .NETStandard 库,用于对象关系映射程序(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.6.1+。

定义

IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite,
@"Data Source=|DataDirectory|/test.db;Pooling=true;Max Pool Size=10")
.UseAutoSyncStructure(true) //自动同步实体结构到数据库
.Build();

入门篇

查询

1、查询一条

fsql.Select<Xxx>.Where(a => a.Id == 1).First();

2、分页:第1页,每页20条

fsql.Select<Xxx>.Page(1, 20).ToList();

细节说明:SqlServer 2012 以前的版本,使用 row_number 分页;SqlServer 2012+ 版本,使用最新的 fetch next rows 分页;

3、IN

fsql.Select<Xxx>.Where(a => new { 1,2,3 }.Contains(a.Id)).ToList();

4、联表

fsql.Select<Xxx>.LeftJoin<Yyy>((a, b) => a.YyyId == b.Id).ToList();

5、Exists子表

fsql.Select<Xxx>.Where(a => fsql.Select<Yyy>(b => b.Id == a.YyyId).Any()).ToList();

6、GroupBy & Having

fsql.Select<Xxx>.GroupBy(a => new { a.CategoryId }).Having(a => a.Count > 2).ToList(a => new { a.Key, a.Count() });

7、指定字段查询

fsql.Select<Xxx>.Limit(10).ToList(a => a.Id);

fsql.Select<Xxx>.Limit(10).ToList(a => new { a.Id, a.Name });

fsql.Select<Xxx>.Limit(10).ToList(a => new Dto());

8、执行SQL返回实体

fsql.Ado.Query<Xxx>("select * from xxx");

fsql.Ado.Query<(int, string, string)>("select * from xxx");

fsql.Ado.Query<dynamic>("select * from xxx");

插入

1、单条

fsql.Insert<Xxx>().AppendData(new Xxx()).ExecuteAffrows();

2、单条,返回自增值

fsql.Insert<Xxx>().AppendData(new Xxx()).ExecuteIdentity();

3、单条,返回插入的行(SqlServer 的 output 特性)

fsql.Insert<Xxx>().AppendData(new Xxx()).ExecuteInserted();

4、批量

fsql.Insert<Xxx>().AppendData(数组).ExecuteAffrows();

5、批量,返回插入的行(SqlServer 的 output 特性)

fsql.Insert<Xxx>().AppendData(数组).ExecuteInserted();

6、指定列

fsql.Insert<Xxx>().AppendData(new Xxx()).InsertColumns(a => a.Title).ExecuteAffrows();

fsql.Insert<Xxx>().AppendData(new Xxx()).InsertColumns(a => new { a.Id, a.Title}).ExecuteAffrows();

7、忽略列

fsql.Insert<Xxx>().AppendData(new Xxx()).IgnoreColumns(a => a.Title).ExecuteAffrows();

fsql.Insert<Xxx>().AppendData(new Xxx()).IgnoreColumns(a => new { a.Id, a.Title}).ExecuteAffrows();

8、事务

fsql.Insert<Xxx>().AppendData(new Xxx()).WithTransaction(事务对象).ExecuteAffrows();

更新

1、指定列

fsql.Update<Xxx>(1).Set(a => a.CreateTime, DateTime.Now).ExecuteAffrows();

2、累加,set clicks = clicks + 1

fsql.Update<Xxx>(1).Set(a => a.Clicks + 1).ExecuteAffrows();

3、保存

fsql.Update<Xxx>().SetSource(单个实体).ExecuteAffrows();

4、批量保存

fsql.Update<Xxx>().SetSource(数组).ExecuteAffrows();

5、忽略列

fsql.Update<Xxx>().SetSource(数组).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ExecuteAffrows();

6、更新条件

fsql.Update<Xxx>().SetSource(数组).Where(a => a.Clicks > 100).ExecuteAffrows();

7、事务

fsql.Update<Xxx>(1).Set(a => a.Clicks + 1).WithTransaction(事务对象).ExecuteAffrows();

删除

1、dywhere

  • 主键值
  • new[] { 主键值1, 主键值2 }
  • Xxx对象
  • new[] { Xxx对象1, Xxx对象2 }
  • new { id = 1 }
fsql.Delete<Xxx>(new[] { 1, 2 }).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1 OR `Id` = 2) fsql.Delete<Xxx>(new Xxx { Id = 1, Title = "test" }).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1) fsql.Delete<Xxx>(new[] { new Xxx { Id = 1, Title = "test" }, new Xxx { Id = 2, Title = "test" } }).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1 OR `Id` = 2) fsql.Delete<Xxx>(new { id = 1 }).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1)

2、条件

fsql.Delete<Xxx>().Where(a => a.Id == 1).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1) fsql.Delete<Xxx>().Where("id = ?id", new { id = 1 }).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (id = ?id) var item = new Xxx { Id = 1, Title = "newtitle" };
var t7 = fsql.Delete<Xxx>().Where(item).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` = 1) var items = new List<Xxx>();
for (var a = 0; a < 10; a++) items.Add(new Xxx { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
fsql.Delete<Xxx>().Where(items).ExecuteAffrows();
//DELETE FROM `xxx` WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))

3、事务

fsql.Delete<Xxx>().Where(a => a.Id == 1).WithTransaction(事务对象).ExecuteAffrows();

初级篇

表达式

支持功能丰富的表达式函数解析,方便程序员在不了解数据库函数的情况下编写代码。这是 FreeSql 非常特色的功能之一,深入细化函数解析尽量做到满意,所支持的类型基本都可以使用对应的表达式函数,例如 日期、字符串、IN查询、数组(PostgreSQL的数组)、字典(PostgreSQL HStore)等等。

1、查找今天创建的数据

fsql.Delete<Xxx>().Where(a => a.CreateTime.Date == DateTime.Now.Date).ToList();

2、SqlServer 下随机获取记录

fsql.Delete<Xxx>().OrderBy(a => Guid.NewGuid()).Limit(1).ToSql();

4、表达式函数全览

表达式 MySql SqlServer PostgreSQL Oracle 功能说明
a ? b : c case when a then b else c end case when a then b else c end case when a then b else c end case when a then b else c end a成立时取b值,否则取c值
a ?? b ifnull(a, b) isnull(a, b) coalesce(a, b) nvl(a, b) 当a为null时,取b值
数字 + 数字 a + b a + b a + b a + b 数字相加
数字 + 字符串 concat(a, b) cast(a as varchar) + cast(b as varchar) case(a as varchar) + b a+b 字符串相加,a或b任意一个为字符串时
a - b a - b a - b a - b a - b
a * b a * b a * b a * b a * b
a / b a / b a / b a / b a / b
a % b a % b a % b a % b mod(a,b)

等等...

5、数组

表达式 MySql SqlServer PostgreSQL Oracle 功能说明
a.Length - - case when a is null then 0 else array_length(a,1) end - 数组长度
常量数组.Length - - array_length(array[常量数组元素逗号分割],1) - 数组长度
a.Any() - - case when a is null then 0 else array_length(a,1) end > 0 - 数组是否为空
常量数组.Contains(b) b in (常量数组元素逗号分割) b in (常量数组元素逗号分割) b in (常量数组元素逗号分割) b in (常量数组元素逗号分割) IN查询
a.Contains(b) - - a @> array[b] - a数组是否包含b元素
a.Concat(b) - - a + b - 数组相连
a.Count() - - 同 Length - 数组长度

一个细节证明 FreeSql 匠心制作

通用的 in 查询 select.Where(a => new []{ 1,2,3 }.Contains(a.xxx))

假设 xxxs 是 pgsql 的数组字段类型,其实会与上面的 in 查询起冲突,FreeSql 解决了这个矛盾 select.Where(a => a.xxxs.Contains(1))

6、字典 Dictionary<string, string>

表达式 MySql SqlServer PostgreSQL Oracle 功能说明
a.Count - - case when a is null then 0 else array_length(akeys(a),1) end - 字典长度
a.Keys - - akeys(a) - 返回字典所有key数组
a.Values - - avals(a) - 返回字典所有value数组
a.Contains(b) - - a @> b - 字典是否包含b
a.ContainsKey(b) - - a? b - 字典是否包含key
a.Concat(b) - - a + b - 字典相连
a.Count() - - 同 Count - 字典长度

7、JSON JToken/JObject/JArray

表达式 MySql SqlServer PostgreSQL Oracle 功能说明
a.Count - - jsonb_array_length(coalesce(a, '[])) - json数组类型的长度
a.Any() - - jsonb_array_length(coalesce(a, '[])) > 0 - json数组类型,是否为空
a.Contains(b) - - coalesce(a, '{}') @> b::jsonb - json中是否包含b
a.ContainsKey(b) - - coalesce(a, '{}') ? b - json中是否包含键b
a.Concat(b) - - coalesce(a, '{}') + b::jsonb - 连接两个json
Parse(a) - - a::jsonb - 转化字符串为json类型

8、字符串

表达式 MySql SqlServer PostgreSQL Oracle Sqlite
string.Empty '' '' '' ''
string.IsNullOrEmpty(a) (a is null or a = '') (a is null or a = '') (a is null or a = '') (a is null or a = '') (a is null or a = '')
a.CompareTo(b) strcmp(a, b) - case when a = b then 0 when a > b then 1 else -1 end case when a = b then 0 when a > b then 1 else -1 end case when a = b then 0 when a > b then 1 else -1 end
a.Contains('b') a like '%b%' a like '%b%' a ilike'%b%' a like '%b%' a like '%b%'
a.EndsWith('b') a like '%b' a like '%b' a ilike'%b' a like '%b' a like '%b'
a.IndexOf(b) locate(a, b) - 1 locate(a, b) - 1 strpos(a, b) - 1 instr(a, b, 1, 1) - 1 instr(a, b) - 1
a.Length char_length(a) len(a) char_length(a) length(a) length(a)
a.PadLeft(b, c) lpad(a, b, c) - lpad(a, b, c) lpad(a, b, c) lpad(a, b, c)
a.PadRight(b, c) rpad(a, b, c) - rpad(a, b, c) rpad(a, b, c) rpad(a, b, c)
a.Replace(b, c) replace(a, b, c) replace(a, b, c) replace(a, b, c) replace(a, b, c) replace(a, b, c)
a.StartsWith('b') a like 'b%' a like 'b%' a ilike'b%' a like 'b%' a like 'b%'
a.Substring(b, c) substr(a, b, c + 1) substring(a, b, c + 1) substr(a, b, c + 1) substr(a, b, c + 1) substr(a, b, c + 1)
a.ToLower lower(a) lower(a) lower(a) lower(a) lower(a)
a.ToUpper upper(a) upper(a) upper(a) upper(a) upper(a)
a.Trim trim(a) trim(a) trim(a) trim(a) trim(a)
a.TrimEnd rtrim(a) rtrim(a) rtrim(a) rtrim(a) rtrim(a)
a.TrimStart ltrim(a) ltrim(a) ltrim(a) ltrim(a) ltrim(a)

使用字符串函数可能会出现性能瓶颈,虽然不推荐使用,但是作为功能库这也是不可缺少的功能之一。

9、日期

表达式 MySql SqlServer PostgreSQL Oracle
DateTime.Now now() getdate() current_timestamp systimestamp
DateTime.UtcNow utc_timestamp() getutcdate() (current_timestamp at time zone 'UTC') sys_extract_utc(systimestamp)
DateTime.Today curdate convert(char(10),getdate(),120) current_date trunc(systimestamp)
DateTime.MaxValue cast('9999/12/31 23:59:59' as datetime) '9999/12/31 23:59:59' '9999/12/31 23:59:59'::timestamp to_timestamp('9999-12-31 23:59:59','YYYY-MM-DD HH24:MI:SS.FF6')
DateTime.MinValue cast('0001/1/1 0:00:00' as datetime) '1753/1/1 0:00:00' '0001/1/1 0:00:00'::timestamp to_timestamp('0001-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS.FF6')
DateTime.Compare(a, b) a - b a - b extract(epoch from a::timestamp-b::timestamp) extract(day from (a-b))
DateTime.DaysInMonth(a, b) dayofmonth(last_day(concat(a, '-', b, '-1'))) datepart(day, dateadd(day, -1, dateadd(month, 1, cast(a as varchar) + '-' + cast(b as varchar) + '-1'))) extract(day from (a
DateTime.Equals(a, b) a = b a = b a = b a = b
DateTime.IsLeapYear(a) a%4=0 and a%100<>0 or a%400=0 a%4=0 and a%100<>0 or a%400=0 a%4=0 and a%100<>0 or a%400=0 mod(a,4)=0 AND mod(a,100)<>0 OR mod(a,400)=0
DateTime.Parse(a) cast(a as datetime) cast(a as datetime) a::timestamp to_timestamp(a,'YYYY-MM-DD HH24:MI:SS.FF6')
a.Add(b) date_add(a, interval b microsecond) dateadd(millisecond, b / 1000, a) a::timestamp+(b
a.AddDays(b) date_add(a, interval b day) dateadd(day, b, a) a::timestamp+(b
a.AddHours(b) date_add(a, interval b hour) dateadd(hour, b, a) a::timestamp+(b
a.AddMilliseconds(b) date_add(a, interval b*1000 microsecond) dateadd(millisecond, b, a) a::timestamp+(b
a.AddMinutes(b) date_add(a, interval b minute) dateadd(minute, b, a) a::timestamp+(b
a.AddMonths(b) date_add(a, interval b month) dateadd(month, b, a) a::timestamp+(b
a.AddSeconds(b) date_add(a, interval b second) dateadd(second, b, a) a::timestamp+(b
a.AddTicks(b) date_add(a, interval b/10 microsecond) dateadd(millisecond, b / 10000, a) a::timestamp+(b
a.AddYears(b) date_add(a, interval b year) dateadd(year, b, a) a::timestamp+(b
a.Date cast(date_format(a, '%Y-%m-%d') as datetime) convert(char(10),a,120) a::date trunc(a)
a.Day dayofmonth(a) datepart(day, a) extract(day from a::timestamp) cast(to_char(a,'DD') as number)
a.DayOfWeek dayofweek(a) datepart(weekday, a) - 1 extract(dow from a::timestamp) case when to_char(a)='7' then 0 else cast(to_char(a) as number) end
a.DayOfYear dayofyear(a) datepart(dayofyear, a) extract(doy from a::timestamp) cast(to_char(a,'DDD') as number)
a.Hour hour(a) datepart(hour, a) extract(hour from a::timestamp) cast(to_char(a,'HH24') as number)
a.Millisecond floor(microsecond(a) / 1000) datepart(millisecond, a) extract(milliseconds from a::timestamp)-extract(second from a::timestamp)*1000 cast(to_char(a,'FF3') as number)
a.Minute minute(a) datepart(minute, a) extract(minute from a::timestamp) cast(to_char(a,'MI') as number)
a.Month month(a) datepart(month, a) extract(month from a::timestamp) cast(to_char(a,'FF3') as number)
a.Second second(a) datepart(second, a) extract(second from a::timestamp) cast(to_char(a,'SS') as number)
a.Subtract(b) timestampdiff(microsecond, b, a) datediff(millisecond, b, a) * 1000 (extract(epoch from a::timestamp-b::timestamp)*1000000) a - b
a.Ticks timestampdiff(microsecond, '0001-1-1', a) * 10 datediff(millisecond, '1970-1-1', a) * 10000 + 621355968000000000 extract(epoch from a::timestamp)*10000000+621355968000000000 cast(to_char(a,'FF7') as number)
a.TimeOfDay timestampdiff(microsecond, date_format(a, '%Y-%m-%d'), a) '1970-1-1 ' + convert(varchar, a, 14) extract(epoch from a::time)*1000000 a - trunc(a)
a.Year year(a) datepart(year, a) extract(year from a::timestamp)
a.Equals(b) a = b a = b a = b a = b
a.CompareTo(b) a - b a - b a - b a - b
a.ToString() date_format(a, '%Y-%m-%d %H:%i:%s.%f') convert(varchar, a, 121) to_char(a, 'YYYY-MM-DD HH24:MI:SS.US') to_char(a,'YYYY-MM-DD HH24:MI:SS.FF6')

10、时间

表达式 MySql(微秒) SqlServer(秒) PostgreSQL(微秒) Oracle(Interval day(9) to second(7))
TimeSpan.Zero 0 0 - 0微秒
TimeSpan.MaxValue 922337203685477580 922337203685477580 - numtodsinterval(233720368.5477580,'second')
TimeSpan.MinValue -922337203685477580 -922337203685477580 - numtodsinterval(-233720368.5477580,'second')
TimeSpan.Compare(a, b) a - b a - b - extract(day from (a-b))
TimeSpan.Equals(a, b) a = b a = b - a = b
TimeSpan.FromDays(a) a * 1000000 * 60 * 60 * 24 a * 1000000 * 60 * 60 * 24 - numtodsinterval(a*86400,'second')
TimeSpan.FromHours(a) a * 1000000 * 60 * 60 a * 1000000 * 60 * 60 - numtodsinterval(a*3600,'second')
TimeSpan.FromMilliseconds(a) a * 1000 a * 1000 - numtodsinterval(a/1000,'second')
TimeSpan.FromMinutes(a) a * 1000000 * 60 a * 1000000 * 60 - numtodsinterval(a*60,'second')
TimeSpan.FromSeconds(a) a * 1000000 a * 1000000 - numtodsinterval(a,'second')
TimeSpan.FromTicks(a) a / 10 a / 10 - numtodsinterval(a/10000000,'second')
a.Add(b) a + b a + b - a + b
a.Subtract(b) a - b a - b - a - b
a.CompareTo(b) a - b a - b - extract(day from (a-b))
a.Days a div (1000000 * 60 * 60 * 24) a div (1000000 * 60 * 60 * 24) - extract(day from a)
a.Hours a div (1000000 * 60 * 60) mod 24 a div (1000000 * 60 * 60) mod 24 - extract(hour from a)
a.Milliseconds a div 1000 mod 1000 a div 1000 mod 1000 - cast(substr(extract(second from a)-floor(extract(second from a)),2,3) as number)
a.Seconds a div 1000000 mod 60 a div 1000000 mod 60 - extract(second from a)
a.Ticks a * 10 a * 10 - (extract(day from a)86400+extract(hour from a)3600+extract(minute from a)60+extract(second from a))10000000
a.TotalDays a / (1000000 * 60 * 60 * 24) a / (1000000 * 60 * 60 * 24) - extract(day from a)
a.TotalHours a / (1000000 * 60 * 60) a / (1000000 * 60 * 60) - (extract(day from a)*24+extract(hour from a))
a.TotalMilliseconds a / 1000 a / 1000 - (extract(day from a)86400+extract(hour from a)3600+extract(minute from a)60+extract(second from a))1000
a.TotalMinutes a / (1000000 * 60) a / (1000000 * 60) -
a.TotalSeconds a / 1000000 a / 1000000 - (extract(day from a)86400+extract(hour from a)3600+extract(minute from a)*60+extract(second from a))
a.Equals(b) a = b a = b - a = b
a.ToString() cast(a as varchar) cast(a as varchar) - to_char(a)

11、数学函数

表达式 MySql SqlServer PostgreSQL Oracle
Math.Abs(a) abs(a) abs(a) abs(a)
Math.Acos(a) acos(a) acos(a) acos(a) acos(a)
Math.Asin(a) asin(a) asin(a) asin(a) asin(a)
Math.Atan(a) atan(a) atan(a) atan(a) atan(a)
Math.Atan2(a, b) atan2(a, b) atan2(a, b) atan2(a, b) -
Math.Ceiling(a) ceiling(a) ceiling(a) ceiling(a) ceil(a)
Math.Cos(a) cos(a) cos(a) cos(a) cos(a)
Math.Exp(a) exp(a) exp(a) exp(a) exp(a)
Math.Floor(a) floor(a) floor(a) floor(a) floor(a)
Math.Log(a) log(a) log(a) log(a) log(e,a)
Math.Log10(a) log10(a) log10(a) log10(a) log(10,a)
Math.PI(a) 3.1415926535897931 3.1415926535897931 3.1415926535897931 3.1415926535897931
Math.Pow(a, b) pow(a, b) power(a, b) pow(a, b) power(a, b)
Math.Round(a, b) round(a, b) round(a, b) round(a, b) round(a, b)
Math.Sign(a) sign(a) sign(a) sign(a) sign(a)
Math.Sin(a) sin(a) sin(a) sin(a) sin(a)
Math.Sqrt(a) sqrt(a) sqrt(a) sqrt(a) sqrt(a)
Math.Tan(a) tan(a) tan(a) tan(a) tan(a)
Math.Truncate(a) truncate(a, 0) floor(a) trunc(a, 0) trunc(a, 0)

12、类型转换

表达式 MySql SqlServer PostgreSQL Oracle Sqlite
Convert.ToBoolean(a), bool.Parse(a) a not in ('0','false') a not in ('0','false') a::varchar not in ('0','false','f','no') - a not in ('0','false')
Convert.ToByte(a), byte.Parse(a) cast(a as unsigned) cast(a as tinyint) a::int2 cast(a as number) cast(a as int2)
Convert.ToChar(a) substr(cast(a as char),1,1) substring(cast(a as nvarchar),1,1) substr(a::char,1,1) substr(to_char(a),1,1) substr(cast(a as character),1,1)
Convert.ToDateTime(a), DateTime.Parse(a) cast(a as datetime) cast(a as datetime) a::timestamp to_timestamp(a,'YYYY-MM-DD HH24:MI:SS.FF6') datetime(a)
Convert.ToDecimal(a), decimal.Parse(a) cast(a as decimal(36,18)) cast(a as decimal(36,19)) a::numeric cast(a as number) cast(a as decimal(36,18))
Convert.ToDouble(a), double.Parse(a) cast(a as decimal(32,16)) cast(a as decimal(32,16)) a::float8 cast(a as number) cast(a as double)
Convert.ToInt16(a), short.Parse(a) cast(a as signed) cast(a as smallint) a::int2 cast(a as number) cast(a as smallint)
Convert.ToInt32(a), int.Parse(a) cast(a as signed) cast(a as int) a::int4 cast(a as number) cast(a as smallint)
Convert.ToInt64(a), long.Parse(a) cast(a as signed) cast(a as bigint) a::int8 cast(a as number) cast(a as smallint)
Convert.ToSByte(a), sbyte.Parse(a) cast(a as signed) cast(a as tinyint) a::int2 cast(a as number) cast(a as smallint)
Convert.ToString(a) cast(a as decimal(14,7)) cast(a as decimal(14,7)) a::float4 to_char(a) cast(a as character)
Convert.ToSingle(a), float.Parse(a) cast(a as char) cast(a as nvarchar) a::varchar cast(a as number) cast(a as smallint)
Convert.ToUInt16(a), ushort.Parse(a) cast(a as unsigned) cast(a as smallint) a::int2 cast(a as number) cast(a as unsigned)
Convert.ToUInt32(a), uint.Parse(a) cast(a as unsigned) cast(a as int) a::int4 cast(a as number) cast(a as decimal(10,0))
Convert.ToUInt64(a), ulong.Parse(a) cast(a as unsigned) cast(a as bigint) a::int8 cast(a as number) cast(a as decimal(21,0))
Guid.Parse(a) substr(cast(a as char),1,36) cast(a as uniqueidentifier) a::uuid substr(to_char(a),1,36) substr(cast(a as character),1,36)
Guid.NewGuid() - newid() - - -
new Random().NextDouble() rand() rand() random() dbms_random.value random()

CodeFirst

参数选项 说明
IsAutoSyncStructure 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改
IsSyncStructureToLower 转小写同步结构
IsSyncStructureToUpper 转大写同步结构,适用 Oracle
IsConfigEntityFromDbFirst 使用数据库的主键和自增,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql。
IsNoneCommandParameter 不使用命令参数化执行,针对 Insert/Update,调试神器
IsLazyLoading 延时加载导航属性对象,导航属性需要声明 virtual

1、配置实体(特性)

public class Song {
[Column(IsIdentity = true)]
public int Id { get; set; } public string Title { get; set; }
public string Url { get; set; } public virtual ICollection<Tag> Tags { get; set; } [Column(IsVersion = true)]
public long versionRow { get; set; }
}

2、在外部配置实体

fsql.CodeFirst
.ConfigEntity<Song>(a => {
a.Property(b => b.Id).IsIdentity(true);
a.Property(b => b.versionRow).IsVersion(true);
});

DbFirst

1、获取所有数据库

fsql.DbFirst.GetDatabases();
//返回字符串数组, ["cccddd", "test"]

2、获取指定数据库的表信息

fsql.DbFirst.GetTablesByDatabase(fsql.DbFirst.GetDatabases()[0]);
//返回包括表、列详情、主键、唯一键、索引、外键、备注等信息

3、生成实体

new FreeSql.Generator.TemplateGenerator()
.Build(fsql.DbFirst,
@"C:\Users\28810\Desktop\github\FreeSql\Templates\MySql\simple-entity",
//模板目录(事先下载)
@"C:\Users\28810\Desktop\你的目录",
//生成后保存的目录
"cccddd"
//数据库
);

高级篇

Repository 仓储实现

1、单个仓储

var curd = fsql.GetRepository<Xxx, int>();
//curd.Find(1);
var item = curd.Get(1);
curd.Update(item); curd.Insert(item);
curd.Delete(1);
curd.Select.Limit(10).ToList();

工作单元

using (var uow = fsql.CreateUnitOfWork()) {
var songRepos = uow.GetRepository<Song>();
var userRepos = uow.GetRepository<User>(); //上面两个仓储,由同一UnitOfWork uow 创建
//在此执行仓储操作 //这里不受异步方便影响 uow.Commit();
}

局部过滤器 + 数据验证

var topicRepository = fsql.GetGuidRepository<Topic>(a => a.UserId == 1);

之后在使用 topicRepository 操作方法时:

  • 查询/修改/删除时附过滤条件,从而达到不会修改其他用户的数据;
  • 添加时,使用过滤条件验证合法性,若不合法则抛出异常;如以下方法就会报错:
topicRepository.Insert(new Topic { UserId = 2 })

乐观锁

更新实体数据,在并发情况下极容易造成旧数据将新的记录更新。FreeSql 核心部分已经支持乐观锁。

乐观锁的原理,是利用实体某字段,如:long version,更新前先查询数据,此时 version 为 1,更新时产生的 SQL 会附加 where version = 1,当修改失败时(即 Affrows == 0)抛出异常。

每个实体只支持一个乐观锁,在属性前标记特性:[Column(IsVersion = true)] 即可。

无论是使用 FreeSql/FreeSql.Repository/FreeSql.DbContext,每次更新 version 的值都会增加 1

DbContext

dotnet add package FreeSql.DbContext

实现类似 EFCore 使用方法,跟踪对象状态,最终通过 SaveChanges 方法以事务的方式提交整段操作。

using (var ctx = new SongContext()) {
var song = new Song { BigNumber = "1000000000000000000" };
ctx.Songs.Add(song); song.BigNumber = (BigInteger.Parse(song.BigNumber) + 1).ToString();
ctx.Songs.Update(song); var tag = new Tag {
Name = "testaddsublist",
Tags = new[] {
new Tag { Name = "sub1" },
new Tag { Name = "sub2" },
new Tag {
Name = "sub3",
Tags = new[] {
new Tag { Name = "sub3_01" }
}
}
}
};
ctx.Tags.Add(tag); ctx.SaveChanges();
} public class Song {
[Column(IsIdentity = true)]
public int Id { get; set; }
public string BigNumber { get; set; } [Column(IsVersion = true)] //乐观锁
public long versionRow { get; set; }
}
public class Tag {
[Column(IsIdentity = true)]
public int Id { get; set; } public int? Parent_id { get; set; }
public virtual Tag Parent { get; set; } public string Name { get; set; } public virtual ICollection<Tag> Tags { get; set; }
} public class SongContext : DbContext {
public DbSet<Song> Songs { get; set; }
public DbSet<Tag> Tags { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder builder) {
builder.UseFreeSql(fsql);
}
}

导航属性

支持 1对1、1对多、多对1、多对多 的约定导航属性配置,主要用于表达式内部查询;

//OneToOne、ManyToOne
var t0 = fsql.Select<Tag>().Where(a => a.Parent.Parent.Name == "粤语").ToList(); //OneToMany
var t1 = fsql.Select<Tag>().Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10)).ToList(); //ManyToMany
var t2 = fsql.Select<Song>().Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语")).ToList();

不朽篇

读写分离

数据库读写分离,本功能是客户端的读写分离行为,数据库服务器该怎么配置仍然那样配置,不受本功能影响,为了方便描术后面讲到的【读写分离】都是指客户端的功能支持。

各种数据库的读写方案不一,数据库端开启读写分离功能后,读写分离的实现大致分为以下几种:

1、nginx代理,配置繁琐且容易出错;

2、中件间,如MyCat,MySql可以其他数据库怎么办?

3、在client端支持;

FreeSql 实现了第3种方案,支持一个【主库】多个【从库】,【从库】的查询策略为随机方式。

若某【从库】发生故障,将切换到其他可用【从库】,若已全部不可用则使用【主库】查询。

出现故障【从库】被隔离起来间隔性的检查可用状态,以待恢复。

IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, connstr)
.UseSlave("connectionString1", "connectionString2")
//使用从数据库,支持多个
.Build(); select.Where(a => a.Id == 1).ToOne();
//读【从库】(默认) select.Master().WhereId(a => a.Id == 1).ToOne();
//强制读【主库】

下面是以前某项目的测试图片,以供参考,整个过程无感切换和恢复:

分区分表

FreeSql 提供 AsTable 分表的基础方法,GuidRepository 作为分存式仓储将实现了分表与分库(不支持跨服务器分库)的封装。

var logRepository = fsql.GetGuidRepository<Log>(null, oldname => $"{oldname}_{DateTime.Now.ToString("YYYYMM")}");

上面我们得到一个日志仓储按年月分表,使用它 CURD 最终会操作 Log_201903 表。

合并两个仓储,实现分表下的联表查询:

fsql.GetGuidRepository<User>().Select.FromRepository(logRepository)
.LeftJoin<Log>(b => b.UserId == a.Id)
.ToList();

租户

1、按租户字段区分

FreeSql.Repository 现实了 filter(过滤与验证)功能,如:

var topicRepos = fsql.GetGuidRepository<Topic>(t => t.TerantId == 1);

使用 topicRepos 对象进行 CURD 方法:

  • 在查询/修改/删除时附加此条件,从而达到不会修改 TerantId != 1 的数据;
  • 在添加时,使用表达式验证数据的合法性,若不合法则抛出异常;

利用这个功能,我们可以很方便的实现数据分区,达到租户的目的。

2、按租户分表

FreeSql.Repository 现实了 分表功能,如:

var tenantId = 1;
var reposTopic = orm.GetGuidRepository<Topic>(null, oldname => $"{oldname}{tenantId}");

上面我们得到一个仓储按租户分表,使用它 CURD 最终会操作 Topic_1 表。

3、按租户分库

与方案二相同,只是表存储的位置不同。

4、全局设置

通过注入的方式设置仓储类的全局过滤器。

public void ConfigureServices(IServiceCollection services) {
services.AddMvc(); services.AddSingleton<IFreeSql>(Fsql);
services.AddFreeRepository(filter => {
var tenantId = 求出当前租户id;
filter
.Apply<ISoftDelete>("softdelete", a => a.IsDeleted == false)
.Apply<ITenant>("tenant", a => a.TenantId == tenantId)
}, this.GetType().Assembly
);
}

结束语

这次全方位介绍 FreeSql 的功能,只抽取了重要内容发布,由于功能实在太多不方便在一篇文章介绍祥尽。

我个人是非常想展开编写,将每个功能的设计和实现放大来介绍,但还是先希望得到更多人的关注,不然就是一台独角戏了。

gayhub: https://github.com/2881099/FreeSql,肯请献上宝贵的一星,谢谢!

.NETCore 新型 ORM 功能介绍的更多相关文章

  1. ORM 创新解放劳动力 -SqlSugar 新功能介绍

    介绍 SqlSugar是一款 老牌 .NET 开源ORM框架,由果糖大数据科技团队维护和更新 ,Github star数仅次于EF 和 Dapper 优点: 简单易用.功能齐全.高性能.轻量级.服务齐 ...

  2. java基础强化——深入理解java注解(附简单ORM功能实现)

    目录 1.什么是注解 2. 注解的结构以及如何在运行时读取注解 2.1 注解的组成 2.2 注解的类层级结构 2.3 如何在运行时获得注解信息 3.几种元注解介绍 3.1 @Retention 3.2 ...

  3. Autodesk Maya 2019 for Mac(三维动画软件)最新功能介绍

    Autodesk Maya是美国Autodesk公司出品的世界顶级的三维动画软件,应用对象是专业的影视广告,角色动画,电影特技等.Maya功能完善,工作灵活,易学易用,制作效率极高,渲染真实感极强,是 ...

  4. 手写开源ORM框架介绍

    手写开源ORM框架介绍 简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另 ...

  5. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  6. 微信小程序产品定位及功能介绍

    产品定位及功能介绍 微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序注册 注册小程序帐号 在微信公众平台官网首页(mp.weixin.qq ...

  7. 带你走近AngularJS - 基本功能介绍

    带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...

  8. MWeb 1.4 新功能介绍一:引入文件夹到 MWeb 中管理,支持 Octpress、Jekyll 等静态博客拖拽插入图片和实时预览

    之前在 MWeb 中打开非文档库中的 Markdown 文档,如果文档中有引用到本机图片,是没办法在 MWeb 中显示出来和预览的.这是因为 Apple 规定在 Mac App Store(MAS) ...

  9. Joomla软件功能介绍与开源程序大比拼Joomla,wordpress,Drupal哪个好?

    Joomla 软件功能介绍:    Joomla!是一套在国外相当知名的内容管理系统 (Content Management System, CMS),它属于Portal(企业入口网站)类型,顾名思义 ...

随机推荐

  1. 深度学习之 TensorFlow(四):卷积神经网络

    基础概念: 卷积神经网络(CNN):属于人工神经网络的一种,它的权值共享的网络结构显著降低了模型的复杂度,减少了权值的数量.卷积神经网络不像传统的识别算法一样,需要对数据进行特征提取和数据重建,可以直 ...

  2. java原子操作的实现原理--转载

    原文地址:http://www.infoq.com/cn/articles/atomic-operation 1. 引言 原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic ...

  3. Java批量压缩下载

    最近做了一些有关批量压缩下载的功能,网上也找了一些资源,但都不是太全面,所以自己整理一份,已备不时之需. 直接上代码: // 获取项目路径 private static String WEBCLASS ...

  4. Python_跟随目标主机IP变换

    ''' 为了防止黑客攻击或者负载均衡,会经常变换主机,这样同一个域名在不同时间可能会对应不同的IP地址,在这种情况下可以通过 socket模块的gethostbyname()函数来实时获取目标主机的I ...

  5. PHP内核之旅-3.变量

    PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 一.弱类型语言 php是弱类型语言.一个变量可以表示任意数据类型. php强大的一 ...

  6. 【转】IntelliJ IDEA中文乱码问题

    1.首先是编辑器的乱码,这个很好解决,file->settings->appearence里面有个Name设置成支持中文的字 体(这个很重要) 同样还要再settings中的Eidtor- ...

  7. Scrapy爬虫框架第五讲(linux环境)【download middleware用法】

    DOWNLOAD MIDDLEWRE用法详解 通过上面的Scrapy工作架构我们对其功能进行下总结: (1).在Scheduler调度出队列时的Request送给downloader下载前对其进行修改 ...

  8. node.js 使用forever守护进程

    //forever的安装:npm install forever -g//使用forever启动守护进程:forever start 路径/your_app.js//关闭守护进程:forever st ...

  9. 运维监控利器Nagios之:nagios配置详解

    http://ixdba.blog.51cto.com/2895551/752870 一.nagios配置过程详解 1.nagios默认配置文件介绍  nagios安装完毕后,默认的配置文件在/usr ...

  10. 21.app后端如何高效更新内容

    在app的主页或通知栏,经常需要通过api取最新的数据.那么,怎么在这部分上做优化,使获取内容的效率更高呢?在本文中,通过推拉和增量更新,实现了一种高效获取数据的策略. 1.高效更新数据策略在app中 ...