using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Data.Common; using System.Linq; using System.Reflection; using FreeSql.DataAnnotations; using FreeSql.Internal; namespace FreeSql { public partial class FreeSqlBuilder { DataType _dataType; string _masterConnectionString; string[] _slaveConnectionString; Func _connectionFactory; bool _isAutoSyncStructure = false; bool _isSyncStructureToLower = false; bool _isSyncStructureToUpper = false; bool _isConfigEntityFromDbFirst = false; bool _isNoneCommandParameter = false; bool _isGenerateCommandParameterWithLambda = false; bool _isLazyLoading = false; StringConvertType _entityPropertyConvertType = StringConvertType.None; NameConvertType _nameConvertType = NameConvertType.None; Action _aopCommandExecuting = null; Action _aopCommandExecuted = null; Type _providerType = null; /// /// 使用连接串(推荐) /// /// 数据库类型 /// 数据库连接串 /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 /// public FreeSqlBuilder UseConnectionString(DataType dataType, string connectionString, Type providerType = null) { if (_connectionFactory != null) throw new Exception("已经指定了 UseConnectionFactory,不能再指定 UseConnectionString"); _dataType = dataType; _masterConnectionString = connectionString; _providerType = providerType; return this; } /// /// 使用从数据库,支持多个 /// /// 从数据库连接串 /// public FreeSqlBuilder UseSlave(params string[] slaveConnectionString) { if (_connectionFactory != null) throw new Exception("已经指定了 UseConnectionFactory,不能再指定 UseSlave"); _slaveConnectionString = slaveConnectionString; return this; } /// /// 使用自定义数据库连接对象(放弃内置对象连接池技术) /// /// 数据库类型 /// 数据库连接对象创建器 /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 /// public FreeSqlBuilder UseConnectionFactory(DataType dataType, Func connectionFactory, Type providerType = null) { if (string.IsNullOrEmpty(_masterConnectionString) == false) throw new Exception("已经指定了 UseConnectionString,不能再指定 UseConnectionFactory"); if (_slaveConnectionString?.Any() == true) throw new Exception("已经指定了 UseSlave,不能再指定 UseConnectionFactory"); _dataType = dataType; _connectionFactory = connectionFactory; _providerType = providerType; return this; } /// /// 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 /// /// true:运行时检查自动同步结构, false:不同步结构 /// public FreeSqlBuilder UseAutoSyncStructure(bool value) { _isAutoSyncStructure = value; return this; } /// /// 将数据库的主键、自增、索引设置导入,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql/oracle。 /// 本功能会影响 IFreeSql 首次访问的速度。 /// 若使用 CodeFirst 创建索引后,又直接在数据库上建了索引,若无本功能下一次 CodeFirst 迁移时数据库上创建的索引将被删除 /// /// /// public FreeSqlBuilder UseConfigEntityFromDbFirst(bool value) { _isConfigEntityFromDbFirst = value; return this; } /// /// 不使用命令参数化执行,针对 Insert/Update,也可临时使用 IInsert/IUpdate.NoneParameter() /// /// /// public FreeSqlBuilder UseNoneCommandParameter(bool value) { _isNoneCommandParameter = value; return this; } /// /// 是否生成命令参数化执行,针对 lambda 表达式解析 /// 注意:常量不会参数化,变量才会做参数化 /// var id = 100; /// fsql.Select<T>().Where(a => a.id == id) 会参数化 /// fsql.Select<T>().Where(a => a.id == 100) 不会参数化 /// /// /// public FreeSqlBuilder UseGenerateCommandParameterWithLambda(bool value) { _isGenerateCommandParameterWithLambda = value; return this; } /// /// 延时加载导航属性对象,导航属性需要声明 virtual /// /// /// public FreeSqlBuilder UseLazyLoading(bool value) { _isLazyLoading = value; return this; } /// /// 监视数据库命令对象 /// /// 执行前 /// 执行后,可监视执行性能 /// public FreeSqlBuilder UseMonitorCommand(Action executing, Action executed = null) { _aopCommandExecuting = executing; _aopCommandExecuted = executed; return this; } /// /// 实体类名 -> 数据库表名,命名转换(类名、属性名都生效) /// 优先级小于 [Table(Name = "xxx")]、[Column(Name = "xxx")] /// /// /// public FreeSqlBuilder UseNameConvert(NameConvertType convertType) { _nameConvertType = convertType; return this; } public IFreeSql Build() => Build(); public IFreeSql Build() { if (string.IsNullOrEmpty(_masterConnectionString) && _connectionFactory == null) throw new Exception("参数 masterConnectionString 不可为空,请检查 UseConnectionString"); IFreeSql ret = null; var type = _providerType; if (type?.IsGenericType == true) type = type.MakeGenericType(typeof(TMark)); if (type == null) { Action throwNotFind = (dll, providerType) => throw new Exception($"缺少 FreeSql 数据库实现包:{dll},可前往 nuget 下载;如果存在 {dll} 依然报错(原因是环境问题导致反射不到类型),请在 UseConnectionString/UseConnectionFactory 第三个参数手工传入 typeof({providerType})"); switch (_dataType) { case DataType.MySql: type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySql")?.MakeGenericType(typeof(TMark)); if (type == null) type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.MySql.dll", "FreeSql.MySql.MySqlProvider<>"); break; case DataType.SqlServer: type = Type.GetType("FreeSql.SqlServer.SqlServerProvider`1,FreeSql.Provider.SqlServer")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.SqlServer.dll", "FreeSql.SqlServer.SqlServerProvider<>"); break; case DataType.PostgreSQL: type = Type.GetType("FreeSql.PostgreSQL.PostgreSQLProvider`1,FreeSql.Provider.PostgreSQL")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.PostgreSQL.dll", "FreeSql.PostgreSQL.PostgreSQLProvider<>"); break; case DataType.Oracle: type = Type.GetType("FreeSql.Oracle.OracleProvider`1,FreeSql.Provider.Oracle")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Oracle.dll", "FreeSql.Oracle.OracleProvider<>"); break; case DataType.Sqlite: type = Type.GetType("FreeSql.Sqlite.SqliteProvider`1,FreeSql.Provider.Sqlite")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Sqlite.dll", "FreeSql.Sqlite.SqliteProvider<>"); break; case DataType.OdbcOracle: type = Type.GetType("FreeSql.Odbc.Oracle.OdbcOracleProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.Oracle.OdbcOracleProvider<>"); break; case DataType.OdbcSqlServer: type = Type.GetType("FreeSql.Odbc.SqlServer.OdbcSqlServerProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.SqlServer.OdbcSqlServerProvider<>"); break; case DataType.OdbcMySql: type = Type.GetType("FreeSql.Odbc.MySql.OdbcMySqlProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.MySql.OdbcMySqlProvider<>"); break; case DataType.OdbcPostgreSQL: type = Type.GetType("FreeSql.Odbc.PostgreSQL.OdbcPostgreSQLProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.PostgreSQL.OdbcPostgreSQLProvider<>"); break; case DataType.Odbc: type = Type.GetType("FreeSql.Odbc.Default.OdbcProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.Default.OdbcProvider<>"); break; case DataType.OdbcDameng: type = Type.GetType("FreeSql.Odbc.Dameng.OdbcDamengProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.Dameng.OdbcDamengProvider<>"); break; case DataType.MsAccess: type = Type.GetType("FreeSql.MsAccess.MsAccessProvider`1,FreeSql.Provider.MsAccess")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.MsAccess.dll", "FreeSql.MsAccess.MsAccessProvider<>"); break; case DataType.Dameng: type = Type.GetType("FreeSql.Dameng.DamengProvider`1,FreeSql.Provider.Dameng")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Dameng.dll", "FreeSql.Dameng.DamengProvider<>"); break; default: throw new Exception("未指定 UseConnectionString 或者 UseConnectionFactory"); } } ret = Activator.CreateInstance(type, new object[] { _masterConnectionString, _slaveConnectionString, _connectionFactory }) as IFreeSql; if (ret != null) { ret.CodeFirst.IsAutoSyncStructure = _isAutoSyncStructure; ret.CodeFirst.IsSyncStructureToLower = _isSyncStructureToLower; ret.CodeFirst.IsSyncStructureToUpper = _isSyncStructureToUpper; ret.CodeFirst.IsConfigEntityFromDbFirst = _isConfigEntityFromDbFirst; ret.CodeFirst.IsNoneCommandParameter = _isNoneCommandParameter; ret.CodeFirst.IsGenerateCommandParameterWithLambda = _isGenerateCommandParameterWithLambda; ret.CodeFirst.IsLazyLoading = _isLazyLoading; if (_aopCommandExecuting != null) ret.Aop.CommandBefore += new EventHandler((s, e) => { _aopCommandExecuting?.Invoke(e.Command); }); if (_aopCommandExecuted != null) ret.Aop.CommandAfter += new EventHandler((s, e) => { _aopCommandExecuted?.Invoke(e.Command, e.Log); }); this.EntityPropertyNameConvert(ret); //添加实体属性名全局AOP转换处理 if (_nameConvertType != NameConvertType.None) { string PascalCaseToUnderScore(string str) => string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())); //string UnderScorePascalCase(string str) => string.Join("", str.Split('_').Select(a => a.Length > 0 ? string.Concat(char.ToUpper(a[0]), a.Substring(1)) : "")); switch (_nameConvertType) { case NameConvertType.ToLower: ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = e.EntityType.Name.ToLower(); ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = e.Property.Name.ToLower(); ret.CodeFirst.IsSyncStructureToLower = true; break; case NameConvertType.ToUpper: ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = e.EntityType.Name.ToUpper(); ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = e.Property.Name.ToUpper(); ret.CodeFirst.IsSyncStructureToUpper = true; break; case NameConvertType.PascalCaseToUnderscore: ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.EntityType.Name); ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.Property.Name); break; case NameConvertType.PascalCaseToUnderscoreWithLower: ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.EntityType.Name).ToLower(); ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.Property.Name).ToLower(); break; case NameConvertType.PascalCaseToUnderscoreWithUpper: ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.EntityType.Name).ToUpper(); ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.Property.Name).ToUpper(); break; //case NameConvertType.UnderscoreToPascalCase: // ret.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = UnderScorePascalCase(e.EntityType.Name); // ret.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = UnderScorePascalCase(e.Property.Name); // break; default: break; } } //处理 MaxLength、EFCore 特性 ret.Aop.ConfigEntityProperty += new EventHandler((s, e) => { object[] attrs = null; try { attrs = e.Property.GetCustomAttributes(false).ToArray(); //.net core 反射存在版本冲突问题,导致该方法异常 } catch { } var dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.Name == "MaxLengthAttribute"; }).FirstOrDefault(); if (dyattr != null) { var lenProp = dyattr.GetType().GetProperties().Where(a => a.PropertyType.IsNumberType()).FirstOrDefault(); if (lenProp != null && int.TryParse(string.Concat(lenProp.GetValue(dyattr, null)), out var tryval) && tryval != 0) { e.ModifyResult.StringLength = tryval; } } dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.RequiredAttribute"; }).FirstOrDefault(); if (dyattr != null) { e.ModifyResult.IsNullable = false; } dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute"; }).FirstOrDefault(); if (dyattr != null) { e.ModifyResult.IsIgnore = true; } dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.ColumnAttribute"; }).FirstOrDefault(); if (dyattr != null) { var name = dyattr.GetType().GetProperties().Where(a => a.PropertyType == typeof(string) && a.Name == "Name").FirstOrDefault()?.GetValue(dyattr, null)?.ToString(); short.TryParse(string.Concat(dyattr.GetType().GetProperties().Where(a => a.PropertyType == typeof(int) && a.Name == "Order").FirstOrDefault()?.GetValue(dyattr, null)), out var order); var typeName = dyattr.GetType().GetProperties().Where(a => a.PropertyType == typeof(string) && a.Name == "TypeName").FirstOrDefault()?.GetValue(dyattr, null)?.ToString(); if (string.IsNullOrEmpty(name) == false) e.ModifyResult.Name = name; if (order != 0) e.ModifyResult.Position = order; if (string.IsNullOrEmpty(typeName) == false) e.ModifyResult.DbType = typeName; } dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.KeyAttribute"; }).FirstOrDefault(); if (dyattr != null) { e.ModifyResult.IsPrimary = true; } }); //EFCore 特性 ret.Aop.ConfigEntity += new EventHandler((s, e) => { object[] attrs = null; try { attrs = e.EntityType.GetCustomAttributes(false).ToArray(); //.net core 反射存在版本冲突问题,导致该方法异常 } catch { } var dyattr = attrs?.Where(a => { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.TableAttribute"; }).FirstOrDefault(); if (dyattr != null) { var name = dyattr.GetType().GetProperties().Where(a => a.PropertyType == typeof(string) && a.Name == "Name").FirstOrDefault()?.GetValue(dyattr, null)?.ToString(); var schema = dyattr.GetType().GetProperties().Where(a => a.PropertyType == typeof(string) && a.Name == "Schema").FirstOrDefault()?.GetValue(dyattr, null)?.ToString(); if (string.IsNullOrEmpty(name) == false && string.IsNullOrEmpty(schema) == false) e.ModifyResult.Name = $"{schema}.{name}"; else if (string.IsNullOrEmpty(name) == false) e.ModifyResult.Name = name; else if (string.IsNullOrEmpty(schema) == false) e.ModifyResult.Name = $"{schema}.{e.EntityType.Name}"; } }); } return ret; } } }