diff --git a/FreeSql.Extensions.EFCoreModelBuilder/CodeFirstExtensions.cs b/FreeSql.Extensions.EFCoreModelBuilder/CodeFirstExtensions.cs new file mode 100644 index 00000000..a563e033 --- /dev/null +++ b/FreeSql.Extensions.EFCoreModelBuilder/CodeFirstExtensions.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace FreeSql.Extensions.EFCoreModelBuilder { + + public static class CodeFirstExtensions { + + public static void ConfigEntity(this ICodeFirst codeFirst, ModelBuilder modelBuilder) { + + foreach (var type in modelBuilder.Model.GetEntityTypes()) { + + codeFirst.ConfigEntity(type.ClrType, a => { + + //表名 + var relationalTableName = type.FindAnnotation("Relational:TableName"); + if (relationalTableName != null) { + a.Name(relationalTableName.Value?.ToString() ?? type.ClrType.Name); + } + + foreach (var prop in type.GetProperties()) { + + var freeProp = a.Property(prop.Name); + + //列名 + var relationalColumnName = prop.FindAnnotation("Relational:ColumnName"); + if (relationalColumnName != null) { + + freeProp.Name(relationalColumnName.Value?.ToString() ?? prop.Name); + } + + //主键 + freeProp.IsPrimary(prop.IsPrimaryKey()); + + //自增 + freeProp.IsIdentity(prop.GetAnnotations().Where(z => + z.Name == "SqlServer:ValueGenerationStrategy" && z.Value.ToString().Contains("IdentityColumn") //sqlserver 自增 + || z.Value.ToString().Contains("IdentityColumn") //其他数据库实现未经测试 + ).Any()); + + //可空 + freeProp.IsNullable(prop.AfterSaveBehavior != Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Throw); + + //类型 + var relationalColumnType = prop.FindAnnotation("Relational:ColumnType"); + if (relationalColumnType != null) { + + var dbType = relationalColumnType.ToString(); + + if (!string.IsNullOrEmpty(dbType)) { + + var maxLength = prop.FindAnnotation("MaxLength"); + if (maxLength != null) + dbType += $"({maxLength})"; + + freeProp.DbType(dbType); + } + } + } + }); + } + } + } +} diff --git a/FreeSql.Extensions.EFCoreModelBuilder/FreeSql.Extensions.EFCoreModelBuilder.csproj b/FreeSql.Extensions.EFCoreModelBuilder/FreeSql.Extensions.EFCoreModelBuilder.csproj new file mode 100644 index 00000000..efc6283c --- /dev/null +++ b/FreeSql.Extensions.EFCoreModelBuilder/FreeSql.Extensions.EFCoreModelBuilder.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + 0.0.12 + FreeSql + FreeSql + FreeSql ICodeFirst 扩展库,现实从 EFCore FluentAPI/Attribute 读取,从而做到无缝接入已使用 EFCore 项目开发。 + https://github.com/2881099/FreeSql + https://github.com/2881099/FreeSql + + + + + + + + + + + diff --git a/FreeSql.RESTful.Demo/Entity/Song.cs b/FreeSql.RESTful.Demo/Entity/Song.cs index a01a4f15..817470de 100644 --- a/FreeSql.RESTful.Demo/Entity/Song.cs +++ b/FreeSql.RESTful.Demo/Entity/Song.cs @@ -1,10 +1,14 @@ -using System; +using Microsoft.EntityFrameworkCore; +using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading.Tasks; namespace FreeSql.RESTful.Demo.Entity { public class Song { + public int Id { get; set; } public string Title { get; set; } } diff --git a/FreeSql.RESTful.Demo/FreeSql.RESTful.Demo.csproj b/FreeSql.RESTful.Demo/FreeSql.RESTful.Demo.csproj index d6e2e03e..a52d3545 100644 --- a/FreeSql.RESTful.Demo/FreeSql.RESTful.Demo.csproj +++ b/FreeSql.RESTful.Demo/FreeSql.RESTful.Demo.csproj @@ -7,6 +7,8 @@ + + diff --git a/FreeSql.RESTful.Demo/Startup.cs b/FreeSql.RESTful.Demo/Startup.cs index 011d96b6..64b60821 100644 --- a/FreeSql.RESTful.Demo/Startup.cs +++ b/FreeSql.RESTful.Demo/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/FreeSql.Tests/DataAnnotations/FluentTest.cs b/FreeSql.Tests/DataAnnotations/FluentTest.cs index 5c5ae522..47f3493f 100644 --- a/FreeSql.Tests/DataAnnotations/FluentTest.cs +++ b/FreeSql.Tests/DataAnnotations/FluentTest.cs @@ -6,16 +6,24 @@ namespace FreeSql.Tests.DataAnnotations { [Fact] public void Fluent() { g.mysql.CodeFirst - .ConfigEntity(a => { - a.Name("xxdkdkdk1").SelectFilter("a.Id22 > 0"); - a.Property(b => b.Id).Name("Id22").IsIdentity(true); - a.Property(b => b.name).DbType("varchar(100)").IsNullable(true); + //.ConfigEntity(a => { + // a.Name("xxdkdkdk1").SelectFilter("a.Id22 > 0"); + // a.Property(b => b.Id).Name("Id22").IsIdentity(true); + // a.Property(b => b.name).DbType("varchar(100)").IsNullable(true); + //}) + + .ConfigEntity(typeof(TestFluenttb1), a => { + a.Name("xxdkdkdk1222").SelectFilter("a.Id22 > 1"); + a.Property("Id").Name("Id22dd").IsIdentity(true); + a.Property("Name").DbType("varchar(101)").IsNullable(true); }) + .ConfigEntity(a => { a.Name("xxdkdkdk2").SelectFilter("a.Idx > 0"); a.Property(b => b.Id).Name("Id22").IsIdentity(true); a.Property(b => b.name).DbType("varchar(100)").IsNullable(true); - }); + }) + ; var ddl1 = g.mysql.CodeFirst.GetComparisonDDLStatements(); var ddl2 = g.mysql.CodeFirst.GetComparisonDDLStatements(); diff --git a/FreeSql.sln b/FreeSql.sln index 1beaf6a3..198f1157 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.PerformanceTe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.RESTful.Demo", "FreeSql.RESTful.Demo\FreeSql.RESTful.Demo.csproj", "{A749092B-4C21-4087-B33D-0FBD4DA51C24}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeSql.Extensions.EFCoreModelBuilder", "FreeSql.Extensions.EFCoreModelBuilder\FreeSql.Extensions.EFCoreModelBuilder.csproj", "{490CC8AF-C47C-4139-AED7-4FB6502F622B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -88,6 +90,18 @@ Global {A749092B-4C21-4087-B33D-0FBD4DA51C24}.Release|x64.Build.0 = Release|Any CPU {A749092B-4C21-4087-B33D-0FBD4DA51C24}.Release|x86.ActiveCfg = Release|Any CPU {A749092B-4C21-4087-B33D-0FBD4DA51C24}.Release|x86.Build.0 = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|x64.ActiveCfg = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|x64.Build.0 = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|x86.ActiveCfg = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Debug|x86.Build.0 = Debug|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|Any CPU.Build.0 = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|x64.ActiveCfg = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|x64.Build.0 = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|x86.ActiveCfg = Release|Any CPU + {490CC8AF-C47C-4139-AED7-4FB6502F622B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FreeSql/DataAnnotations/ColumnFluent.cs b/FreeSql/DataAnnotations/ColumnFluent.cs index 3b9ffe56..a5e63721 100644 --- a/FreeSql/DataAnnotations/ColumnFluent.cs +++ b/FreeSql/DataAnnotations/ColumnFluent.cs @@ -1,7 +1,7 @@ using System; namespace FreeSql.DataAnnotations { - public class ColumnFluent { + public class ColumnFluent { public ColumnFluent(ColumnAttribute column) { _column = column; @@ -11,21 +11,21 @@ namespace FreeSql.DataAnnotations { /// /// 数据库列名 /// - public ColumnFluent Name(string value) { + public ColumnFluent Name(string value) { _column.Name = value; return this; } /// /// 指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】 /// - public ColumnFluent OldName(string value) { + public ColumnFluent OldName(string value) { _column.OldName = value; return this; } /// /// 数据库类型,如: varchar(255) /// - public ColumnFluent DbType(string value) { + public ColumnFluent DbType(string value) { _column.DbType = value; return this; } @@ -33,20 +33,20 @@ namespace FreeSql.DataAnnotations { /// /// 主键 /// - public ColumnFluent IsPrimary(bool value) { + public ColumnFluent IsPrimary(bool value) { _column.IsPrimary = value; return this; } /// 自增标识 /// - public ColumnFluent IsIdentity(bool value) { + public ColumnFluent IsIdentity(bool value) { _column.IsIdentity = value; return this; } /// /// 是否可DBNull /// - public ColumnFluent IsNullable(bool value) { + public ColumnFluent IsNullable(bool value) { _column.IsNullable = value; return this; } diff --git a/FreeSql/DataAnnotations/TableAttribute.cs b/FreeSql/DataAnnotations/TableAttribute.cs index 1210d8d8..fe6788a5 100644 --- a/FreeSql/DataAnnotations/TableAttribute.cs +++ b/FreeSql/DataAnnotations/TableAttribute.cs @@ -17,6 +17,6 @@ namespace FreeSql.DataAnnotations { /// public string SelectFilter { get; set; } - internal ConcurrentDictionary _columns => new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); + internal ConcurrentDictionary _columns { get; } = new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); } } diff --git a/FreeSql/DataAnnotations/TableFluent.cs b/FreeSql/DataAnnotations/TableFluent.cs index 2860f1df..944d380d 100644 --- a/FreeSql/DataAnnotations/TableFluent.cs +++ b/FreeSql/DataAnnotations/TableFluent.cs @@ -1,9 +1,51 @@ using System; using System.Collections.Generic; using System.Collections.Concurrent; +using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace FreeSql.DataAnnotations { + public class TableFluent { + + public TableFluent(Type entityType, TableAttribute table) { + _entityType = entityType; + _properties = _entityType.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase); + _table = table; + } + + Type _entityType; + Dictionary _properties; + TableAttribute _table; + /// + /// 数据库表名 + /// + public TableFluent Name(string value) { + _table.Name = value; + return this; + } + /// + /// 指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】 + /// + public TableFluent OldName(string value) { + _table.OldName = value; + return this; + } + /// + /// 查询过滤SQL,实现类似 a.IsDeleted = 1 功能 + /// + public TableFluent SelectFilter(string value) { + _table.SelectFilter = value; + return this; + } + + public ColumnFluent Property(string proto) { + if (_properties.ContainsKey(proto) == false) throw new KeyNotFoundException($"找不到属性名 {proto}"); + var col = _table._columns.GetOrAdd(proto, name => new ColumnAttribute { Name = proto }); + return new ColumnFluent(col); + } + } + public class TableFluent { public TableFluent(TableAttribute table) { @@ -33,11 +75,11 @@ namespace FreeSql.DataAnnotations { return this; } - public ColumnFluent Property(Expression> column) { + public ColumnFluent Property(Expression> column) { 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 new ColumnFluent(col); + return new ColumnFluent(col); } } } diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 96704b3a..718f746b 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.0.12 + 0.0.13 true YeXiangQin 打造 .NETCore 最方便的 ORM,DbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite 数据库。 diff --git a/FreeSql/Interface/ICodeFirst.cs b/FreeSql/Interface/ICodeFirst.cs index e333da97..50cc363c 100644 --- a/FreeSql/Interface/ICodeFirst.cs +++ b/FreeSql/Interface/ICodeFirst.cs @@ -50,5 +50,6 @@ namespace FreeSql { /// (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type); ICodeFirst ConfigEntity(Action> entity); + ICodeFirst ConfigEntity(Type type, Action entity); } } diff --git a/FreeSql/Internal/CommonUtils.cs b/FreeSql/Internal/CommonUtils.cs index dfe91af9..7be2acd4 100644 --- a/FreeSql/Internal/CommonUtils.cs +++ b/FreeSql/Internal/CommonUtils.cs @@ -40,9 +40,15 @@ namespace FreeSql.Internal { entity?.Invoke(fluent); return _orm.CodeFirst; } - internal TableAttribute GetEntityTableAttribute(Type entityType) { - var attr = entityType.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; - if (dicConfigEntity.TryGetValue(entityType, out var trytb) == false) return attr; + internal ICodeFirst ConfigEntity(Type type, Action entity) { + var table = dicConfigEntity.GetOrAdd(type, new TableAttribute()); + var fluent = new TableFluent(type, table); + entity?.Invoke(fluent); + return _orm.CodeFirst; + } + internal TableAttribute GetEntityTableAttribute(Type type) { + var attr = type.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; + if (dicConfigEntity.TryGetValue(type, out var trytb) == false) return attr; if (attr == null) attr = new TableAttribute(); if (string.IsNullOrEmpty(attr.Name)) attr.Name = trytb.Name; @@ -50,9 +56,9 @@ namespace FreeSql.Internal { if (string.IsNullOrEmpty(attr.SelectFilter)) attr.SelectFilter = trytb.SelectFilter; return attr; } - internal ColumnAttribute GetEntityColumnAttribute(Type entityType, PropertyInfo proto) { + internal ColumnAttribute GetEntityColumnAttribute(Type type, PropertyInfo proto) { var attr = proto.GetCustomAttributes(typeof(ColumnAttribute), false).LastOrDefault() as ColumnAttribute; - if (dicConfigEntity.TryGetValue(entityType, out var trytb) == false) return attr; + if (dicConfigEntity.TryGetValue(type, out var trytb) == false) return attr; if (trytb._columns.TryGetValue(proto.Name, out var trycol) == false) return attr; if (attr == null) attr = new ColumnAttribute(); diff --git a/FreeSql/MySql/MySqlCodeFirst.cs b/FreeSql/MySql/MySqlCodeFirst.cs index 5d21c230..534a29ef 100644 --- a/FreeSql/MySql/MySqlCodeFirst.cs +++ b/FreeSql/MySql/MySqlCodeFirst.cs @@ -272,5 +272,6 @@ where a.table_schema in ({0}) and a.table_name in ({1})".FormatMySql(tboldname ? } } public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); + public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); } } \ No newline at end of file diff --git a/FreeSql/Oracle/OracleCodeFirst.cs b/FreeSql/Oracle/OracleCodeFirst.cs index dc88797d..369d6808 100644 --- a/FreeSql/Oracle/OracleCodeFirst.cs +++ b/FreeSql/Oracle/OracleCodeFirst.cs @@ -310,5 +310,6 @@ where owner={{0}} and table_name={{1}}".FormatOracleSQL(tboldname ?? tbname); } } public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); + public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); } } \ No newline at end of file diff --git a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs index e277dc31..49c808bb 100644 --- a/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs +++ b/FreeSql/PostgreSQL/PostgreSQLCodeFirst.cs @@ -324,5 +324,6 @@ where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contyp } } public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); + public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); } } \ No newline at end of file diff --git a/FreeSql/SqlServer/SqlServerCodeFirst.cs b/FreeSql/SqlServer/SqlServerCodeFirst.cs index c0b491cb..1e69912a 100644 --- a/FreeSql/SqlServer/SqlServerCodeFirst.cs +++ b/FreeSql/SqlServer/SqlServerCodeFirst.cs @@ -282,5 +282,6 @@ use " + database, tboldname ?? tbname); } } public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); + public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); } } \ No newline at end of file diff --git a/FreeSql/Sqlite/SqliteCodeFirst.cs b/FreeSql/Sqlite/SqliteCodeFirst.cs index ca8a61d6..54b31bff 100644 --- a/FreeSql/Sqlite/SqliteCodeFirst.cs +++ b/FreeSql/Sqlite/SqliteCodeFirst.cs @@ -240,5 +240,6 @@ namespace FreeSql.Sqlite { } } public ICodeFirst ConfigEntity(Action> entity) => _commonUtils.ConfigEntity(entity); + public ICodeFirst ConfigEntity(Type type, Action entity) => _commonUtils.ConfigEntity(type, entity); } } \ No newline at end of file