From 8cb7ef2130dda9ce8982d596a6902345ad8dc46f Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Sun, 29 Dec 2019 15:30:43 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20EfCoreFluentApi=20?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=8C=85=EF=BC=8C=E6=8E=A5=E8=BF=91=20efcore?= =?UTF-8?q?=20fluentApi=20=E7=9A=84=E4=BD=BF=E7=94=A8=E4=B9=A0=E6=83=AF?= =?UTF-8?q?=EF=BC=9B#4=20-=20=E5=A2=9E=E5=8A=A0=20ColumnAttribute=20?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=20InsertValueSql=EF=BC=8C=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9A=84=E6=97=B6=E5=80=99=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E7=94=A8=20sql=20=E5=80=BC=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EfCoreColumnFluent.cs | 54 +++ .../EfCoreTableFluent.cs | 312 ++++++++++++++++++ .../FreeSql.Extensions.EfCoreFluentApi.csproj | 34 ++ .../ICodeFirstExtensions.cs | 37 +++ FreeSql.DbContext/FreeSql.DbContext.xml | 7 + .../FreeSql.Tests/FreeSql.Tests.csproj | 1 + FreeSql.Tests/FreeSql.Tests/UnitTest3.cs | 33 ++ FreeSql.sln | 15 + FreeSql/DataAnnotations/ColumnAttribute.cs | 6 + FreeSql/DataAnnotations/ColumnFluent.cs | 12 + FreeSql/DataAnnotations/TableFluent.cs | 50 ++- FreeSql/FreeSql.xml | 311 +++++++++-------- FreeSql/Internal/CommonUtils.cs | 7 +- FreeSql/Internal/UtilsExpressionTree.cs | 5 + 14 files changed, 721 insertions(+), 163 deletions(-) create mode 100644 Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreColumnFluent.cs create mode 100644 Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreTableFluent.cs create mode 100644 Extensions/FreeSql.Extensions.EfCoreFluentApi/FreeSql.Extensions.EfCoreFluentApi.csproj create mode 100644 Extensions/FreeSql.Extensions.EfCoreFluentApi/ICodeFirstExtensions.cs diff --git a/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreColumnFluent.cs b/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreColumnFluent.cs new file mode 100644 index 00000000..2a9cb547 --- /dev/null +++ b/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreColumnFluent.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using FreeSql.DataAnnotations; + +namespace FreeSql.Extensions.EfCoreFluentApi +{ + public class EfCoreColumnFluent + { + ColumnFluent _cf; + + internal EfCoreColumnFluent(ColumnFluent tf) + { + _cf = tf; + } + + public ColumnFluent Help() => _cf; + + public EfCoreColumnFluent HasColumnName(string name) + { + _cf.Name(name); + return this; + } + public EfCoreColumnFluent HashColumnType(string dbtype) + { + _cf.DbType(dbtype); + return this; + } + public EfCoreColumnFluent IsRequired() + { + _cf.IsNullable(false); + return this; + } + public EfCoreColumnFluent HasMaxLength(int length) + { + _cf.StringLength(length); + return this; + } + public EfCoreColumnFluent HasDefaultValueSql(string sqlValue) + { + _cf.InsertValueSql(sqlValue); + return this; + } + public EfCoreColumnFluent IsRowVersion() + { + _cf.IsVersion(true); + return this; + } + //public EfCoreColumnFluent HasConversion(Func stringify, Func parse) + //{ + // return this; + //} + } +} diff --git a/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreTableFluent.cs b/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreTableFluent.cs new file mode 100644 index 00000000..b914fbab --- /dev/null +++ b/Extensions/FreeSql.Extensions.EfCoreFluentApi/EfCoreTableFluent.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using FreeSql.DataAnnotations; + +namespace FreeSql.Extensions.EfCoreFluentApi +{ + public class EfCoreTableFluent + { + TableFluent _tf; + internal EfCoreTableFluent(TableFluent tf) + { + _tf = tf; + } + + public EfCoreTableFluent ToTable(string name) + { + _tf.Name(name); + return this; + } + public EfCoreTableFluent ToView(string name) + { + _tf.DisableSyncStructure(true); + _tf.Name(name); + return this; + } + + public EfCoreColumnFluent Property(Expression> property) => new EfCoreColumnFluent(_tf.Property(property)); + public EfCoreColumnFluent Property(string property) => new EfCoreColumnFluent(_tf.Property(property)); + + public TableFluent Help() => _tf; + + #region HasKey + public EfCoreTableFluent HasKey(Expression> key) + { + if (key?.Body == null) return this; + var exp = key.Body; + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _tf.Property((exp as MemberExpression).Member.Name).IsPrimary(true); + break; + case ExpressionType.New: + foreach (var member in (exp as NewExpression).Members) + _tf.Property(member.Name).IsPrimary(true); + break; + } + return this; + } + #endregion + + #region HasIndex + public HasIndexFluent HasIndex(Expression> index) + { + if (index?.Body == null) throw new ArgumentException("参数错误 index 不能为 null"); + var exp = index.Body; + + var indexName = $"idx_{Guid.NewGuid().ToString("N").Substring(0, 8)}"; + var columns = new List(); + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + columns.Add((exp as MemberExpression).Member.Name); + break; + case ExpressionType.New: + foreach (var member in (exp as NewExpression).Members) + columns.Add(member.Name); + break; + } + _tf.Index(indexName, string.Join(", ", columns), false); + return new HasIndexFluent(_tf, indexName, columns); + } + public class HasIndexFluent + { + TableFluent _modelBuilder; + string _indexName; + List _columns; + bool _isUnique; + + internal HasIndexFluent(TableFluent modelBuilder, string indexName, List columns) + { + _modelBuilder = modelBuilder; + _indexName = indexName; + _columns = columns; + } + public HasIndexFluent IsUnique() + { + _isUnique = true; + _modelBuilder.Index(_indexName, string.Join(", ", _columns), _isUnique); + return this; + } + public HasIndexFluent HasName(string name) + { + _modelBuilder.IndexRemove(_indexName); + _indexName = name; + _modelBuilder.Index(_indexName, string.Join(", ", _columns), _isUnique); + return this; + } + } + #endregion + + #region HasOne + public HasOneFluent HasOne(Expression> one) + { + if (one?.Body == null) throw new ArgumentException("参数错误 one 不能为 null"); + var exp = one.Body; + + var oneProperty = ""; + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + oneProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(oneProperty)) throw new ArgumentException("参数错误 one"); + return new HasOneFluent(_tf, oneProperty); + } + public class HasOneFluent + { + TableFluent _tf; + string _selfProperty; + string _selfBind; + string _withManyProperty; + string _withOneProperty; + string _withOneBind; + + internal HasOneFluent(TableFluent modelBuilder, string oneProperty) + { + _tf = modelBuilder; + _selfProperty = oneProperty; + } + public HasOneFluent WithMany(Expression> many) + { + if (many?.Body == null) throw new ArgumentException("参数错误 many 不能为 null"); + var exp = many.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withManyProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(_withManyProperty)) throw new ArgumentException("参数错误 many"); + if (string.IsNullOrEmpty(_selfBind) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + return this; + } + public HasOneFluent WithOne(Expression> one, Expression> foreignKey) + { + if (one?.Body == null) throw new ArgumentException("参数错误 one 不能为 null"); + var exp = one.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withOneProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(_withOneProperty)) throw new ArgumentException("参数错误 one"); + + if (foreignKey?.Body == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + exp = foreignKey.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withOneBind = (exp as MemberExpression).Member.Name; + _withOneBind = _withOneBind.TrimStart(',', ' '); + break; + case ExpressionType.New: + _withOneBind = ""; + foreach (var member in (exp as NewExpression).Members) + _withOneBind += ", " + member.Name; + _withOneBind = _withOneBind.TrimStart(',', ' '); + break; + } + if (string.IsNullOrEmpty(_withOneBind)) throw new ArgumentException("参数错误 foreignKey"); + if (string.IsNullOrEmpty(_selfBind) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + return this; + } + public HasOneFluent HasForeignKey(Expression> foreignKey) + { + if (foreignKey?.Body == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + var exp = foreignKey.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _selfBind = (exp as MemberExpression).Member.Name; + _selfBind = _selfBind.TrimStart(',', ' '); + break; + case ExpressionType.New: + _selfBind = ""; + foreach (var member in (exp as NewExpression).Members) + _selfBind += ", " + member.Name; + _selfBind = _selfBind.TrimStart(',', ' '); + break; + } + if (string.IsNullOrEmpty(_selfBind)) throw new ArgumentException("参数错误 foreignKey"); + _tf.Navigate(_selfProperty, _selfBind); + if (string.IsNullOrEmpty(_withManyProperty) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + if (string.IsNullOrEmpty(_withOneProperty) == false && string.IsNullOrEmpty(_withOneBind) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + return this; + } + } + #endregion + + #region HasMany + public HasManyFluent HasMany(Expression>> many) + { + if (many?.Body == null) throw new ArgumentException("参数错误 many 不能为 null"); + var exp = many.Body; + + var manyProperty = ""; + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + manyProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(manyProperty)) throw new ArgumentException("参数错误 many"); + return new HasManyFluent(_tf, manyProperty); + } + public class HasManyFluent + { + TableFluent _tf; + string _selfProperty; + string _selfBind; + string _withOneProperty; + string _withManyProperty; + + internal HasManyFluent(TableFluent modelBuilder, string manyProperty) + { + _tf = modelBuilder; + _selfProperty = manyProperty; + } + + public void WithMany(Expression>> many, Type middleType) + { + if (many?.Body == null) throw new ArgumentException("参数错误 many 不能为 null"); + var exp = many.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withManyProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(_withManyProperty)) throw new ArgumentException("参数错误 many"); + + _tf.Navigate(_selfProperty, null, middleType); + _tf.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, null, middleType)); + } + public HasManyFluent WithOne(Expression> one) + { + if (one?.Body == null) throw new ArgumentException("参数错误 one 不能为 null"); + var exp = one.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withOneProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(_withOneProperty)) throw new ArgumentException("参数错误 one"); + + if (string.IsNullOrEmpty(_selfBind) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _selfBind)); + return this; + } + public HasManyFluent HasForeignKey(Expression> foreignKey) + { + if (foreignKey?.Body == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + var exp = foreignKey.Body; + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _selfBind = (exp as MemberExpression).Member.Name; + _selfBind = _selfBind.TrimStart(',', ' '); + break; + case ExpressionType.New: + _selfBind = ""; + foreach (var member in (exp as NewExpression).Members) + _selfBind += ", " + member.Name; + _selfBind = _selfBind.TrimStart(',', ' '); + break; + } + if (string.IsNullOrEmpty(_selfBind)) throw new ArgumentException("参数错误 foreignKey"); + _tf.Navigate(_selfProperty, _selfBind); + if (string.IsNullOrEmpty(_withOneProperty) == false) + _tf.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _selfBind)); + return this; + } + } + #endregion + + public EfCoreTableFluent Ignore(Expression> property) + { + _tf.Property(property).IsIgnore(true); + return this; + } + //public EfCoreTableFluent HasData(T data) => HasData(new[] { data }); + //public EfCoreTableFluent HasData(IEnumerable data) + //{ + // return this; + //} + } +} diff --git a/Extensions/FreeSql.Extensions.EfCoreFluentApi/FreeSql.Extensions.EfCoreFluentApi.csproj b/Extensions/FreeSql.Extensions.EfCoreFluentApi/FreeSql.Extensions.EfCoreFluentApi.csproj new file mode 100644 index 00000000..671086f8 --- /dev/null +++ b/Extensions/FreeSql.Extensions.EfCoreFluentApi/FreeSql.Extensions.EfCoreFluentApi.csproj @@ -0,0 +1,34 @@ + + + + netstandard2.0 + 1.0.0 + true + YeXiangQin + FreeSql 扩展包实现,使用 FluentApi 方式配置实体模型,使用习惯接近 EFCore,方便过渡. + https://github.com/2881099/FreeSql + https://github.com/2881099/FreeSql + git + MIT + FreeSql;ORM;FluentApi + $(AssemblyName) + logo.png + $(AssemblyName) + true + true + + + + + + + + FreeSql.Extensions.EfCoreFluentApi.xml + 3 + + + + + + + diff --git a/Extensions/FreeSql.Extensions.EfCoreFluentApi/ICodeFirstExtensions.cs b/Extensions/FreeSql.Extensions.EfCoreFluentApi/ICodeFirstExtensions.cs new file mode 100644 index 00000000..e6799c46 --- /dev/null +++ b/Extensions/FreeSql.Extensions.EfCoreFluentApi/ICodeFirstExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using FreeSql.DataAnnotations; + +namespace FreeSql.Extensions.EfCoreFluentApi +{ + public static class ICodeFirstExtensions + { + + static void Test() + { + ICodeFirst cf = null; + cf.Entity(eb => + { + eb.Property(b => b.Name).HashColumnType("varchar(50)"); + eb.Property(b => b.FullName).HashColumnType("varchar(60)"); + + eb.HasKey(a => a.Id).HasKey(a => new { a.Id, a.Name }); + eb.HasIndex(a => a.Name).IsUnique().HasName("idx_xxx11"); + }); + } + class TestInfo + { + public int Id { get; set; } + public string Name { get; set; } + public string FullName { get; set; } + public int DefaultValue { get; set; } + } + + public static ICodeFirst Entity(this ICodeFirst codeFirst, Action> modelBuilder) + { + codeFirst.ConfigEntity(tf => modelBuilder(new EfCoreTableFluent(tf))); + return codeFirst; + } + } +} diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index dc0203b8..d9f91124 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -110,6 +110,13 @@ 清空状态数据 + + + 根据 lambda 条件删除数据 + + + + 添加 diff --git a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj index 5d7edd8a..e2398841 100644 --- a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs index 235f8eb4..ee8ef69d 100644 --- a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs +++ b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs @@ -51,6 +51,39 @@ namespace FreeSql.Tests //var getTestByte = g.sqlserver.Select(testByte).First(); //File.WriteAllBytes(@"C:\Users\28810\Desktop\71500003-0ad69400-289e-11ea-85cb-36a54f52ebc0_write.png", getTestByte.pic); + + var ib = new IdleBus(TimeSpan.FromMinutes(10), 2); + ib.Notice += (_, e2) => Trace.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 线程{Thread.CurrentThread.ManagedThreadId}:{e2.Log}"); + + ib.Register("db1", () => new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=3") + .UseAutoSyncStructure(true) + .UseMonitorCommand(cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText)) + .UseLazyLoading(true) + .Build()); + ib.Register("db2", () => new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.Oracle, "user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=3") + .UseAutoSyncStructure(true) + .UseLazyLoading(true) + .UseSyncStructureToUpper(true) + .UseMonitorCommand(cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText)) + .Build()); + ib.Register("db3", () => new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=3") + .UseAutoSyncStructure(true) + .UseLazyLoading(true) + .UseMonitorCommand(cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText)) + .Build()); + //...注入很多个 + + var fsql = ib.Get("db1"); //使用的时候用 Get 方法,不要存其引用关系 + fsql.Select().Limit(10).ToList(); + + fsql = ib.Get("db2"); + fsql.Select().Limit(10).ToList(); + + fsql = ib.Get("db3"); + fsql.Select().Limit(10).ToList(); } class TestByte diff --git a/FreeSql.sln b/FreeSql.sln index 9ac013cd..b4e15e7c 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -72,6 +72,8 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "FreeSql.Tests.VB", "FreeSql EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.MsAccess", "Providers\FreeSql.Provider.MsAccess\FreeSql.Provider.MsAccess.csproj", "{B397A761-F646-41CF-A160-AB6C05DAF2FB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.EfCoreFluentApi", "Extensions\FreeSql.Extensions.EfCoreFluentApi\FreeSql.Extensions.EfCoreFluentApi.csproj", "{773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -430,6 +432,18 @@ Global {B397A761-F646-41CF-A160-AB6C05DAF2FB}.Release|x64.Build.0 = Release|Any CPU {B397A761-F646-41CF-A160-AB6C05DAF2FB}.Release|x86.ActiveCfg = Release|Any CPU {B397A761-F646-41CF-A160-AB6C05DAF2FB}.Release|x86.Build.0 = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|x64.ActiveCfg = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|x64.Build.0 = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|x86.ActiveCfg = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Debug|x86.Build.0 = Debug|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|Any CPU.Build.0 = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|x64.ActiveCfg = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|x64.Build.0 = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|x86.ActiveCfg = Release|Any CPU + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -455,6 +469,7 @@ Global {1674BCE3-EEB4-4003-A2A7-06F51EFAEA23} = {94C8A78D-AA15-47B2-A348-530CD86BFC1B} {6A3A4470-7DF7-411B-AAD7-755D7A9DB5A4} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} {B397A761-F646-41CF-A160-AB6C05DAF2FB} = {2A381C57-2697-427B-9F10-55DA11FD02E4} + {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} diff --git a/FreeSql/DataAnnotations/ColumnAttribute.cs b/FreeSql/DataAnnotations/ColumnAttribute.cs index 8eb5eb1e..96cc37b5 100644 --- a/FreeSql/DataAnnotations/ColumnAttribute.cs +++ b/FreeSql/DataAnnotations/ColumnAttribute.cs @@ -97,5 +97,11 @@ namespace FreeSql.DataAnnotations /// Sqlite -> text /// public int StringLength { get => _StringLength ?? 0; set => _StringLength = value; } + + /// + /// 执行 Insert 方法时使用此值 + /// 注意:如果是 getdate() 这种请可考虑使用 ServerTime,因为它对数据库间作了适配 + /// + public string InsertValueSql { get; set; } } } diff --git a/FreeSql/DataAnnotations/ColumnFluent.cs b/FreeSql/DataAnnotations/ColumnFluent.cs index e5d6e13c..2a897e6f 100644 --- a/FreeSql/DataAnnotations/ColumnFluent.cs +++ b/FreeSql/DataAnnotations/ColumnFluent.cs @@ -158,5 +158,17 @@ namespace FreeSql.DataAnnotations _column.StringLength = value; return this; } + + /// + /// 执行 Insert 方法时使用此值 + /// 注意:如果是 getdate() 这种请可考虑使用 ServerTime,因为它对数据库间作了适配 + /// + /// + /// + public ColumnFluent InsertValueSql(string value) + { + _column.InsertValueSql = value; + return this; + } } } diff --git a/FreeSql/DataAnnotations/TableFluent.cs b/FreeSql/DataAnnotations/TableFluent.cs index e2b199d5..d36e0e2e 100644 --- a/FreeSql/DataAnnotations/TableFluent.cs +++ b/FreeSql/DataAnnotations/TableFluent.cs @@ -9,16 +9,21 @@ namespace FreeSql.DataAnnotations public class TableFluent { - public TableFluent(Type entityType, TableAttribute table) + public TableFluent(ICodeFirst codeFirst, Type entityType, TableAttribute table) { + _codeFirst = codeFirst; _entityType = entityType; _properties = _entityType.GetPropertiesDictIgnoreCase(); _table = table; } + ICodeFirst _codeFirst; Type _entityType; Dictionary _properties; TableAttribute _table; + + public void ConfigEntity(Action> fluent2) => _codeFirst.ConfigEntity(fluent2); + /// /// 数据库表名 /// @@ -52,6 +57,21 @@ namespace FreeSql.DataAnnotations return new ColumnFluent(col); } + /// + /// 导航关系Fluent,与 NavigateAttribute 对应 + /// + /// + /// + /// 多对多关系的中间实体类型 + /// + public TableFluent Navigate(string proto, string bind, Type manyToMany = null) + { + if (_properties.TryGetValue(proto, out var tryProto) == false) throw new KeyNotFoundException($"找不到属性名 {proto}"); + var nav = new NavigateAttribute { Bind = bind, ManyToMany = manyToMany }; + _table._navigates.AddOrUpdate(tryProto.Name, nav, (name, old) => nav); + return this; + } + /// /// 设置实体的索引 /// @@ -70,12 +90,19 @@ namespace FreeSql.DataAnnotations public class TableFluent { - public TableFluent(TableAttribute table) + public TableFluent(ICodeFirst codeFirst, TableAttribute table) { + _codeFirst = codeFirst; + _properties = typeof(T).GetPropertiesDictIgnoreCase(); _table = table; } + ICodeFirst _codeFirst; + Dictionary _properties; TableAttribute _table; + + public void ConfigEntity(Action> fluent2) => _codeFirst.ConfigEntity(fluent2); + /// /// 数据库表名 /// @@ -106,7 +133,12 @@ namespace FreeSql.DataAnnotations { var proto = (column.Body as MemberExpression)?.Member; if (proto == null) throw new FormatException($"错误的表达式格式 {column}"); - var col = _table._columns.GetOrAdd(proto.Name, name => new ColumnAttribute { Name = proto.Name }); + return Property(proto.Name); + } + public ColumnFluent Property(string proto) + { + if (_properties.TryGetValue(proto, out var tryProto)) throw new KeyNotFoundException($"找不到属性名 {proto}"); + var col = _table._columns.GetOrAdd(tryProto.Name, name => new ColumnAttribute { Name = proto }); return new ColumnFluent(col); } @@ -122,8 +154,13 @@ namespace FreeSql.DataAnnotations { var member = (proto.Body as MemberExpression)?.Member; if (member == null) throw new FormatException($"错误的表达式格式 {proto}"); + return Navigate(member.Name, bind, manyToMany); + } + public TableFluent Navigate(string proto, string bind, Type manyToMany = null) + { + if (_properties.TryGetValue(proto, out var tryProto) == false) throw new KeyNotFoundException($"找不到属性名 {proto}"); var nav = new NavigateAttribute { Bind = bind, ManyToMany = manyToMany }; - _table._navigates.AddOrUpdate(member.Name, nav, (name, old) => nav); + _table._navigates.AddOrUpdate(tryProto.Name, nav, (name, old) => nav); return this; } @@ -140,5 +177,10 @@ namespace FreeSql.DataAnnotations _table._indexs.AddOrUpdate(name, idx, (_, __) => idx); return this; } + public TableFluent IndexRemove(string name) + { + _table._indexs.TryRemove(name, out var oldidx); + return this; + } } } diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index d2213da2..a3e67577 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -98,6 +98,12 @@ Sqlite -> text + + + 执行 Insert 方法时使用此值 + 注意:如果是 getdate() 这种请可考虑使用 ServerTime,因为它对数据库间作了适配 + + 数据库列名 @@ -198,6 +204,14 @@ Sqlite -> text + + + 执行 Insert 方法时使用此值 + 注意:如果是 getdate() 这种请可考虑使用 ServerTime,因为它对数据库间作了适配 + + + + 自定义表达式函数解析 @@ -319,6 +333,15 @@ 禁用 CodeFirst 同步结构迁移 + + + 导航关系Fluent,与 NavigateAttribute 对应 + + + + 多对多关系的中间实体类型 + + 设置实体的索引 @@ -2224,6 +2247,137 @@ + + + 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 + + + + + + + + + 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) + + + + + + + 查询 + + + + + + + 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) + + + + + + + + 查询 + + + + + + + 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) + + + + + + + + 查询 + + + + + + + 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) + + + + + + + + 在【主库】执行 + + + + + + + + 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) + + + + + + + + 在【主库】执行 + + + + + + + + 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) + + + + + + + + + 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) + + + + + + + + + + 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new { age = 25 }) + + + + + + 可自定义解析表达式 @@ -3002,160 +3156,3 @@ -unc{``0,System.Boolean}},System.Boolean,System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}})"> - - 使用 or 拼接两个 lambda 表达式 - - - - true 时生效 - - - - - - 将 lambda 表达式取反 - - - - true 时生效 - - - - - 生成类似Mongodb的ObjectId有序、不重复Guid - - - - - - 插入数据 - - - - - - - 插入数据,传入实体 - - - - - - - - 插入数据,传入实体数组 - - - - - - - - 插入数据,传入实体集合 - - - - - - - - 插入数据,传入实体集合 - - - - - - - - 修改数据 - - - - - - - 修改数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 查询数据 - - - - - - - 查询数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 删除数据 - - - - - - - 删除数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 开启事务(不支持异步),60秒未执行完成(可能)被其他线程事务自动提交 - - 事务体 () => {} - - - - 开启事务(不支持异步) - - 超时,未执行完成(可能)被其他线程事务自动提交 - 事务体 () => {} - - - - 开启事务(不支持异步) - - - 事务体 () => {} - 超时,未执行完成(可能)被其他线程事务自动提交 - - - - 数据库访问对象 - - - - - 所有拦截方法都在这里 - - - - - CodeFirst 模式开发相关方法 - - - - - DbFirst 模式开发相关方法 - - - - - 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - - - - diff --git a/FreeSql/Internal/CommonUtils.cs b/FreeSql/Internal/CommonUtils.cs index 0b985d08..b70e0dea 100644 --- a/FreeSql/Internal/CommonUtils.cs +++ b/FreeSql/Internal/CommonUtils.cs @@ -58,7 +58,7 @@ namespace FreeSql.Internal if (entity == null) return _orm.CodeFirst; var type = typeof(T); var table = dicConfigEntity.GetOrAdd(type, new TableAttribute()); - var fluent = new TableFluent(table); + var fluent = new TableFluent(CodeFirst, table); entity.Invoke(fluent); Utils.RemoveTableByEntity(type, this); //remove cache return _orm.CodeFirst; @@ -67,7 +67,7 @@ namespace FreeSql.Internal { if (entity == null) return _orm.CodeFirst; var table = dicConfigEntity.GetOrAdd(type, new TableAttribute()); - var fluent = new TableFluent(type, table); + var fluent = new TableFluent(CodeFirst, type, table); entity.Invoke(fluent); Utils.RemoveTableByEntity(type, this); //remove cache return _orm.CodeFirst; @@ -132,6 +132,7 @@ namespace FreeSql.Internal if (trycol._CanUpdate != null) attr._CanUpdate = trycol.CanUpdate; if (trycol.ServerTime != DateTimeKind.Unspecified) attr.ServerTime = trycol.ServerTime; if (trycol._StringLength != null) attr.StringLength = trycol.StringLength; + if (!string.IsNullOrEmpty(trycol.InsertValueSql)) attr.InsertValueSql = trycol.InsertValueSql; } var attrs = proto.GetCustomAttributes(typeof(ColumnAttribute), false); foreach (var tryattrobj in attrs) @@ -152,6 +153,7 @@ namespace FreeSql.Internal if (tryattr._CanUpdate != null) attr._CanUpdate = tryattr.CanUpdate; if (tryattr.ServerTime != DateTimeKind.Unspecified) attr.ServerTime = tryattr.ServerTime; if (tryattr._StringLength != null) attr.StringLength = tryattr.StringLength; + if (!string.IsNullOrEmpty(tryattr.InsertValueSql)) attr.InsertValueSql = tryattr.InsertValueSql; } ColumnAttribute ret = null; if (!string.IsNullOrEmpty(attr.Name)) ret = attr; @@ -168,6 +170,7 @@ namespace FreeSql.Internal if (attr._CanUpdate != null) ret = attr; if (attr.ServerTime != DateTimeKind.Unspecified) ret = attr; if (attr._StringLength != null) ret = attr; + if (!string.IsNullOrEmpty(attr.InsertValueSql)) ret = attr; if (ret != null && ret.MapType == null) ret.MapType = proto.PropertyType; return ret; } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 85ac0949..8c8631c1 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -191,6 +191,11 @@ namespace FreeSql.Internal col.DbDefaultValue = colattr.ServerTime == DateTimeKind.Local ? common.Now : common.NowUtc; col.DbInsertValue = colattr.ServerTime == DateTimeKind.Local ? common.Now : common.NowUtc; } + if (string.IsNullOrEmpty(colattr.InsertValueSql) == false) + { + col.DbDefaultValue = colattr.InsertValueSql; + col.DbInsertValue = colattr.InsertValueSql; + } if (colattr.MapType == typeof(string) && colattr.StringLength != 0) { int strlen = colattr.StringLength;