diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 537315e2..26522f10 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -800,5 +800,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs index 8ac1e221..f6ea7ea0 100644 --- a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs @@ -1569,6 +1569,84 @@ WHERE (((cast(a.[Id] as nvarchar(100))) in (SELECT TOP 10 b.[Title] .IncludeByPropertyName("Songs") .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) .ToList(); + var tags3339 = g.sqlserver.Select() + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + tags3339 + .IncludeByPropertyName(g.sqlserver, "Tags", + then: then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName(g.sqlserver, "Parent") + .IncludeByPropertyName(g.sqlserver, "Songs"); + + var tags33331 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .IncludeByPropertyName("Tags") + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + var tags333319 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + tags333319.IncludeByPropertyName(g.sqlserver, "Tags"); + + var tags3333 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .IncludeByPropertyName("Tags", + then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName("Parent") + .IncludeByPropertyName("Songs") + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + var tags33339 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + tags33339 + .IncludeByPropertyName(g.sqlserver, "Tags", + then: then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName(g.sqlserver, "Parent") + .IncludeByPropertyName(g.sqlserver, "Songs"); + + var tags33333 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .IncludeByPropertyName("Parent.Tags", + then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName("Songs") + .Where(a => (a as Tag).Id == tag1_1.Id || (a as Tag).Id == tag1_1.Id) + .ToList(); + var tags333339 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .Where(a => (a as Tag).Id == tag1_1.Id || (a as Tag).Id == tag1_1.Id) + .ToList(); + tags333339 + .IncludeByPropertyName(g.sqlserver, "Parent.Tags", + then: then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName(g.sqlserver, "Songs"); + + var tags333333 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .IncludeByPropertyName("Parent.Tags", + then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName("Songs") + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + var tags3333339 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .Where(a => (a as Tag).Id == tag1.Id || (a as Tag).Id == tag2.Id) + .ToList(); + tags3333339 + .IncludeByPropertyName(g.sqlserver, "Parent.Tags", + select: "Id, Name", + then: then => then.IncludeByPropertyName("Parent").IncludeByPropertyName("Songs").IncludeByPropertyName("Tags")) + .IncludeByPropertyName(g.sqlserver, "Songs"); + var tags33333391 = g.sqlserver.Select() + .AsType(typeof(Tag)) + .Where(a => (a as Tag).Id == tag1_1.Id || (a as Tag).Id == tag2_1.Id) + .ToList(); + tags33333391 + .IncludeByPropertyName(g.sqlserver, "Parent.Tags", + select: "Id, Name") + .IncludeByPropertyName(g.sqlserver, "Songs"); var tags11 = g.sqlserver.Select() .IncludeMany(a => a.Tags.Take(1)) diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index 35ae6016..3522ebcf 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -378,15 +378,16 @@ public static partial class FreeSqlGlobalExtensions #endif IncludeByPropertyNameSyncOrAsync(bool isAsync, List list, IFreeSql orm, string property, string where, int take, string select, Expression>> then) where T1 : class { + if (list?.Any() != true) return list; + var entityType = typeof(T1) == typeof(object) ? list[0].GetType() : typeof(T1); + var t1tb = orm.CodeFirst.GetTableByEntity(entityType); if (orm.CodeFirst.IsAutoSyncStructure) { - var tb = orm.CodeFirst.GetTableByEntity(typeof(T1)); - if (tb == null || tb.Primarys.Any() == false) - (orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(T1)); //._dicSyced.TryAdd(typeof(TReturn), true); + if (t1tb == null || t1tb.Primarys.Any() == false) + (orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(entityType); //._dicSyced.TryAdd(typeof(TReturn), true); } var props = property.Split('.'); - var t1tb = orm.CodeFirst.GetTableByEntity(typeof(T1)); - var t1sel = orm.Select() as Select1Provider; + var t1sel = orm.Select().AsType(entityType) as Select1Provider; var t1expFul = t1sel.ConvertStringPropertyToExpression(property, true); var t1exp = props.Length == 1 ? t1expFul : t1sel.ConvertStringPropertyToExpression(props[0], true); if (t1expFul == null) throw new ArgumentException(CoreStrings.Cannot_Resolve_ExpressionTree(nameof(property))); @@ -395,7 +396,7 @@ public static partial class FreeSqlGlobalExtensions { if (props.Length > 1) IncludeByPropertyName(list, orm, string.Join(".", props.Take(props.Length - 1))); - var imsel = IncludeManyByPropertyNameCommonGetSelect(orm, property, where, take, select, then); + var imsel = IncludeManyByPropertyNameCommonGetSelect(orm, entityType, property, where, take, select, then); #if net40 imsel.SetList(list); #else @@ -409,10 +410,8 @@ public static partial class FreeSqlGlobalExtensions var reftb = orm.CodeFirst.GetTableByEntity(t1exp.Type); var refsel = orm.Select().AsType(t1exp.Type) as Select1Provider; if (props.Length > 1) - { - var refexp = refsel.ConvertStringPropertyToExpression(string.Join(".", props.Skip(1)), true); - refsel.Include(Expression.Lambda>(refexp, refsel._tables[0].Parameter)); - } + refsel.IncludeByPropertyName(string.Join(".", props.Skip(1))); + var listdic = list.Select(item => { var refitem = t1exp.Type.CreateInstanceGetDefaultValue(); @@ -444,15 +443,15 @@ public static partial class FreeSqlGlobalExtensions }); return list; } - static Select1Provider IncludeManyByPropertyNameCommonGetSelect(IFreeSql orm, string property, string where, int take, string select, Expression>> then) where T1 : class + static Select1Provider IncludeManyByPropertyNameCommonGetSelect(IFreeSql orm, Type entityType, string property, string where, int take, string select, Expression>> then) { if (orm.CodeFirst.IsAutoSyncStructure) { - var tb = orm.CodeFirst.GetTableByEntity(typeof(T1)); + var tb = orm.CodeFirst.GetTableByEntity(entityType); if (tb == null || tb.Primarys.Any() == false) - (orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(T1)); //._dicSyced.TryAdd(typeof(TReturn), true); + (orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(entityType); //._dicSyced.TryAdd(typeof(TReturn), true); } - var sel = orm.Select() as Select1Provider; + var sel = orm.Select().AsType(entityType) as Select1Provider; var exp = sel.ConvertStringPropertyToExpression(property, true); if (exp == null) throw new ArgumentException(CoreStrings.Cannot_Resolve_ExpressionTree(nameof(property))); var memExp = exp as MemberExpression; @@ -521,7 +520,13 @@ public static partial class FreeSqlGlobalExtensions newthen = newthenLambda.Compile(); } - var funcType = typeof(Func<,>).MakeGenericType(sel._tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(reftb.Type)); + var funcType = typeof(Func<,>).MakeGenericType(typeof(object), typeof(IEnumerable<>).MakeGenericType(reftb.Type)); + if (sel._tables[0].Table.Type != typeof(object)) + { + var expParm = Expression.Parameter(typeof(object), sel._tables[0].Alias); + exp = new Select0Provider.ReplaceMemberExpressionVisitor().Replace(exp, sel._tables[0].Parameter, Expression.Convert(expParm, sel._tables[0].Table.Type)); + sel._tables[0].Parameter = expParm; + } var navigateSelector = Expression.Lambda(funcType, exp, sel._tables[0].Parameter); var incMethod = sel.GetType().GetMethod("IncludeMany"); if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany); diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 91120bfb..a8cd52d5 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3806,184 +3806,201 @@ - - 发生的错误 + 反序列化 - - - - 耗时(单位:Ticks) - - - - - 耗时(单位:毫秒) - - - - - 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 - - - - - 转小写同步结构,适用 PostgreSQL - - - - - 转大写同步结构,适用 Oracle/达梦/人大金仓 - - - - - 将数据库的主键、自增、索引设置导入,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql/oracle。 - 本功能会影响 IFreeSql 首次访问的速度。 - 若使用 CodeFirst 创建索引后,又直接在数据库上建了索引,若无本功能下一次 CodeFirst 迁移时数据库上创建的索引将被删除 - - - - - 不使用命令参数化执行,针对 Insert/Update - - - - - 是否生成命令参数化执行,针对 lambda 表达式解析 - 注意:常量不会参数化,变量才会做参数化 - var id = 100; - fsql.Select<T>().Where(a => a.id == id) 会参数化 - fsql.Select<T>().Where(a => a.id == 100) 不会参数化 - - - - - 延时加载导航属性对象,导航属性需要声明 virtual - - - - - 将实体类型与数据库对比,返回DDL语句 - - + - + - 将实体类型集合与数据库对比,返回DDL语句 - - 实体类型 - - - - - 将实体类型与数据库对比,返回DDL语句(指定表名) - - 实体类型 - 指定表名对比 - - - - - 同步实体类型到数据库 - 注意:生产环境中谨慎使用 - - - - - - 同步实体类型集合到数据库 - 注意:生产环境中谨慎使用 - - - - - - 同步实体类型到数据库(指定表名) - 注意:生产环境中谨慎使用 - - 实体类型 - 指定表名对比 - 强制同步结构,无视缓存每次都同步 - - - - 根据 System.Type 获取数据库信息 - - - - - - - FreeSql FluentApi 配置实体,方法名与特性相同 - - - - - - - - FreeSql FluentApi 配置实体,方法名与特性相同 - - - - - - - - 获取 FreeSql FluentApi 配置实体的元数据 - - - 未使用ConfigEntity配置时,返回null - - - - 获取实体类核心配置 - - - - - - - 获取所有数据库 - - - - - - 获取指定数据库的表信息,包括表、列详情、主键、唯一键、索引、外键、备注 + 获取数据库枚举类型,适用 PostgreSQL - + - 获取指定单表信息,包括列详情、主键、唯一键、索引、备注 + 临时 LambdaExpression.Parameter - 表名,如:dbo.table1 - 是否忽略大小写 + + + + 如果实体类有自增属性,分成两个 List,有值的Item1 merge,无值的Item2 insert + + - + - 判断表是否存在 + AsType, Ctor, ClearData 三处地方需要重新加载 - 表名,如:dbo.table1 - 是否忽略大小写 + + + + AsType, Ctor, ClearData 三处地方需要重新加载 + + + + + 动态读取 DescriptionAttribute 注释文本 + + - + - 获取数据库枚举类型int值 + 通过属性的注释文本,通过 xml 读取 - + + Dict:key=属性名,value=注释 + + + + 更新实体的元数据 + + + + + 执行更新的 SQL + + + + + 执行更新命令的参数 + + + + + 执行更新命令影响的行 + + + + + 更新的实体数量 + + + + + 更新的实体 + + + + + 映射优先级,默认: Attribute > FluentApi > Aop + + + + + 实体特性 + [Table(Name = "tabname")] + [Column(Name = "table_id")] + + + + + 流式接口 + fsql.CodeFirst.ConfigEntity(a => a.Name("tabname")) + fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) + + + + + AOP 特性 https://github.com/dotnetcore/FreeSql/wiki/AOP + fsql.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = "public.tabname"; + fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = "table_id"; + + + + + 不进行任何处理 + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串 + + BigApple -> Big_Apple + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 + + BigApple -> BIG_APPLE + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 + + BigApple -> big_apple + + + + + 将字符串转换为大写 + + BigApple -> BIGAPPLE + + + + + 将字符串转换为小写 + + BigApple -> bigapple + + + + + 不进行任何处理 + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串 + + BigApple -> Big_Apple + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 + + BigApple -> BIG_APPLE + + + + + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 + + BigApple -> big_apple + + + + + 将字符串转换为大写 + + BigApple -> BIGAPPLE + + + + + 将字符串转换为小写 + + BigApple -> bigapple + + + + + 创建一个过滤器 + 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + + + 名字 + 表达式 - - - 获取c#转换,(int)、(long) - - + diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 7012550c..325222d1 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -498,6 +498,8 @@ namespace FreeSql.Internal.CommonProvider protected override Expression VisitMember(MemberExpression node) { if (_findExp == node) return _replaceExp; + if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == _findExp) + return Expression.Property(_replaceExp, node.Member.Name); return base.VisitMember(node); } } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 07a10f2f..5ab05599 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -491,7 +491,13 @@ namespace FreeSql.Internal.CommonProvider case TableRefType.ManyToMany: case TableRefType.OneToMany: case TableRefType.PgArrayToMany: - var funcType = typeof(Func<,>).MakeGenericType(_tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); + var funcType = typeof(Func<,>).MakeGenericType(typeof(T1), typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); + if (_tables[0].Table.Type != typeof(T1)) + { + var expParm = Expression.Parameter(typeof(T1), _tables[0].Alias); + exp = new ReplaceMemberExpressionVisitor().Replace(exp, _tables[0].Parameter, Expression.Convert(expParm, _tables[0].Table.Type)); + _tables[0].Parameter = expParm; + } var navigateSelector = Expression.Lambda(funcType, exp, _tables[0].Parameter); var incMethod = this.GetType().GetMethod("IncludeMany"); if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany); @@ -580,6 +586,15 @@ namespace FreeSql.Internal.CommonProvider param = tmpExp as ParameterExpression; isbreak = true; break; + case ExpressionType.Convert: + var convertExp = tmpExp as UnaryExpression; + if (convertExp?.Operand.NodeType == ExpressionType.Parameter) + { + param = convertExp.Operand as ParameterExpression; + isbreak = true; + break; + } + throw new Exception(CoreStrings.Expression_Error_Use_Successive_MemberAccess_Type(exp)); default: throw new Exception(CoreStrings.Expression_Error_Use_Successive_MemberAccess_Type(exp)); } @@ -769,6 +784,7 @@ namespace FreeSql.Internal.CommonProvider var t1parm = Expression.Parameter(typeof(T1)); Expression membersExp = t1parm; Expression membersExpNotNull = null; + if (typeof(T1) != _tables[0].Table.Type) membersExp = Expression.TypeAs(membersExp, _tables[0].Table.Type); foreach (var mem in members) { membersExp = Expression.MakeMemberAccess(membersExp, mem.Member); @@ -863,6 +879,7 @@ namespace FreeSql.Internal.CommonProvider return getListValue1(item, propName); }; + if (list.Where(a => getListValue1(a, "") == null).Count() == list.Count) return; //Parent.Childs 当 parent 都是 NULL 就没有和要向下查询了 foreach (var item in list) setListValue(item, null);