I have a fairly simple Automapper projection that works with EF6 but it refusing to work with EF Core. I believe this is being caused due to some null checking that Automapper is doing. Although an error is thrown, EF Core does manage to translate it into a SQL Query. Tagging Jimmy on it as well. Thanks @jbogard
Exception message: System.InvalidOperationException : No mapping to a relational type can be found for the CLR type 'FlagDTO'.
Stack trace: at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMappingSourceExtensions.GetMapping(IRelationalTypeMappingSource typeMappingSource, Type clrType)
at Microsoft.EntityFrameworkCore.Storage.TypeMaterializationInfo..ctor(Type modelClrType, IProperty property, IRelationalTypeMappingSource typeMappingSource, Int32 index)
at Microsoft.EntityFrameworkCore.Query.Expressions.SelectExpression.<GetMappedProjectionTypes>b__69_0(Expression e)
at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Expressions.SelectExpression.<GetMappedProjectionTypes>d__69.MoveNext()
at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Microsoft.EntityFrameworkCore.Query.Sql.DefaultQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.CorrelateSubquery[TInner,TOut,TCollection](Int32 correlatedCollectionId, INavigation navigation, Func`2 resultCollectionFactory, MaterializedAnonymousObject& outerKey, Boolean tracking, Func`1 correlatedCollectionFactory, Func`3 correlationPredicate)
at lambda_method(Closure , QueryContext , ValueBuffer )
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at FinBrook.WebJobs.Test.TestTest.<TestAsync>d__0.MoveNext() in C:\Sources\FinBrook.WebJobs\FinBrook.WebJobs.Test\TestTest.cs:line 46
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
```c#
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace FinBrook.WebJobs.Test
{
public class TestTest
{
[Fact]
public void Test()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap
cfg.CreateMap
cfg.CreateMap
});
var optionsBuilder = new DbContextOptionsBuilder<TestContext>();
optionsBuilder.UseSqlServer("Server=(LocalDb)\\MSSQLLocalDB;Database=TestingCore;Trusted_Connection=true;MultipleActiveResultSets=true");
var dbContext = new TestContext(optionsBuilder.Options);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var continent = new Continent()
{
Title = "test",
Countries = new List<Country>
{
new Country
{
Flag = new Flag()
{
Title = "test"
},
Title = "test1"
}
}
};
dbContext.Continents.Add(continent);
dbContext.SaveChanges();
var a = dbContext.Continents.ProjectTo<ContinentDTO>().ToList();
}
}
public class TestContext : DbContext
{
public TestContext(DbContextOptions<TestContext> options) : base(options)
{
}
public DbSet<Continent> Continents { get; set; }
}
public class Continent
{
public int ID { get; set; }
public string Title { get; set; }
public ICollection<Country> Countries { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Title { get; set; }
public Flag Flag { get; set; }
}
public class Flag
{
public int ID { get; set; }
public string Title { get; set; }
}
public class ContinentDTO
{
public int ID { get; set; }
public string Title { get; set; }
public List<CountryDTO> Countries { get; set; }
}
public class CountryDTO
{
public int ID { get; set; }
public string Title { get; set; }
public FlagDTO Flag { get; set; }
}
public class FlagDTO
{
public int ID { get; set; }
public string Title { get; set; }
}
}
```
EF Core version: 2.1.0-rc1-final
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.7
+1, I have this problem as well. When using the 2.1.0-preview2 release there was no error. It seems to be that there is a small change between preview2 and RC1 that triggers this error, only I couldn't find it (yet).
I did not realize that this was in fact working fine with 2.1.0-preview2, just checked and indeed it works fine with preview2
@maganuk That's what I'm looking for. What has been changed between the preview2 and the RC1 release. I have the same problem when I'm navigating from a collection to a single object (in your example from CountryDTO -> FlagDTO where the error occurs on).
@maumar @smitpatel @anpete Below is the repro with the AutoMapper projection pulled into normal LINQ--it fails in the same way with the same message. Removing the null check c2.Flag != null in the projection causes the query to compile. Who should tackle this for 2.1?
```C#
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace FinBrook.WebJobs.Test
{
public class TestTest
{
public static void Main()
{
using (var context = new TestContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var continent = new Continent
{
Title = "test",
Countries = new List<Country>
{
new Country
{
Flag = new Flag
{
Title = "test"
},
Title = "test1"
}
}
};
context.Continents.Add(continent);
context.SaveChanges();
}
using (var context = new TestContext())
{
var queryable = context.Continents.Select(c =>
new ContinentDTO
{
ID = c.ID,
Title = c.Title,
Countries = c.Countries.Select(c2 => new CountryDTO
{
ID = c2.ID,
Title = c2.Title,
Flag = c2.Flag != null
? new FlagDTO
{
ID = c2.Flag.ID,
Title = c2.Flag.Title
}
: null
}).ToList()
});
var results = queryable.ToList();
}
}
}
public class TestContext : DbContext
{
public DbSet<Continent> Continents { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
public class Continent
{
public int ID { get; set; }
public string Title { get; set; }
public ICollection<Country> Countries { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Title { get; set; }
public Flag Flag { get; set; }
}
public class Flag
{
public int ID { get; set; }
public string Title { get; set; }
}
public class ContinentDTO
{
public int ID { get; set; }
public string Title { get; set; }
public List<CountryDTO> Countries { get; set; }
}
public class CountryDTO
{
public int ID { get; set; }
public string Title { get; set; }
public FlagDTO Flag { get; set; }
}
public class FlagDTO
{
public int ID { get; set; }
public string Title { get; set; }
}
}
```
regression was introduced by 3902d943a489107d9e2336a434ecf489376d0516
After this change we started translating constants inside subqueries to sql, we also try to do this for the null value of the FlagDTO that is part of the ? : check.
Discussed with @maumar & have got nice one line fix. Assigning to him to finish the work and create PR.
fixed in 81f5db20d7332c359da0314fb4410f4c8ad52802
Is there any workaround before this gets released ? We dared to use this in a productive environment T_T
We are in the same, desperate situation like @elessariel. We need a workaround for this before it gets released. Is there any chance?
@elessariel @Avenel This will be fixed in the RTM release. Two possible workarounds are:
Hello, i have similar problem with anonoymous object projection with null check. Also after this fix. Can you look at this ?
var partners = apiContext.Partners
.Select(x => new
{
Addresses = x.Addresses.Select(y => new
{
Turnovers = y.Turnovers == null ? null : new
{
y.Turnovers.AmountIn
},
}).ToList()
}).ToList();
@peterjanto - Please file a new issue with detailed repro steps.