簡體   English   中英

Entity Framework Core 在使用自有類型時嘗試加入不存在的表

[英]Entity Framework Core tries to join to a non-existent table when using an owned type

我在 ASP.NET Core 3.1 應用程序中使用 Entity Framework Core 5.0(這是將現有 .NET 框架應用程序遷移到 .NET 核心的一部分)。 我有這樣定義的 POCO:

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
}

表架構如下所示(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

根據有關擁有類型的 Microsoft 文檔,我應該能夠使用顯式聲明來完成此操作(這是第一個示例),因此我的 model 構建器看起來像這樣(當前 state;我仍在遷移站點):

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);
}

所有這些都與 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(); 
    }
}

問題是,當我使用查詢字符串$top=5訪問 OData 端點時,我得到一個異常說SqlException: Invalid object name 'MessageDispatcherRoute'. 使用 SQL Profiler,我可以看到 EF 正在嘗試加入一個不存在的表(注意:為簡潔起見,省略了不相關的列)。

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

我嘗試在 model 構建器中顯式設置列名,如EF Core 2.2 所示,擁有的實體在多個層次結構中生成為另一個表,但這沒有任何改變。

更新:我已經制作了一個控制台應用程序,只有一個精簡的 DbContext,上面顯示的 3 個 POCO 沒有幫助。 如果我將[Owned]添加到 MessageDispatcherRoute class,則會導致非常奇怪的 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]

我究竟做錯了什么? 謝謝。

問題可能是您在調用.OwnsOne() .WithOwner()之后錯過了對 .WithOwner() 的調用。

文檔

配置目標實體由該實體擁有(或屬於該實體的一部分)的關系。

即使導航屬於同一類型,每個所有權關系的目標實體類型也被視為不同的實體類型。 目標實體類型的配置不適用於其他所有權關系的目標實體類型。

對擁有實體的大多數操作都需要通過擁有者實體使用相應的導航來訪問它。

調用此方法后,您應該鏈接調用 WithOwner(String) 以完全配置關系。

所以,在你的情況下,你可以嘗試這樣的事情:

modelBuilder.Entity<Message>().OwnsOne(m => m.RouteInfo).WithOwner();
modelBuilder.Entity<Message>().OwnsOne(m => m.ReturnRouteInfo).WithOwner();

在此之后您可能需要添加遷移,但我認為您不需要,因為您的數據庫架構已經可以了。

在嘗試隨機事情以查看會發生什么之后,解決方案是將 map 擁有的類型返回到同一個表,即使文檔根本沒有提供此信息。 事實上,它明確表示只有當擁有的類型在單獨的表中時才需要這樣做。

modelBuilder.Entity<Message>().OwnsOne(m => m.RouteInfo, mdr => mdr.ToTable(nameof(Message)));
modelBuilder.Entity<Message>().OwnsOne(m => m.ReturnRouteInfo, mdr => mdr.ToTable(nameof(Message)));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM