You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In a situation where FluentMappingBuilder is defining an association using expressions for both properties that are being used to join entities together, the generic extension is failing to traverse that association. When i access the same association later in my own LINQ query, it is successfully working.
Exception message: Association key 'MySpecialFieldForPermissionLinking' not found for type 'IHasPermissions`1[User].
Stack trace: linq2db
at LinqToDB.Linq.Builder.AssociationHelper.CreateAssociationQueryLambda(ExpressionBuilder builder, AccessorMember onMember, AssociationDescriptor association, Type parentOriginalType, Type parentType, Type objectType, Boolean inline, Boolean enforceDefault, List`1 loadWith, Boolean& isLeft)
at LinqToDB.Linq.Builder.TableBuilder.TableContext.GetContext(Expression expression, Int32 level, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionContext.GetContext(Expression expression, Int32 level, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.TableBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo)
at LinqToDB.Linq.Builder.AllAnyBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.GetSubQuery(IBuildContext context, MethodCallExpression expr)
at LinqToDB.Linq.Builder.ExpressionBuilder.GetSubQueryContext(IBuildContext context, MethodCallExpression expr)
at LinqToDB.Linq.Builder.ExpressionBuilder.SubQueryToSql(IBuildContext context, MethodCallExpression expression)
at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertToSql(IBuildContext context, Expression expression, Boolean unwrap, ColumnDescriptor columnDescriptor, Boolean isPureExpression)
at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertPredicate(IBuildContext context, Expression expression)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSearchCondition(IBuildContext context, Expression expression, List`1 conditions)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery, Boolean enforceHaving)
at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo)
at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo)
at LinqToDB.Linq.Builder.SelectBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo)
at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]()
at LinqToDB.Linq.Query`1.CreateQuery(ExpressionTreeOptimizationContext optimizationContext, ParametersContext parametersContext, IDataContext dataContext, Expression expr)
at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr, Boolean& dependsOnParameters)
at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache, Boolean& dependsOnParameters)
at LinqToDB.Linq.ExpressionQuery`1.get_SqlText()
at LinqToDB.Linq.ExpressionQueryImpl`1.ToString()
at System.IO.TextWriter.WriteLine(Object value)
at System.IO.TextWriter.SyncTextWriter.WriteLine(Object value)
at System.Console.WriteLine(Object value)
at Program.<Main>$(String[] args) in C:\src\Linq2DbPlayground\Linq2DbPlayground\Program.cs:line 21
Steps to reproduce
Here's a simplified example of "soft-linked" permission system.
Permission table has a Guid LinkId { get; set; } which defines a link to any object based on that Guid value. It has no foreign keys.
Any entity, that wants to use permissions system, must provide a linking guid in the form of Guid-provider expression and a List<Permission> Permissions property.
Later, when i access the Permissions property, the association works well.
But when i use extension method on based on IQueryable<IHasPermissions<T>>, i get an error.
I'm sorry for a bit complex example. This is how much i was able to consolidate the scenario to still produce an error.
(In real application, i have [RestrictedEntity] attribute, that adds a query filter that uses similar filtering like OnlyWithPermissions() is here.)
using LinqToDB;using LinqToDB.Data;using LinqToDB.Mapping;using System.Linq.Expressions;using System.Reflection;usingvardbCtx=new MyDbCtx(new DataOptions(new ConnectionOptions(){ConnectionString="Data Source=:memory:;Version=3;New=True;",ProviderName= ProviderName.SQLite
}));
dbCtx.CreateTable<User>();
dbCtx.CreateTable<Permission>();varquery= dbCtx.GetTable<User>().OnlyWithPermissions()// If you comment this out, it works.Where(x => x.Name =="MyNameSearch!").Select(x =>new{ x.Id, x.Name, x.Permissions.Count });// <- Here i can properly get the count!
Console.WriteLine(query);varresult= query.ToList();
Console.ReadLine();publicclassMyDbCtx:DataConnection{publicMyDbCtx(DataOptionsoptions):base(options){varbuilder=new FluentMappingBuilder();varbaseEntityType=typeof(IBaseEntity);varentityTypes= AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(x =>!x.IsAbstract&& x.IsClass&& x.IsPublic&& baseEntityType.IsAssignableFrom(x));varhasPermissionProcessorMethod=typeof(MyDbCtx).GetMethod(nameof(HasPermissionProcessor), BindingFlags.Static | BindingFlags.NonPublic)!;foreach(var entityType in entityTypes){varconcreteIHasPermission=typeof(IHasPermissions<>).MakeGenericType(entityType);if(entityType.IsAssignableTo(concreteIHasPermission)){
hasPermissionProcessorMethod.MakeGenericMethod(entityType).Invoke(null,[builder]);}}
builder.Build();
AddMappingSchema(builder.MappingSchema);}privatestaticvoidHasPermissionProcessor<T>(FluentMappingBuilderbuilder)whereT:IHasPermissions<T>{varpermissionIdGetterMethod=typeof(T).GetMethod(
nameof(IHasPermissions<T>.PermissionIdGetter),
BindingFlags.Static
| BindingFlags.NonPublic
| BindingFlags.Public
);varidGetter=(Expression<Func<T,Guid>>)permissionIdGetterMethod!.Invoke(null,[])!;
builder.Entity<T>().Association(x => x.Permissions,
idGetter,p => p.LinkId);}}publicrecordUser:IHasPermissions<User>,IBaseEntity{publicGuidId{get;set;}publicGuidMySpecialFieldForPermissionLinking{get;set;}publicstringName{get;set;}=null!;// From IHasPermission<>publicList<Permission> Permissions {get;set;}=null!;publicstaticExpression<Func<User,Guid>>PermissionIdGetter()=>x => x.MySpecialFieldForPermissionLinking;}publicrecordPermission:IBaseEntity{publicGuidId{get;set;}publicGuidLinkId{get;set;}publicstringGrant{get;set;}=null!;}publicinterfaceIHasPermissions<T>{// This is for versatility to choose based on what value permissions are linkedstaticabstractExpression<Func<T,Guid>>PermissionIdGetter();List<Permission> Permissions {get;set;}}publicstaticclassIHasPermissionsExtensions{publicstaticIQueryable<T>OnlyWithPermissions<T>(thisIQueryable<T>source)whereT:IHasPermissions<T>=> source.Where(x => x.Permissions.Any());// Just to access it, in real life it's more complex}publicinterfaceIBaseEntity{}
joonatanu-softwerk
changed the title
Fluent mapper based association fails during query filter. Work when just accessing in Linq
Fluent mapper + expression based association fails during query filter. Work when just accessing in Linq
May 17, 2024
joonatanu-softwerk
changed the title
Fluent mapper + expression based association fails during query filter. Work when just accessing in Linq
Fluent mapper + expression based association fails during query filter. Works when just accessing in Linq
May 17, 2024
joonatanu-softwerk
changed the title
Fluent mapper + expression based association fails during query filter. Works when just accessing in Linq
Fluent mapper + expression based association fails when using generic IQueryable extension. Works when just accessing in Linq
May 17, 2024
Describe your issue
In a situation where
FluentMappingBuilder
is defining an association using expressions for both properties that are being used to join entities together, the generic extension is failing to traverse that association. When i access the same association later in my own LINQ query, it is successfully working.Steps to reproduce
Here's a simplified example of "soft-linked" permission system.
Permission table has a
Guid LinkId { get; set; }
which defines a link to any object based on that Guid value. It has no foreign keys.Any entity, that wants to use permissions system, must provide a linking guid in the form of Guid-provider expression and a
List<Permission> Permissions
property.Later, when i access the
Permissions
property, the association works well.But when i use extension method on based on
IQueryable<IHasPermissions<T>>
, i get an error.I'm sorry for a bit complex example. This is how much i was able to consolidate the scenario to still produce an error.
(In real application, i have
[RestrictedEntity]
attribute, that adds a query filter that uses similar filtering likeOnlyWithPermissions()
is here.)Environment details
Linq To DB
version: 5.4.1Database (with version): SQLite
ADO.NET Provider (with version): System.Data.SQLite.Core 1.0.118
Operating system: Windows 11
.NET Version: 8.0
The text was updated successfully, but these errors were encountered: