Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug]Issue with linq2db in dotnet 7 ef core model with nullable fields #340

Open
aloksharma1 opened this issue Aug 15, 2023 · 12 comments
Open

Comments

@aloksharma1
Copy link

aloksharma1 commented Aug 15, 2023

hi i think this is a possible bug, can you check this query:

pageRepository.Query().Where(x => x.IsActive == true && x.SiteId == accountService.GetSiteId() && x.RecordStatus == RecordStatus.Published)
                   .Select(x => new SlugCache
                   {
                       SlugUrl = x.PageSlugUrl,
                       LastRequested = x.LastRequested,
                       DateModified = x.DateModified,
                       PageTypeName = pageTypesRepository.Query().Where(t => t.Id == x.PageTypeId).Select(t => t.PageTypeName).FirstOrDefault(),
                       IsHomePage = x.IsHomePage
                   }).ToList()

but if i add :

pageRepository.Query().Where(x => x.IsActive == true && x.SiteId == accountService.GetSiteId() && x.RecordStatus == RecordStatus.Published)
                   .Select(x => new SlugCache
                   {
                       SlugUrl = x.PageSlugUrl,
                       LastRequested = x.LastRequested,
                       DateModified = x.DateModified,
                       PageTypeName = pageTypesRepository.Query().Where(t => t.Id == x.PageTypeId).Select(t => t.PageTypeName).ToLinqToDB().FirstOrDefault(),
                       IsHomePage = x.IsHomePage
                   }).ToLinqToDB().ToList()

i am getting following error:

Expression 'x.IsActive' is not a Field.
its defined in model as public new bool? IsActive { get; set; } = true;

full stacktrace:

LinqToDB.Linq.LinqException: Expression 'x.IsActive' is not a Field.
   at LinqToDB.Linq.Builder.TableBuilder.TableContext.ConvertToSql(Expression expression, Int32 level, ConvertFlags flags)
   at LinqToDB.Linq.Builder.TableBuilder.TableContext.ConvertToIndex(Expression expression, Int32 level, ConvertFlags flags)
   at LinqToDB.Linq.Builder.SubQueryContext.ConvertToSql(Expression expression, Int32 level, ConvertFlags flags)
   at LinqToDB.Linq.Builder.ExpressionContext.ConvertToSql(Expression expression, Int32 level, ConvertFlags flags)
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertToSql(IBuildContext context, Expression expression, Boolean unwrap, ColumnDescriptor columnDescriptor, Boolean isPureExpression)
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertCompare(IBuildContext context, ExpressionType nodeType, Expression left, Expression right)
   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.BuildSearchCondition(IBuildContext context, Expression expression, List`1 conditions)
   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.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.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at LinqToDB.EntityFrameworkCore.Internal.LinqToDBForEFQueryProvider`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

please check linq2db.efcore version 7.5.0

@sdanyliv
Copy link
Member

If you add .ToLinqToDB() in projection? It has no sense.

@aloksharma1
Copy link
Author

ok, i removed it from projection but its the same .ToLinqToDB().ToList() the error didnt go away i am upgrading my project to latest dotnet it is working ok in net 5 version.

@sdanyliv
Copy link
Member

public new bool? IsActive { get; set; } = true;

How it is mapped to database?

@aloksharma1
Copy link
Author

using fluent api of ef core 7.0.10

builder.Property(e => e.IsActive).IsRequired(false).HasDefaultValueSql("1");

the thing is its working ok if i remove ToLinqToDB.

@aloksharma1
Copy link
Author

found it, in some place it was overriden by .IsRequired() it worked for ef core but gave error in linqtodb conversion, looks like linq2db is more strict.

@sdanyliv
Copy link
Member

Post your model classes. Base class and this one withIsActive. Will check what happened.

@aloksharma1
Copy link
Author

aloksharma1 commented Aug 27, 2023

Hi, sorry for late reply have been stuck in this project upgrade so hardly getting any time, the problem still persists as i have explained above, here is the model classes:

public abstract class BaseEntity : Framework.Domain.Common.BaseEntity, IEquatable<BaseEntity>, IBaseEntity<long>
{
    [Key]
    public new virtual long Id { get; set; }
    
    public new bool? IsActive { get; set; } = true;
    public new DateTimeOffset? DateCreated { get; set; } = DateTimeOffset.UtcNow;
    public new DateTimeOffset? DateModified { get; set; } = DateTimeOffset.UtcNow;

    [NotMapped]
    public virtual Constants.ActionType? ActionType { get; set; } = Constants.ActionType.Create;
    [NotMapped]
    public override bool IsModified { get; set; }
    public bool Equals([AllowNull] BaseEntity other)
    {
        return Equals(other);
    }
}
public abstract class GeneralEntity : BaseEntity, IEquatable<GeneralEntity>
{
    public virtual long SiteId { get; set; }

    public bool Equals([AllowNull] GeneralEntity other)
    {
        return Equals(other);
    }
}
    public class PageProperties : GeneralEntity
    {
        //to use bigger range on created records
        public virtual new Guid Id { get; set; } = Guid.NewGuid();

        //[StringLength(500)]
        //[Required]
        public virtual string? PageName { get; set; }
        //additional properties redacted   
        
        public virtual RecordStatus RecordStatus { get; set; }        
        public virtual DateTimeOffset LastRequested { get; set; } = DateTimeOffset.UtcNow;
    }
}
public class Pages : PageProperties
{
    public DateTimeOffset? PublishDate { get; set; }
}

my code is divide in many different modules, so there is a lot of abstract classes. Anyways the issue is due to nullable type handling that much i can tell.

