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

InvalidCastException when table merging #1209

Closed
ichemidov opened this issue Jun 28, 2018 · 12 comments
Closed

InvalidCastException when table merging #1209

ichemidov opened this issue Jun 28, 2018 · 12 comments
Assignees
Labels
area: linq status: has-pr There is active PR for issue type: bug
Milestone

Comments

@ichemidov
Copy link

I'm trying update table Registry with List of my own class objects. I found that I should use temp tables for merge with my collection and then merge this temp table with target table.
But when I use Merge operation I'm getting InvalidCastException.

Exception message: System.InvalidCastException: Specified cast is not valid.
Stack trace:
   at LinqToDB.Linq.Builder.AllJoinsLinqBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\AllJoinsLinqBuilder.cs:line 44
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ContextParser.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ContextParser.cs:line 22
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]() in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 146
   at LinqToDB.Linq.Query`1.CreateQuery(IDataContext dataContext, Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 280
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 233
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 84
   at LinqToDB.Linq.ExpressionQuery`1.System.Linq.IQueryProvider.Execute[TResult](Expression expression) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 165
   at LinqToDB.DataProvider.BasicMergeBuilder`2.BuildPredicateByKeys(Type keyType, Expression targetKey, Expression sourceKey) in C:\projects\linq2db\Source\LinqToDB\DataProvider\BasicMergeBuilder.cs:line 97
   at LinqToDB.DataProvider.BasicMergeBuilder`2.BuildMatch() in C:\projects\linq2db\Source\LinqToDB\DataProvider\BasicMergeBuilder.cs:line 561
   at LinqToDB.DataProvider.BasicMergeBuilder`2.BuildCommandText() in C:\projects\linq2db\Source\LinqToDB\DataProvider\BasicMergeBuilder.cs:line 646
   at LinqToDB.DataProvider.BasicMergeBuilder`2.BuildCommand() in C:\projects\linq2db\Source\LinqToDB\DataProvider\BasicMergeBuilder.cs:line 1265
   at LinqToDB.DataProvider.DataProviderBase.Merge[TTarget,TSource](DataConnection dataConnection, IMergeable`2 merge) in C:\projects\linq2db\Source\LinqToDB\DataProvider\DataProviderBase.cs:line 481
   at WsMVC.Core.ExpenditurePaper.Incur() in D:\Projects\ProjMVC\MVC\Core\Document.cs:line 225

Steps to reproduce

public class DocumentContent
    {
        public int CardId { get; set; }
        public double Quantity { get; set; }
    }
[Table(Name = "Registry")]
    public class Registry
    {
        [Column(Name = "CardId"), PrimaryKey, Identity]
        public int CardId{ get; set; }
        [Column(Name = "Quantity"), NotNull]
        public double Quantity { get; set; }
}
var tsource = Content.GroupBy(l => l.CardId).Select(s => new DocumentContent
    {
       CardId = s.Key,
       Quantity = s.Sum(c=> c.Quantity)
    }).ToList();
var tempTable = db.CreateTable<DocumentContent>("#tmp");
tempTable.BulkCopy(tsource);
var source = tempTable.Select(s => s);

db.GetTable<Registry>()
.Merge()
.Using(source)
.On(s => s.CardId, f => f.CardId)
.UpdateWhenMatched((t, s) => new Registry()
{
   Quantity = t.Quantity + s.Quantity
})
.Merge();

Environment details

linq2db version: 2.1.0
Database Server: SqlServer 2016 (13.0.4001.0)
Operating system: Windows 8
Framework version: .NET Framework 4.5.2

@sdanyliv
Copy link
Member

@ichemidov, will handle this. Thanks for reporting.

@sdanyliv
Copy link
Member

BTW, it is syntetic sample? I do not see purpose why you have used temporary table. Grouping query can be used in merge operation without objects materialzation.

@ichemidov
Copy link
Author

In some ways it's syntetic sample. I've create it to understand how works merge api. But In the future I plan to extend it (add fields to classes in sample) and use in my project.
I found that I should use temp tables in issue #277, beacuse I want merge database table with List<DocumentContent> (no such table exists in db, it's only class in my programm).

@sdanyliv
Copy link
Member

@ichemidov, unfortunately i can not reproduce issue.
Do you have some extra code that transforms Expression Tree?

@sdanyliv
Copy link
Member

I ask because such exception is possible if somewhere in dynamic Expression Tree generation used incorrect Join method call. I have reviewed our code according to call stack and not found problems.
If you can supply us sample project it will be great.
Thanks!

@ichemidov
Copy link
Author

ichemidov commented Jul 2, 2018

@sdanyliv, I created sample project and I was even able to localize the problem.
See WsDataProvider.cs

No Exception

var cont = DocsCont.Where(s => s.IdDoc == id).Select(s => new { s.CardId, s.Quantity });
foreach (var item in cont)
 {
    var dc = new DocumentContent
    {
        CardId = (int)item.CardId,
        ResourceId = 0,
        Code = "",
        Quantity = (double)item.Quantity
    };
    contentList.Add(dc);
}

InvalidCastException

var cont = DocsCont.Where(s => s.IdDoc == id).InnerJoin(Resource,
(s, e) => s.ResourceId == e.ResourceId,
(s, e) => new { s.CardId, e.ResourceId, e.Code, s.Quantity });
foreach (var item in cont)
{
    var dc = new DocumentContent
    {
        CardId = item.CardId,
        ResourceId = item.ResourceId,
        Code = item.Code,
        Quantity = (double)item.Quantity
    };
    contentList.Add(dc);
}

@sdanyliv sdanyliv added this to the 2.2.0 milestone Jul 2, 2018
@sdanyliv
Copy link
Member

sdanyliv commented Jul 2, 2018

@ichemidov, again.
It works for me. Looks like you have some post build plugin that modifies Expression Tree.
If yes. Let me know which one.
We have introduced our Join method, it has a little bit different parameters that IQueryable equivalent and looks like some extension won't work if expression tree contains this method.

public static IQueryable<TResult> Join<TOuter, TInner, TResult>(
	[NotNull]           this IQueryable<TOuter>                   outer,
	[NotNull]           IQueryable<TInner>                        inner,
	[SqlQueryDependent] SqlJoinType                               joinType,
	[NotNull, InstantHandle] Expression<Func<TOuter, TInner, bool>>    predicate,
	[NotNull, InstantHandle] Expression<Func<TOuter, TInner, TResult>> resultSelector)

InnerJoin call in your sample translated to this Join function with parameter SqlJoinType.Inner

P.S.
Simple comment for your sample solution. Better to generate models by T4 templates
And create methods like GetWsDocumentById using extension methods.

@ichemidov
Copy link
Author

ichemidov commented Jul 3, 2018

@sdanyliv, do you run the project "as is"? Lines 47-58 in WsDataProvider.cs are commented?
I checked the installed VS plugins and I have no plugin that modifies Expression Tree. And post puild events in Project Properties are empty.
Also I tried reproduce issue on another computer on VS 2015 and VS 2017 (15.4.4) and I got same result (InvalidCastException) in both cases.

P.S.
Thanks for advice

@sdanyliv
Copy link
Member

sdanyliv commented Jul 3, 2018

Really strange. Could you please clone linq2db sources and add refrence to the library in your test project?

@ichemidov
Copy link
Author

So, I cloned linq2db sources and build it (I had to change file \Source\LinqToDB\Compile.cmd to specify the path to MSBuild.exe), and add refere to the .dll in test project.
I got InvalidCastException...
Github does not allow to attach file (too big)
https://yadi.sk/d/TTI5IObT3YhfZk

@sdanyliv
Copy link
Member

sdanyliv commented Jul 3, 2018

Just add reference to the linq2db.csproj. And try figure our why it fails.
Probjem with Join, but why...

@MaceWindu MaceWindu modified the milestones: 2.2.0, Future Jul 25, 2018
@sdanyliv sdanyliv added the status: has-pr There is active PR for issue label Sep 26, 2018
@sdanyliv sdanyliv modified the milestones: Future, 2.4.0 Sep 26, 2018
@sdanyliv
Copy link
Member

Fixed in bb1ef44

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: linq status: has-pr There is active PR for issue type: bug
Development

No branches or pull requests

3 participants