I'm using Entity Framework Core 5.0 in a ASP.NET Core 3.1 application (this is part of migrating an existing .NET Framework application to .NET Core). I have POCOs defined as such:
public class Message
{
public Guid MessageId { get; set; }
public MessageDispatcherRoute RouteInfo { get; set; }
public MessageDispatcherRoute ReturnRouteInfo { get; set; }
public List<MessageResponse> Responses { get; set; }
// other properties
}
public class MessageDispatcherRoute
{
public string Url { get; set; }
public string HttpVerb { get; set; }
}
public class MessageResponse
{
public Guid MessageId { get; set; }
public int ResponseNumber { get; set; }
// other properties
}
The table schema looks like this (SQL Server 13.0):
CREATE TABLE [Message] (
[MessageId] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
[RouteInfo_Url] VARCHAR(255),
[RouteInfo_HttpVerb] VARCHAR(10),
[ReturnRouteInfo_Url] VARCHAR(255),
[ReturnRouteInfo_HttpVerb] VARCHAR(10),
-- other columns
}
-- MessageResponse is a separate table
According to the Microsoft documentation on owned types, I should be able to use explicit declaration to accomplish this (it's the very first example), so my model builder looks like this (current state; I'm still in the middle of migrating the site):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RemovePluralizingTableNameConvention(); // https://stackoverflow.com/questions/37493095/entity-framework-core-rc2-table-name-pluralization
modelBuilder.Entity<App>().HasKey(x => x.AppId);
modelBuilder.Entity<App>().HasMany(x => x.AppSettings);
modelBuilder.Entity<CustomQueue>().HasKey(x => x.CustomQueueId);
modelBuilder.Entity<Message>().HasKey(m => m.MessageId);
modelBuilder.Entity<Message>().OwnsOne(m => m.RouteInfo);
modelBuilder.Entity<Message>().OwnsOne(m => m.ReturnRouteInfo);
modelBuilder.Entity<MessageResponse>().HasKey(m => new { m.MessageId, m.ResponseNumber });
modelBuilder.Entity<Message>().HasMany(m => m.Responses);
}
All this is hooked to an OData controller:
public class MessagesController : Controller
{
private readonly IMyContext _context;
public MessagesController(IMyContext context)
{
_context = context;
}
[HttpGet]
[EnableQuery(MaxExpansionDepth = 1)]
public IQueryable<Message> Get(ODataQueryOptions options)
{
return _context.Messages.AsQueryable().Include(x => x.Responses).AsSingleQuery();
}
}
The problem is when I hit the OData endpoint with the query string $top=5
, I get an exception saying SqlException: Invalid object name 'MessageDispatcherRoute'.
Using SQL Profiler, I can see that EF is trying to join to a non-existent table (note: unrelated columns omitted for brevity).
exec sp_executesql N'SELECT [t].[MessageId]
, [m1].[MessageId], [m1].[RouteInfo_HttpVerb], [m1].[RouteInfo_Url]
, [m0].[MessageId], [m2].[MessageId], [m2].[ResponseNumber]
FROM (
SELECT TOP(@__TypedProperty_0) [m].[MessageId]
, [m].[ReturnRouteInfo_HttpVerb], [m].[ReturnRouteInfo_Url]
FROM [Message] AS [m]
ORDER BY [m].[MessageId]
) AS [t]
LEFT JOIN [MessageDispatcherRoute] AS [m0] ON [t].[MessageId] = [m0].[MessageId]
LEFT JOIN [MessageDispatcherRoute] AS [m1] ON [t].[MessageId] = [m1].[MessageId]
LEFT JOIN [MessageResponse] AS [m2] ON [t].[MessageId] = [m2].[MessageId]
ORDER BY [t].[MessageId], [m0].[MessageId], [m1].[MessageId], [m2].[MessageId], [m2].[ResponseNumber]'
,N'@__TypedProperty_0 int',@__TypedProperty_0=5
I tried explicitly setting the column names in the model builder, as shown in EF Core 2.2, owned entities generated as another table when multiple in hierarchy, but that changed nothing.
UPDATE: I've made a console application with nothing but a stripped-down DbContext and the 3 POCOs shown above didn't help. If I add [Owned]
to the MessageDispatcherRoute class, that results in a really weird output:
LEFT JOIN [Message.ReturnRouteInfo#MessageDispatcherRoute] AS [m0] ON [t].[MessageId] = [m0].[MessageID]
LEFT JOIN [Message.RouteInfo#MessageDispatcherRoute] AS [m1] ON [t].[MessageId] = [m1].[MessageID]
LEFT JOIN [Message.ReturnRouteInfo#MessageDispatcherRoute] AS [m2] ON [t].[MessageId] = [m2].[MessageID]
What am I doing wrong? Thanks.