From b1660e87a510f37fe426b9a2189ae631cd0d4a92 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Mon, 20 Apr 2020 21:13:44 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20DbContext=20=E4=B8=8E?= =?UTF-8?q?=20EFCore=20=E7=9B=B8=E4=BC=BC=E7=9A=84=20FluentApi=20=E5=AF=B9?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=9B#281?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/efcore_to_freesql/Startup.cs | 3 +- .../EfCoreFluentApiExtensions.cs | 77 +++- .../EfCoreFluentApi/EfCoreTableFluent.cs | 267 +++++-------- .../EfCoreFluentApi/EfCoreTableFluent`1.cs | 365 ++++++++++++++++++ FreeSql.DbContext/FreeSql.DbContext.xml | 33 +- FreeSql/DataAnnotations/TableFluent.cs | 5 + FreeSql/FreeSql.xml | 143 +++++++ 7 files changed, 701 insertions(+), 192 deletions(-) create mode 100644 FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent`1.cs diff --git a/Examples/efcore_to_freesql/Startup.cs b/Examples/efcore_to_freesql/Startup.cs index 2271d74f..0245f0c9 100644 --- a/Examples/efcore_to_freesql/Startup.cs +++ b/Examples/efcore_to_freesql/Startup.cs @@ -27,7 +27,8 @@ namespace efcore_to_freesql .UseAutoSyncStructure(true) .Build(); - FreeSqlDbContextExtensions.EfCoreFluentApiTest(Fsql); + //FreeSqlDbContextExtensions.EfCoreFluentApiTestGeneric(Fsql); + FreeSqlDbContextExtensions.EfCoreFluentApiTestDynamic(Fsql); DBContexts.BaseDBContext.Fsql = Fsql; diff --git a/FreeSql.DbContext/EfCoreFluentApi/EfCoreFluentApiExtensions.cs b/FreeSql.DbContext/EfCoreFluentApi/EfCoreFluentApiExtensions.cs index 30661258..e31ac3fe 100644 --- a/FreeSql.DbContext/EfCoreFluentApi/EfCoreFluentApiExtensions.cs +++ b/FreeSql.DbContext/EfCoreFluentApi/EfCoreFluentApiExtensions.cs @@ -9,7 +9,7 @@ using FreeSql.Internal.CommonProvider; partial class FreeSqlDbContextExtensions { /// - /// EFCore 99% 相似的 FluentApi 扩展方法 + /// EFCore 95% 相似的 FluentApi 扩展方法 /// /// /// @@ -21,13 +21,26 @@ partial class FreeSqlDbContextExtensions codeFirst.ConfigEntity(tf => modelBuilder(new EfCoreTableFluent(cf._orm, tf))); return codeFirst; } + /// + /// EFCore 95% 相似的 FluentApi 扩展方法 + /// + /// + /// 实体类型 + /// + /// + public static ICodeFirst Entity(this ICodeFirst codeFirst, Type entityType, Action modelBuilder) + { + var cf = codeFirst as CodeFirstProvider; + codeFirst.ConfigEntity(entityType, tf => modelBuilder(new EfCoreTableFluent(cf._orm, tf, entityType))); + return codeFirst; + } - public static void EfCoreFluentApiTest(IFreeSql fsql) + public static void EfCoreFluentApiTestGeneric(IFreeSql fsql) { var cf = fsql.CodeFirst; cf.Entity(eb => { - eb.ToTable("tb_song"); + eb.ToTable("tb_song1"); eb.Ignore(a => a.Field1); eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired(); eb.Property(a => a.Url).HasMaxLength(100); @@ -36,7 +49,7 @@ partial class FreeSqlDbContextExtensions eb.Property(a => a.CreateTime).HasDefaultValueSql("current_timestamp"); eb.HasKey(a => a.Id); - eb.HasIndex(a => a.Title).IsUnique().HasName("idx_xxx11"); + eb.HasIndex(a => a.Title).IsUnique().HasName("idx_tb_song1111"); //一对多、多对一 eb.HasOne(a => a.Type).HasForeignKey(a => a.TypeId).WithMany(a => a.Songs); @@ -46,6 +59,7 @@ partial class FreeSqlDbContextExtensions }); cf.Entity(eb => { + eb.ToTable("tb_songtype1"); eb.HasMany(a => a.Songs).WithOne(a => a.Type).HasForeignKey(a => a.TypeId); eb.HasData(new[] @@ -76,6 +90,61 @@ partial class FreeSqlDbContextExtensions cf.SyncStructure(); } + public static void EfCoreFluentApiTestDynamic(IFreeSql fsql) + { + var cf = fsql.CodeFirst; + cf.Entity(typeof(Song), eb => + { + eb.ToTable("tb_song2"); + eb.Ignore("Field1"); + eb.Property("Title").HasColumnType("varchar(50)").IsRequired(); + eb.Property("Url").HasMaxLength(100); + + eb.Property("RowVersion").IsRowVersion(); + eb.Property("CreateTime").HasDefaultValueSql("current_timestamp"); + + eb.HasKey("Id"); + eb.HasIndex("Title").IsUnique().HasName("idx_tb_song2222"); + + //一对多、多对一 + eb.HasOne("Type").HasForeignKey("TypeId").WithMany("Songs"); + + //多对多 + eb.HasMany("Tags").WithMany("Songs", typeof(Song_tag)); + }); + cf.Entity(typeof(SongType), eb => + { + eb.ToTable("tb_songtype2"); + eb.HasMany("Songs").WithOne("Type").HasForeignKey("TypeId"); + + eb.HasData(new[] + { + new SongType + { + Id = 1, + Name = "流行", + Songs = new List(new[] + { + new Song{ Title = "真的爱你" }, + new Song{ Title = "爱你一万年" }, + }) + }, + new SongType + { + Id = 2, + Name = "乡村", + Songs = new List(new[] + { + new Song{ Title = "乡里乡亲" }, + }) + }, + }); + }); + + cf.SyncStructure(); + cf.SyncStructure(); + } + public class SongType { public int Id { get; set; } diff --git a/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent.cs b/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent.cs index 388e7198..90200d24 100644 --- a/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent.cs +++ b/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -6,88 +7,73 @@ using FreeSql.DataAnnotations; namespace FreeSql.Extensions.EfCoreFluentApi { - public class EfCoreTableFluent + public class EfCoreTableFluent { IFreeSql _fsql; - TableFluent _tf; - internal EfCoreTableFluent(IFreeSql fsql, TableFluent tf) + TableFluent _tf; + internal Type _entityType; + internal EfCoreTableFluent(IFreeSql fsql, TableFluent tf, Type entityType) { _fsql = fsql; _tf = tf; + _entityType = entityType; } - public EfCoreTableFluent ToTable(string name) + public EfCoreTableFluent ToTable(string name) { _tf.Name(name); return this; } - public EfCoreTableFluent ToView(string name) + 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)); /// /// 使用 FreeSql FluentApi 方法,当 EFCore FluentApi 方法无法表示的时候使用 /// /// - public TableFluent Help() => _tf; + public TableFluent Help() => _tf; #region HasKey - public EfCoreTableFluent HasKey(Expression> key) + public EfCoreTableFluent HasKey(string key) { - var exp = key?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 key 不能为 null"); - - switch (exp.NodeType) + if (key == null) throw new ArgumentException("参数错误 key 不能为 null"); + foreach (string name in key.Split(',')) { - 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; + if (string.IsNullOrEmpty(name.Trim())) continue; + _tf.Property(name.Trim()).IsPrimary(true); } return this; } #endregion #region HasIndex - public HasIndexFluent HasIndex(Expression> index) + public HasIndexFluent HasIndex(string index) { - var exp = index?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 index 不能为 null"); - + if (index == null) throw new ArgumentException("参数错误 index 不能为 null"); var indexName = $"idx_{Guid.NewGuid().ToString("N").Substring(0, 8)}"; var columns = new List(); - switch (exp.NodeType) + foreach (string name in index.Split(',')) { - 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; + if (string.IsNullOrEmpty(name.Trim())) continue; + columns.Add(name.Trim()); } _tf.Index(indexName, string.Join(", ", columns), false); return new HasIndexFluent(_tf, indexName, columns); } public class HasIndexFluent { - TableFluent _modelBuilder; + TableFluent _modelBuilder; string _indexName; List _columns; bool _isUnique; - internal HasIndexFluent(TableFluent modelBuilder, string indexName, List columns) + internal HasIndexFluent(TableFluent modelBuilder, string indexName, List columns) { _modelBuilder = modelBuilder; _indexName = indexName; @@ -110,230 +96,157 @@ namespace FreeSql.Extensions.EfCoreFluentApi #endregion #region HasOne - public HasOneFluent HasOne(Expression> one) + public HasOneFluent HasOne(string one) { - var exp = one?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); - - 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(_fsql, _tf, oneProperty); + if (string.IsNullOrEmpty(one)) throw new ArgumentException("参数错误 one 不能为 null"); + if (_entityType.GetPropertiesDictIgnoreCase().TryGetValue(one, out var oneProperty) == false) throw new ArgumentException($"参数错误 {one} 属性不存在"); + return new HasOneFluent(_fsql, _tf, _entityType, oneProperty.PropertyType, one); } - public class HasOneFluent + public class HasOneFluent { IFreeSql _fsql; - TableFluent _tf; + TableFluent _tf; + Type _entityType1; + Type _entityType2; string _selfProperty; string _selfBind; string _withManyProperty; string _withOneProperty; string _withOneBind; - internal HasOneFluent(IFreeSql fsql, TableFluent modelBuilder, string oneProperty) + internal HasOneFluent(IFreeSql fsql, TableFluent modelBuilder, Type entityType1, Type entityType2, string oneProperty) { _fsql = fsql; _tf = modelBuilder; + _entityType1 = entityType1; + _entityType2 = entityType2; _selfProperty = oneProperty; } - public HasOneFluent WithMany(Expression> many) + public HasOneFluent WithMany(string many) { - var exp = many?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); - - switch (exp.NodeType) - { - case ExpressionType.MemberAccess: - _withManyProperty = (exp as MemberExpression).Member.Name; - break; - } - if (string.IsNullOrEmpty(_withManyProperty)) throw new ArgumentException("参数错误 many"); + if (many == null) throw new ArgumentException("参数错误 many 不能为 null"); + if (_entityType2.GetPropertiesDictIgnoreCase().TryGetValue(many, out var manyProperty) == false) throw new ArgumentException($"参数错误 {many} 属性不存在"); + _withManyProperty = manyProperty.Name; if (string.IsNullOrEmpty(_selfBind) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(many, _selfBind)); return this; } - public HasOneFluent WithOne(Expression> one, Expression> foreignKey) + public HasOneFluent WithOne(string one, string foreignKey) { - var exp = one?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); + if (string.IsNullOrEmpty(one)) throw new ArgumentException("参数错误 one 不能为 null"); + if (_entityType1.GetPropertiesDictIgnoreCase().TryGetValue(one, out var oneProperty) == false) throw new ArgumentException($"参数错误 {one} 属性不存在"); + if (oneProperty != _entityType1) throw new ArgumentException($"参数错误 {one} 属性不存在"); + _withOneProperty = oneProperty.Name; - switch (exp.NodeType) + if (foreignKey == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + foreach (string name in foreignKey.Split(',')) { - case ExpressionType.MemberAccess: - _withOneProperty = (exp as MemberExpression).Member.Name; - break; - } - if (string.IsNullOrEmpty(_withOneProperty)) throw new ArgumentException("参数错误 one"); - - exp = foreignKey?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); - - 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(name.Trim())) continue; + _withOneBind += ", " + name.Trim(); } if (string.IsNullOrEmpty(_withOneBind)) throw new ArgumentException("参数错误 foreignKey"); + _withOneBind = _withOneBind.TrimStart(',', ' '); if (string.IsNullOrEmpty(_selfBind) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); return this; } - public HasOneFluent HasForeignKey(Expression> foreignKey) + public HasOneFluent HasForeignKey(string foreignKey) { - var exp = foreignKey?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); - - switch (exp.NodeType) + if (foreignKey == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + foreach (string name in foreignKey.Split(',')) { - 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(name.Trim())) continue; + _selfBind += ", " + name.Trim(); } if (string.IsNullOrEmpty(_selfBind)) throw new ArgumentException("参数错误 foreignKey"); + _selfBind = _selfBind.TrimStart(',', ' '); _tf.Navigate(_selfProperty, _selfBind); if (string.IsNullOrEmpty(_withManyProperty) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(_withManyProperty, _selfBind)); if (string.IsNullOrEmpty(_withOneProperty) == false && string.IsNullOrEmpty(_withOneBind) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); return this; } } #endregion #region HasMany - public HasManyFluent HasMany(Expression>> many) + public HasManyFluent HasMany(string many) { - var exp = many?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); - - 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(_fsql, _tf, manyProperty); + if (string.IsNullOrEmpty(many)) throw new ArgumentException("参数错误 many 不能为 null"); + if (_entityType.GetPropertiesDictIgnoreCase().TryGetValue(many, out var manyProperty) == false) throw new ArgumentException($"参数错误 {many} 集合属性不存在"); + if (typeof(IEnumerable).IsAssignableFrom(manyProperty.PropertyType) == false || manyProperty.PropertyType.IsGenericType == false) throw new ArgumentException("参数错误 {many} 不是集合属性"); + return new HasManyFluent(_fsql, _tf, _entityType, manyProperty.PropertyType.GetGenericArguments()[0], manyProperty.Name); } - public class HasManyFluent + public class HasManyFluent { IFreeSql _fsql; - TableFluent _tf; + TableFluent _tf; + Type _entityType1; + Type _entityType2; string _selfProperty; string _selfBind; string _withOneProperty; string _withManyProperty; - internal HasManyFluent(IFreeSql fsql, TableFluent modelBuilder, string manyProperty) + internal HasManyFluent(IFreeSql fsql, TableFluent modelBuilder, Type entityType1, Type entityType2, string manyProperty) { _fsql = fsql; _tf = modelBuilder; + _entityType1 = entityType1; + _entityType2 = entityType2; _selfProperty = manyProperty; } - public void WithMany(Expression>> many, Type middleType) + public void WithMany(string many, Type middleType) { - var exp = many?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); - - switch (exp.NodeType) - { - case ExpressionType.MemberAccess: - _withManyProperty = (exp as MemberExpression).Member.Name; - break; - } - if (string.IsNullOrEmpty(_withManyProperty)) throw new ArgumentException("参数错误 many"); - + if (string.IsNullOrEmpty(many)) throw new ArgumentException("参数错误 many 不能为 null"); + if (_entityType2.GetPropertiesDictIgnoreCase().TryGetValue(many, out var manyProperty) == false) throw new ArgumentException($"参数错误 {many} 集合属性不存在"); + if (typeof(IEnumerable).IsAssignableFrom(manyProperty.PropertyType) == false || manyProperty.PropertyType.IsGenericType == false) throw new ArgumentException("参数错误 {many} 不是集合属性"); + _withManyProperty = manyProperty.Name; _tf.Navigate(_selfProperty, null, middleType); - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, null, middleType)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(_withManyProperty, null, middleType)); } - public HasManyFluent WithOne(Expression> one) + public HasManyFluent WithOne(string one) { - var exp = one?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); - - switch (exp.NodeType) - { - case ExpressionType.MemberAccess: - _withOneProperty = (exp as MemberExpression).Member.Name; - break; - } - if (string.IsNullOrEmpty(_withOneProperty)) throw new ArgumentException("参数错误 one"); - + if (string.IsNullOrEmpty(one)) throw new ArgumentException("参数错误 one 不能为 null"); + if (_entityType2.GetPropertiesDictIgnoreCase().TryGetValue(one, out var oneProperty) == false) throw new ArgumentException($"参数错误 {one} 属性不存在"); + if (oneProperty.PropertyType != _entityType1) throw new ArgumentException($"参数错误 {one} 属性不存在"); + _withOneProperty = oneProperty.Name; if (string.IsNullOrEmpty(_selfBind) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _selfBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(oneProperty.Name, _selfBind)); return this; } - public HasManyFluent HasForeignKey(Expression> foreignKey) + public HasManyFluent HasForeignKey(string foreignKey) { - var exp = foreignKey?.Body; - if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; - if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); - - switch (exp.NodeType) + if (foreignKey == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + foreach (string name in foreignKey.Split(',')) { - 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(name.Trim())) continue; + _selfBind += ", " + name.Trim(); } if (string.IsNullOrEmpty(_selfBind)) throw new ArgumentException("参数错误 foreignKey"); + _selfBind = _selfBind.TrimStart(',', ' '); _tf.Navigate(_selfProperty, _selfBind); if (string.IsNullOrEmpty(_withOneProperty) == false) - _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _selfBind)); + _fsql.CodeFirst.ConfigEntity(_entityType2, eb2 => eb2.Navigate(_withOneProperty, _selfBind)); return this; } } #endregion - public EfCoreTableFluent Ignore(Expression> property) + public EfCoreTableFluent Ignore(string property) { _tf.Property(property).IsIgnore(true); return this; } - public EfCoreTableFluent HasData(T data) => HasData(new[] { data }); /// /// 使用 Repository + EnableAddOrUpdateNavigateList + NoneParameter 方式插入种子数据 /// /// /// - public EfCoreTableFluent HasData(IEnumerable data) + public EfCoreTableFluent HasData(IEnumerable data) { if (data.Any() == false) return this; var sdCopy = data.Select(a => (object)a).ToList(); @@ -346,7 +259,7 @@ namespace FreeSql.Extensions.EfCoreFluentApi if (sd == null || sd.Any() == false) return; foreach (var et in e.EntityTypes) { - if (et != typeof(T)) continue; + if (et != _entityType) continue; if (_fsql.Select().AsType(et).Any()) continue; var repo = _fsql.GetRepository(); diff --git a/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent`1.cs b/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent`1.cs new file mode 100644 index 00000000..388e7198 --- /dev/null +++ b/FreeSql.DbContext/EfCoreFluentApi/EfCoreTableFluent`1.cs @@ -0,0 +1,365 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FreeSql.DataAnnotations; + +namespace FreeSql.Extensions.EfCoreFluentApi +{ + public class EfCoreTableFluent + { + IFreeSql _fsql; + TableFluent _tf; + internal EfCoreTableFluent(IFreeSql fsql, TableFluent tf) + { + _fsql = fsql; + _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)); + + /// + /// 使用 FreeSql FluentApi 方法,当 EFCore FluentApi 方法无法表示的时候使用 + /// + /// + public TableFluent Help() => _tf; + + #region HasKey + public EfCoreTableFluent HasKey(Expression> key) + { + var exp = key?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 key 不能为 null"); + + 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) + { + var exp = index?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 index 不能为 null"); + + 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) + { + var exp = one?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); + + 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(_fsql, _tf, oneProperty); + } + public class HasOneFluent + { + IFreeSql _fsql; + TableFluent _tf; + string _selfProperty; + string _selfBind; + string _withManyProperty; + string _withOneProperty; + string _withOneBind; + + internal HasOneFluent(IFreeSql fsql, TableFluent modelBuilder, string oneProperty) + { + _fsql = fsql; + _tf = modelBuilder; + _selfProperty = oneProperty; + } + public HasOneFluent WithMany(Expression> many) + { + var exp = many?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); + + 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) + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + return this; + } + public HasOneFluent WithOne(Expression> one, Expression> foreignKey) + { + var exp = one?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); + + switch (exp.NodeType) + { + case ExpressionType.MemberAccess: + _withOneProperty = (exp as MemberExpression).Member.Name; + break; + } + if (string.IsNullOrEmpty(_withOneProperty)) throw new ArgumentException("参数错误 one"); + + exp = foreignKey?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + + 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) + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + return this; + } + public HasOneFluent HasForeignKey(Expression> foreignKey) + { + var exp = foreignKey?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + + 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) + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, _selfBind)); + if (string.IsNullOrEmpty(_withOneProperty) == false && string.IsNullOrEmpty(_withOneBind) == false) + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _withOneBind)); + return this; + } + } + #endregion + + #region HasMany + public HasManyFluent HasMany(Expression>> many) + { + var exp = many?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); + + 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(_fsql, _tf, manyProperty); + } + public class HasManyFluent + { + IFreeSql _fsql; + TableFluent _tf; + string _selfProperty; + string _selfBind; + string _withOneProperty; + string _withManyProperty; + + internal HasManyFluent(IFreeSql fsql, TableFluent modelBuilder, string manyProperty) + { + _fsql = fsql; + _tf = modelBuilder; + _selfProperty = manyProperty; + } + + public void WithMany(Expression>> many, Type middleType) + { + var exp = many?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 many 不能为 null"); + + 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); + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withManyProperty, null, middleType)); + } + public HasManyFluent WithOne(Expression> one) + { + var exp = one?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 one 不能为 null"); + + 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) + _fsql.CodeFirst.ConfigEntity(eb2 => eb2.Navigate(_withOneProperty, _selfBind)); + return this; + } + public HasManyFluent HasForeignKey(Expression> foreignKey) + { + var exp = foreignKey?.Body; + if (exp?.NodeType == ExpressionType.Convert) exp = (exp as UnaryExpression)?.Operand; + if (exp == null) throw new ArgumentException("参数错误 foreignKey 不能为 null"); + + 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) + _fsql.CodeFirst.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 }); + + /// + /// 使用 Repository + EnableAddOrUpdateNavigateList + NoneParameter 方式插入种子数据 + /// + /// + /// + public EfCoreTableFluent HasData(IEnumerable data) + { + if (data.Any() == false) return this; + var sdCopy = data.Select(a => (object)a).ToList(); + var sdCopyLock = new object(); + _fsql.Aop.SyncStructureAfter += new EventHandler((s, e) => + { + object[] sd = null; + lock (sdCopyLock) + sd = sdCopy?.ToArray(); + if (sd == null || sd.Any() == false) return; + foreach (var et in e.EntityTypes) + { + if (et != typeof(T)) continue; + if (_fsql.Select().AsType(et).Any()) continue; + + var repo = _fsql.GetRepository(); + repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; + repo.DbContextOptions.NoneParameter = true; + repo.AsType(et); + repo.Insert(sd); + + lock (sdCopyLock) + sdCopy = null; + } + }); + return this; + } + } +} diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 8fd2938a..38667865 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -175,6 +175,19 @@ + + + 使用 FreeSql FluentApi 方法,当 EFCore FluentApi 方法无法表示的时候使用 + + + + + + 使用 Repository + EnableAddOrUpdateNavigateList + NoneParameter 方式插入种子数据 + + + + 使用 FreeSql FluentApi 方法,当 EFCore FluentApi 方法无法表示的时候使用 @@ -330,13 +343,22 @@ - EFCore 99% 相似的 FluentApi 扩展方法 + EFCore 95% 相似的 FluentApi 扩展方法 + + + EFCore 95% 相似的 FluentApi 扩展方法 + + + 实体类型 + + + 创建普通数据上下文档对象 @@ -395,14 +417,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql/DataAnnotations/TableFluent.cs b/FreeSql/DataAnnotations/TableFluent.cs index 46c1f760..5a9c25e7 100644 --- a/FreeSql/DataAnnotations/TableFluent.cs +++ b/FreeSql/DataAnnotations/TableFluent.cs @@ -80,6 +80,11 @@ 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; + } } public class TableFluent diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 9780f9de..cb7ec67f 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -2305,6 +2305,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 }) + + + + + + 可自定义解析表达式 @@ -2825,6 +2956,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -2895,6 +3032,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发