v0.0.13 #4 - 修复和丰富 ICodeFirst.ConfigEntity 方法;

- 增加 FreeSql.Extensions.EFCoreModelBuilder 扩展库,现实与 EFCore 实体共存;
- 增加 FreeSql.RESTful.Demo 示例项目;
This commit is contained in:
28810 2019-02-14 19:07:52 +08:00
parent 45bc234160
commit 488a6edd4d
18 changed files with 190 additions and 22 deletions

View File

@ -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);
}
}
}
});
}
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.0.12</Version>
<Authors>FreeSql</Authors>
<Product>FreeSql</Product>
<Description>FreeSql ICodeFirst 扩展库,现实从 EFCore FluentAPI/Attribute 读取,从而做到无缝接入已使用 EFCore 项目开发。</Description>
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FreeSql\FreeSql.csproj" />
</ItemGroup>
</Project>

View File

@ -1,10 +1,14 @@
using System; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FreeSql.RESTful.Demo.Entity { namespace FreeSql.RESTful.Demo.Entity {
public class Song { public class Song {
public int Id { get; set; } public int Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
} }

View File

@ -7,6 +7,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="4.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="4.0.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -6,16 +6,24 @@ namespace FreeSql.Tests.DataAnnotations {
[Fact] [Fact]
public void Fluent() { public void Fluent() {
g.mysql.CodeFirst g.mysql.CodeFirst
.ConfigEntity<TestFluenttb1>(a => { //.ConfigEntity<TestFluenttb1>(a => {
a.Name("xxdkdkdk1").SelectFilter("a.Id22 > 0"); // a.Name("xxdkdkdk1").SelectFilter("a.Id22 > 0");
a.Property(b => b.Id).Name("Id22").IsIdentity(true); // a.Property(b => b.Id).Name("Id22").IsIdentity(true);
a.Property(b => b.name).DbType("varchar(100)").IsNullable(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<TestFluenttb2>(a => { .ConfigEntity<TestFluenttb2>(a => {
a.Name("xxdkdkdk2").SelectFilter("a.Idx > 0"); a.Name("xxdkdkdk2").SelectFilter("a.Idx > 0");
a.Property(b => b.Id).Name("Id22").IsIdentity(true); a.Property(b => b.Id).Name("Id22").IsIdentity(true);
a.Property(b => b.name).DbType("varchar(100)").IsNullable(true); a.Property(b => b.name).DbType("varchar(100)").IsNullable(true);
}); })
;
var ddl1 = g.mysql.CodeFirst.GetComparisonDDLStatements<TestFluenttb1>(); var ddl1 = g.mysql.CodeFirst.GetComparisonDDLStatements<TestFluenttb1>();
var ddl2 = g.mysql.CodeFirst.GetComparisonDDLStatements<TestFluenttb2>(); var ddl2 = g.mysql.CodeFirst.GetComparisonDDLStatements<TestFluenttb2>();

View File

@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.PerformanceTe
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.RESTful.Demo", "FreeSql.RESTful.Demo\FreeSql.RESTful.Demo.csproj", "{A749092B-4C21-4087-B33D-0FBD4DA51C24}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.RESTful.Demo", "FreeSql.RESTful.Demo\FreeSql.RESTful.Demo.csproj", "{A749092B-4C21-4087-B33D-0FBD4DA51C24}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeSql.Extensions.EFCoreModelBuilder", "FreeSql.Extensions.EFCoreModelBuilder\FreeSql.Extensions.EFCoreModelBuilder.csproj", "{490CC8AF-C47C-4139-AED7-4FB6502F622B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{A749092B-4C21-4087-B33D-0FBD4DA51C24}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,7 +1,7 @@
using System; using System;
namespace FreeSql.DataAnnotations { namespace FreeSql.DataAnnotations {
public class ColumnFluent<T> { public class ColumnFluent {
public ColumnFluent(ColumnAttribute column) { public ColumnFluent(ColumnAttribute column) {
_column = column; _column = column;
@ -11,21 +11,21 @@ namespace FreeSql.DataAnnotations {
/// <summary> /// <summary>
/// 数据库列名 /// 数据库列名
/// </summary> /// </summary>
public ColumnFluent<T> Name(string value) { public ColumnFluent Name(string value) {
_column.Name = value; _column.Name = value;
return this; return this;
} }
/// <summary> /// <summary>
/// 指定数据库旧的列名修改实体属性命名时同时设置此参数为修改之前的值CodeFirst才可以正确修改数据库字段否则将视为【新增字段】 /// 指定数据库旧的列名修改实体属性命名时同时设置此参数为修改之前的值CodeFirst才可以正确修改数据库字段否则将视为【新增字段】
/// </summary> /// </summary>
public ColumnFluent<T> OldName(string value) { public ColumnFluent OldName(string value) {
_column.OldName = value; _column.OldName = value;
return this; return this;
} }
/// <summary> /// <summary>
/// 数据库类型,如: varchar(255) /// 数据库类型,如: varchar(255)
/// </summary> /// </summary>
public ColumnFluent<T> DbType(string value) { public ColumnFluent DbType(string value) {
_column.DbType = value; _column.DbType = value;
return this; return this;
} }
@ -33,20 +33,20 @@ namespace FreeSql.DataAnnotations {
/// <summary> /// <summary>
/// 主键 /// 主键
/// </summary> /// </summary>
public ColumnFluent<T> IsPrimary(bool value) { public ColumnFluent IsPrimary(bool value) {
_column.IsPrimary = value; _column.IsPrimary = value;
return this; return this;
} }
/// 自增标识 /// 自增标识
/// </summary> /// </summary>
public ColumnFluent<T> IsIdentity(bool value) { public ColumnFluent IsIdentity(bool value) {
_column.IsIdentity = value; _column.IsIdentity = value;
return this; return this;
} }
/// <summary> /// <summary>
/// 是否可DBNull /// 是否可DBNull
/// </summary> /// </summary>
public ColumnFluent<T> IsNullable(bool value) { public ColumnFluent IsNullable(bool value) {
_column.IsNullable = value; _column.IsNullable = value;
return this; return this;
} }

View File

@ -17,6 +17,6 @@ namespace FreeSql.DataAnnotations {
/// </summary> /// </summary>
public string SelectFilter { get; set; } public string SelectFilter { get; set; }
internal ConcurrentDictionary<string, ColumnAttribute> _columns => new ConcurrentDictionary<string, ColumnAttribute>(StringComparer.CurrentCultureIgnoreCase); internal ConcurrentDictionary<string, ColumnAttribute> _columns { get; } = new ConcurrentDictionary<string, ColumnAttribute>(StringComparer.CurrentCultureIgnoreCase);
} }
} }

View File

@ -1,9 +1,51 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection;
namespace FreeSql.DataAnnotations { 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<string, PropertyInfo> _properties;
TableAttribute _table;
/// <summary>
/// 数据库表名
/// </summary>
public TableFluent Name(string value) {
_table.Name = value;
return this;
}
/// <summary>
/// 指定数据库旧的表名修改实体命名时同时设置此参数为修改之前的值CodeFirst才可以正确修改数据库表否则将视为【创建新表】
/// </summary>
public TableFluent OldName(string value) {
_table.OldName = value;
return this;
}
/// <summary>
/// 查询过滤SQL实现类似 a.IsDeleted = 1 功能
/// </summary>
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<T> { public class TableFluent<T> {
public TableFluent(TableAttribute table) { public TableFluent(TableAttribute table) {
@ -33,11 +75,11 @@ namespace FreeSql.DataAnnotations {
return this; return this;
} }
public ColumnFluent<TProto> Property<TProto>(Expression<Func<T, TProto>> column) { public ColumnFluent Property<TProto>(Expression<Func<T, TProto>> column) {
var proto = (column.Body as MemberExpression)?.Member; var proto = (column.Body as MemberExpression)?.Member;
if (proto == null) throw new FormatException($"错误的表达式格式 {column}"); if (proto == null) throw new FormatException($"错误的表达式格式 {column}");
var col = _table._columns.GetOrAdd(proto.Name, name => new ColumnAttribute { Name = proto.Name }); var col = _table._columns.GetOrAdd(proto.Name, name => new ColumnAttribute { Name = proto.Name });
return new ColumnFluent<TProto>(col); return new ColumnFluent(col);
} }
} }
} }

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<Version>0.0.12</Version> <Version>0.0.13</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>YeXiangQin</Authors> <Authors>YeXiangQin</Authors>
<Description>打造 .NETCore 最方便的 ORMDbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite 数据库。</Description> <Description>打造 .NETCore 最方便的 ORMDbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite 数据库。</Description>

View File

@ -50,5 +50,6 @@ namespace FreeSql {
/// <returns></returns> /// <returns></returns>
(int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type); (int type, string dbtype, string dbtypeFull, bool? isnullable, object defaultValue)? GetDbInfo(Type type);
ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity); ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity);
ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity);
} }
} }

View File

@ -40,9 +40,15 @@ namespace FreeSql.Internal {
entity?.Invoke(fluent); entity?.Invoke(fluent);
return _orm.CodeFirst; return _orm.CodeFirst;
} }
internal TableAttribute GetEntityTableAttribute(Type entityType) { internal ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) {
var attr = entityType.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; var table = dicConfigEntity.GetOrAdd(type, new TableAttribute());
if (dicConfigEntity.TryGetValue(entityType, out var trytb) == false) return attr; 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 (attr == null) attr = new TableAttribute();
if (string.IsNullOrEmpty(attr.Name)) attr.Name = trytb.Name; 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; if (string.IsNullOrEmpty(attr.SelectFilter)) attr.SelectFilter = trytb.SelectFilter;
return attr; 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; 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 (trytb._columns.TryGetValue(proto.Name, out var trycol) == false) return attr;
if (attr == null) attr = new ColumnAttribute(); if (attr == null) attr = new ColumnAttribute();

View File

@ -272,5 +272,6 @@ where a.table_schema in ({0}) and a.table_name in ({1})".FormatMySql(tboldname ?
} }
} }
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
} }
} }

View File

@ -310,5 +310,6 @@ where owner={{0}} and table_name={{1}}".FormatOracleSQL(tboldname ?? tbname);
} }
} }
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
} }
} }

View File

@ -324,5 +324,6 @@ where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contyp
} }
} }
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
} }
} }

View File

@ -282,5 +282,6 @@ use " + database, tboldname ?? tbname);
} }
} }
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
} }
} }

View File

@ -240,5 +240,6 @@ namespace FreeSql.Sqlite {
} }
} }
public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity); public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
} }
} }