Description
Hi, it's me again ;).
I wanted to make my life easier and I am now stopping to map two properties to one column.
So I made a custom Type which is like an Enum but i can have also a custom String, if it doesn't fit.
When i tried to use it in a query like this
db.GetTable<MasterClass>()
.Where(m => m.Value == AnredeAuswahlliste.Herr)
.ToList();
Linq2Db is happy. But when i try to use it in an "In"-Extension Method it breaks.
var test2 = db.GetTable<MasterClass>()
.Where(m => m.Value.In<AnredeAuswahlliste>(AnredeAuswahlliste.Frau, AnredeAuswahlliste.Herr))
.ToList();
Exception message: LinqToDB.LinqToDBException: 'Cannot convert value of type Tests.Linq.EagerLoadingTests+AnredeAuswahlliste to SQL'
Stack trace:
bei LinqToDB.SqlProvider.ValueToSqlConverter.Convert(StringBuilder stringBuilder, SqlDataType dataType, Object value) in C:\git\linq2db\Source\LinqToDB\SqlProvider\ValueToSqlConverter.cs:Zeile 230.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildValue(SqlDataType dataType, Object value) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2520.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildExpression(ISqlExpression expr, Boolean buildTableName, Boolean checkParentheses, String alias, Boolean& addAlias, Boolean throwExceptionIfTableNotFound) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2355.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildExpression(ISqlExpression expr) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2489.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildInListValues(InList predicate, IEnumerable values) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2154.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildInListPredicate(ISqlPredicate predicate) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2086.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildPredicate(ISqlPredicate predicate) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 1864.
bei LinqToDB.DataProvider.SQLite.SQLiteSqlBuilder.BuildPredicate(ISqlPredicate predicate) in C:\git\linq2db\Source\LinqToDB\DataProvider\SQLite\SQLiteSqlBuilder.cs:Zeile 157.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildPredicate(Int32 parentPrecedence, Int32 precedence, ISqlPredicate predicate) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 2193.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSearchCondition(SqlSearchCondition condition) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 1794.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSearchCondition(Int32 parentPrecedence, SqlSearchCondition condition) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 1805.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildWhereSearchCondition(SelectQuery selectQuery, SqlSearchCondition condition) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 1759.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildWhereClause(SelectQuery selectQuery) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 1536.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSelectQuery(SqlSelectStatement selectStatement) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 283.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSql() in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 213.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSql(Int32 commandNumber, SqlStatement statement, StringBuilder sb, Int32 indent, Boolean skipAlias) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 131.
bei LinqToDB.SqlProvider.BasicSqlBuilder.BuildSql(Int32 commandNumber, SqlStatement statement, StringBuilder sb, Int32 startIndent) in C:\git\linq2db\Source\LinqToDB\SqlProvider\BasicSqlBuilder.cs:Zeile 92.
bei LinqToDB.Data.DataConnection.QueryRunner.GetCommand(DataConnection dataConnection, IQueryContext query, Int32 startIndent) in C:\git\linq2db\Source\LinqToDB\Data\DataConnection.QueryRunner.cs:Zeile 189.
bei LinqToDB.Data.DataConnection.QueryRunner.SetQuery(DataConnection dataConnection, IQueryContext queryContext, Int32 startIndent) in C:\git\linq2db\Source\LinqToDB\Data\DataConnection.QueryRunner.cs:Zeile 253.
bei LinqToDB.Data.DataConnection.QueryRunner.SetQuery() in C:\git\linq2db\Source\LinqToDB\Data\DataConnection.QueryRunner.cs:Zeile 262.
bei LinqToDB.Linq.QueryRunnerBase.SetCommand(Boolean clearQueryHints) in C:\git\linq2db\Source\LinqToDB\Linq\QueryRunnerBase.cs:Zeile 70.
bei LinqToDB.Data.DataConnection.QueryRunner.ExecuteReader() in C:\git\linq2db\Source\LinqToDB\Data\DataConnection.QueryRunner.cs:Zeile 433.
bei LinqToDB.Linq.QueryRunner.<ExecuteQuery>d__13`1.MoveNext() in C:\git\linq2db\Source\LinqToDB\Linq\QueryRunner.cs:Zeile 570.
bei System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
bei System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
bei Tests.Linq.EagerLoadingTests.TestLoadWith(String context) in C:\git\linq2db\Tests\Tests.Playground\TestTemplate.cs:Zeile 327.
I Tried Converters, to help the Query Builder, but it didn't help.
db.MappingSchema.SetConverter<string, AnredeAuswahlliste>(s => new AnredeAuswahlliste(s));
db.MappingSchema.SetConverter<AnredeAuswahlliste, string>(s => s.Value);
It helps, when i use SetValueToSqlConverter. But then i have to Escape it myself and i think there has to be a better way.
db.MappingSchema.SetValueToSqlConverter(typeof(AnredeAuswahlliste), (builder, type, val) => builder.Append("'" + ((AnredeAuswahlliste)val).Value + "'"));
Steps to reproduce
I use you're Unittests again. (EagerLoadingTests).
[Table]
class MasterClass
{
[Column] [PrimaryKey] public int Id1 { get; set; }
[Column] [PrimaryKey] public int Id2 { get; set; }
private string? _value;
[Column(DataType = DataType.NVarChar)]
public AnredeAuswahlliste? Value
{
get => new AnredeAuswahlliste(_value);
set => _value = value?.Value;
}
[Column] public byte[]? ByteValues { get; set; }
[Association(ThisKey = nameof(Id1), OtherKey = nameof(DetailClass.MasterId))]
public List<DetailClass> Details { get; set; } = null!;
[Association(QueryExpressionMethod = nameof(DetailsQueryImpl))]
public DetailClass[] DetailsQuery { get; set; } = null!;
static Expression<Func<MasterClass, IDataContext, IQueryable<DetailClass>>> DetailsQueryImpl()
{
return (m, dc) => dc.GetTable<DetailClass>().Where(d => d.MasterId == m.Id1 && d.MasterId == m.Id2 && d.DetailId % 2 == 0);
}
}
public class AnredeAuswahlliste
{
public string Value { get; set; }
public const string Herr = "Herr";
public const string Frau = "Frau";
public AnredeAuswahlliste(string value)
{
Value = value;
}
public static implicit operator AnredeAuswahlliste(string value)
=> new AnredeAuswahlliste(value);
public static implicit operator string(AnredeAuswahlliste auswahlliste)
=> auswahlliste.Value;
public static bool operator ==(AnredeAuswahlliste leftSide, string rightSide)
=> leftSide?.Value == rightSide;
public static bool operator !=(AnredeAuswahlliste leftSide, string rightSide)
=> leftSide?.Value != rightSide;
}
[Test]
public void TestLoadWith([IncludeDataSources(TestProvName.AllSQLite)] string context)
{
var (masterRecords, detailRecords) = GenerateData();
var intParam = 0;
using (new AllowMultipleQuery())
using (var db = GetDataContext(context))
using (var master = db.CreateLocalTable(masterRecords))
using (var detail = db.CreateLocalTable(detailRecords))
{
db.MappingSchema.SetConverter<string, AnredeAuswahlliste>(s => new AnredeAuswahlliste(s));
db.MappingSchema.SetConverter<AnredeAuswahlliste, string>(s => s.Value);
// db.MappingSchema.SetValueToSqlConverter(typeof(AnredeAuswahlliste), (builder, type, val) => builder.Append("'" + ((AnredeAuswahlliste)val).Value + "'"));
var works = db.GetTable<MasterClass>()
.Where(m => m.Value == AnredeAuswahlliste.Frau)
.ToList();
try
{
var crashes = db.GetTable<MasterClass>()
.Where(m => m.Value.In<AnredeAuswahlliste>(AnredeAuswahlliste.Frau, AnredeAuswahlliste.Herr))
.ToList();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
Maybe you can give me a tip what is wrong, or what i have to map to make the queries work, even with "In"-Extensions.
Environment details
linq2db version: 3.1.5
Database Server: SqlServer
Database Provider: SqlServer
Operating system: Win10
.NET Framework: .Net Framework 4.7.2