@sdanyliv
Copy link
Member

Please add BaseEntity

@aloksharma1
Copy link
Author

aloksharma1 commented Aug 27, 2023

base entity is same as the above base entity just without nulls and extra cluttering

public abstract class BaseEntity : IBaseEntity<Guid>
{
    public virtual Guid Id { get; set; }
    
    public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset DateModified { get; set; } = DateTimeOffset.UtcNow;
    
    public bool IsActive { get; set; } = true;
    public bool IsModified { get; set; } =false;
}

@aloksharma1
Copy link
Author

aloksharma1 commented Sep 4, 2023

hi, were you able to find any solution to this issue? i am mostly stuck with union queries that were working previously like this one error it works individually but when I union it with some other table I get this error

Sequence 'value(LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection).GetTable().TagQuery("query called for Images")
.Where(pi => ((pi.Id == value(LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection).GetTable()
.TagQuery("query called for BlogPostImages").Where(pim => ((pim.IsActive == Convert(True, Nullable`1))
AndAlso (pim.BlogId == x.Id))).Select(pim => pim.PostImageId).FirstOrDefault()) AndAlso pi.isPublished))
.OrderBy(pi => pi.SortOrder)
.Select(pi => new SearchImages() {ImagePath = pi.ImagePath, ImageAltText = pi.ImageAltText, SortOrder = pi.SortOrder, Id = pi.Id}).ToList()'
cannot be converted to SQL.

Note: Using AsSplitQuery made it work, i didnt had to do that before as i am simply copying old code from dotnet 5 to 7.

@AntonC9018
Copy link

Hi, sorry for late reply have been stuck in this project upgrade so hardly getting any time, the problem still persists as i have explained above, here is the model classes:

public abstract class BaseEntity : Framework.Domain.Common.BaseEntity, IEquatable<BaseEntity>, IBaseEntity<long>
{
    [Key]
    public new virtual long Id { get; set; }
    
    public new bool? IsActive { get; set; } = true;
    public new DateTimeOffset? DateCreated { get; set; } = DateTimeOffset.UtcNow;
    public new DateTimeOffset? DateModified { get; set; } = DateTimeOffset.UtcNow;

    [NotMapped]
    public virtual Constants.ActionType? ActionType { get; set; } = Constants.ActionType.Create;
    [NotMapped]
    public override bool IsModified { get; set; }
    public bool Equals([AllowNull] BaseEntity other)
    {
        return Equals(other);
    }
}
public abstract class GeneralEntity : BaseEntity, IEquatable<GeneralEntity>
{
    public virtual long SiteId { get; set; }

    public bool Equals([AllowNull] GeneralEntity other)
    {
        return Equals(other);
    }
}
    public class PageProperties : GeneralEntity
    {
        //to use bigger range on created records
        public virtual new Guid Id { get; set; } = Guid.NewGuid();

        //[StringLength(500)]
        //[Required]
        public virtual string? PageName { get; set; }
        //additional properties redacted   
        
        public virtual RecordStatus RecordStatus { get; set; }        
        public virtual DateTimeOffset LastRequested { get; set; } = DateTimeOffset.UtcNow;
    }
}
public class Pages : PageProperties
{
    public DateTimeOffset? PublishDate { get; set; }
}

my code is divide in many different modules, so there is a lot of abstract classes. Anyways the issue is due to nullable type handling that much i can tell.

Why are you redefining those properties, and why are they virtual in the first place? Do you realize what new virtual does?

@aloksharma1
Copy link
Author

Hi, sorry for late reply have been stuck in this project upgrade so hardly getting any time, the problem still persists as i have explained above, here is the model classes:

public abstract class BaseEntity : Framework.Domain.Common.BaseEntity, IEquatable<BaseEntity>, IBaseEntity<long>
{
    [Key]
    public new virtual long Id { get; set; }
    
    public new bool? IsActive { get; set; } = true;
    public new DateTimeOffset? DateCreated { get; set; } = DateTimeOffset.UtcNow;
    public new DateTimeOffset? DateModified { get; set; } = DateTimeOffset.UtcNow;

    [NotMapped]
    public virtual Constants.ActionType? ActionType { get; set; } = Constants.ActionType.Create;
    [NotMapped]
    public override bool IsModified { get; set; }
    public bool Equals([AllowNull] BaseEntity other)
    {
        return Equals(other);
    }
}
public abstract class GeneralEntity : BaseEntity, IEquatable<GeneralEntity>
{
    public virtual long SiteId { get; set; }

    public bool Equals([AllowNull] GeneralEntity other)
    {
        return Equals(other);
    }
}
    public class PageProperties : GeneralEntity
    {
        //to use bigger range on created records
        public virtual new Guid Id { get; set; } = Guid.NewGuid();

        //[StringLength(500)]
        //[Required]
        public virtual string? PageName { get; set; }
        //additional properties redacted   
        
        public virtual RecordStatus RecordStatus { get; set; }        
        public virtual DateTimeOffset LastRequested { get; set; } = DateTimeOffset.UtcNow;
    }
}
public class Pages : PageProperties
{
    public DateTimeOffset? PublishDate { get; set; }
}

my code is divide in many different modules, so there is a lot of abstract classes. Anyways the issue is due to nullable type handling that much i can tell.

Why are you redefining those properties, and why are they virtual in the first place? Do you realize what new virtual does?

i am using these base classes in other places too, where i have to override these properties (old database structure is not same for all tables some are using int some guid some long, it a pain but it is what it is).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants