linq2db / linq2db.EntityFrameworkCore

Bring power of Linq To DB to Entity Framework Core projects
MIT License
449 stars 39 forks source link

Merge doesn't include fields from the output expression #365

Open AntonC9018 opened 9 months ago

AntonC9018 commented 9 months ago

I have a situation like this:

In the merge query, I do an insert of A with values from AModel as well as the additional context values, setting the missing fields to their default values if needed; I also do an update of A for the fields that are present on AModel. I want to output the result in a temp table as Code, Id pairs (both being present on A, but only Code being present on AModel). It complains to me that ID was not found in the query.

var mappingsTable = await linq2dbContext.CreateTempTableAsync<CodeToIdMapping>(
    tableName: cache.TempTableName,
    cancellationToken: cancellationToken);
Dictionary<string, int> codeToIdMap;
try
{
    await using var tempTable = await linq2dbContext.CreateTempTableAsync(
        values,
        cancellationToken: cancellationToken);

    var insertExpression = cache.GetInsertExpression(contextConstant);
    var q = _dbContext.Set<TEntity>()
        .Where(ownerHelper)
        .AsCte()
        .Merge()
        .Using(tempTable)
        .On(cache.ComparisonExpression)
        .UpdateWhenMatched(cache.UpdateExpression)
        .InsertWhenNotMatched(insertExpression);

    if (shouldDelete)
        q = q.DeleteWhenNotMatchedBySource();

    await q.MergeWithOutputIntoAsync(
        mappingsTable,
        cache.CreateMappingExpression,
        cancellationToken);

    codeToIdMap = await AsyncExtensions.ToDictionaryAsync(
        mappingsTable,
        x => x.Code,
        x => x.Id,
        cancellationToken);
}
catch (Exception)
{
    await mappingsTable.DisposeAsync();
    throw;
}

For the CreateMappingExpression, I'm using the 3 parameter overload. It's created in a generic function that knows TEntity has a Code and an Id, but I expect working with generics through interfaces to be allowed by the library:

Expression<Func<string, TEntity, TEntity, CodeToIdMapping>> createMappingExpression =
    (_, _, c) => new()
    {
        Code = c.Code,
        Id = c.Id,
    };

Is it not allowed to output the ids of newly created objects in the output clause? I figure I should just do another query if that's not allowed.

The error I'm seeing is:

(0x80131904): Invalid column name 'ID'.
Database name 'tempdb' ignored, referencing object in tempdb.
Database name 'tempdb' ignored, referencing object in tempdb.
AntonC9018 commented 9 months ago

The logging had to be enabled prior to creating the context, I assume. It did include the ID column:

OUTPUT
        [INSERTED].[CODE],
        [INSERTED].[ID]
INTO [tempdb]..[#tempTable]
(
        [Code],
        [Id]
)

But it still produces the error about missing the ID column.

AntonC9018 commented 9 months ago

Note that ID is an autoincremented column (identity + primary key in sql server).