AspNetCoreOData icon indicating copy to clipboard operation
AspNetCoreOData copied to clipboard

Composite Key not working, when one of Key matches name of other Entity

Open GorkyeMatt opened this issue 3 years ago • 3 comments
trafficstars

Hi!

When configuring Composite Key for one of tables we got problem with key. We had two entities:

public class PurchaseOrder
{
    public long Id { get; set; } //Key
    public string Name { get; set;}
    public IEnumerable<PurchaseOrderLine> PurchaseOrderLines { get; set; } = new List<PurchaseOrderLine>();
}
public class PurchaseOrderLine
{
    public long PurchaseOrderId { get; set; } //Part of Key
    public int LineId { get; set; } //Part of Key
    public string Name { get; set;}
    public PurchaseOrder? PurchaseOrder { get; set; }
}

Then we configure it as follows:

builder.EntitySet<PurchaseOrder>("PurchaseOrders");
builder.EntitySet<PurchaseOrderLine>("PurchaseOrderLines").EntityType
    .HasKey(pol => new {pol.PurchaseOrderId, pol.LineId});

Finally, we had PurchaseOrderController:

public class PurchaseOrderLinesController : ODataController
{
    private readonly IEnumerable<PurchaseOrderLine> _purchaseOrderLines; 
    
    // code to setup _purchaseOrderLines in ctor

    [EnableQuery]
    public IQueryable<PurchaseOrderLine> Get() => _purchaseOrderLines.AsQueryable();

    [EnableQuery]
    public SingleResult<PurchaseOrderLine> Get(long keyPurchaseOrderId, int keyLineId)
    {
        _logger.Log("Retrieving PurchaseOrder by composite key");
        var result = _purchaseOrderLines.AsQueryable().Where(m => m.PurchaseOrderId == keyPurchaseOrderId && m.LineId == keyLineId);
        return SingleResult.Create(result);
    }
}

When we call {host}/PurchaseOrderLines(PurchaseOrderId=1,LineId=1) we are not hitting the endpoint. 404 is returned, but no log trace (so it's 404, that enpoint not found, not that no entity with such id exist);

Funny thing is, that if we change PurchaseOrderId to HeadId, then it works:

builder.EntitySet<PurchaseOrder>("PurchaseOrders");
builder.EntitySet<PurchaseOrderLine>("PurchaseOrderLines").EntityType
    .HasKey(pol => new {pol.HeadId, pol.LineId});
        
        
[EnableQuery]
public SingleResult<PurchaseOrderLine> Get(long keyHeadId, int keyLineId)
{
    var result = _repository.Query()
        .Where(m => m.HeadId == keyHeadId && m.LineId == keyLineId);
    return SingleResult.Create(result);
}

Is it a bug, or there is some not documented convention that prevents using keys, which have similar names that other entities?

GorkyeMatt avatar Mar 10 '22 12:03 GorkyeMatt

@GorkyeMatt Can you enable the route debug middelware and send $odata to verify whether the routing is created correctly?

https://github.com/OData/AspNetCoreOData/tree/main/sample/ODataRoutingSample#static-routing-table

xuzhg avatar Mar 10 '22 21:03 xuzhg

Hi, guys! I guess I can add my five cents here. I also faced the similar problem after having migrated Microsoft.AspNetCore.OData nuget from 7.x to 8.x. The requests to previously working endpoint with a composite key stopped hitting the endpoint.

@xuzhg From what I managed to figure out, now (starting from 8.x) the endpoint expects the 'composite key' arguments to be provided in the alphabetical order (so it actually ignores the order, specified in controller's actions argument list).

Given the model

public class MySuperEntity
{
	[Key]
	public int First { get; set; }

	[Key]
	public string Second { get; set; }
}

and the controller action defined as follows: public async Task<IActionResult> Get([FromODataUri] string keySecond, [FromODataUri] int keyFirst)

$odata endpoint shows the following endpoint: <host>/odata/MySuperEntity(First={keyFirst},Second={keySecond})

This also explains @GorkyeMatt's case about "Funny thing is, that if we change PurchaseOrderId to HeadId, then it works"

misha-fm avatar May 24 '22 12:05 misha-fm

Isn't there a way to just honor whatever is defined in the EDM as the order for the components? Forcing alphabetical order seems like a bad default to me.

julealgon avatar May 24 '22 13:05 julealgon

Isn't there a way to just honor whatever is defined in the EDM as the order for the components? Forcing alphabetical order seems like a bad default to me.

+1. Enforced ordering is just silly.

VladDerptastic avatar Jun 14 '23 09:06 VladDerptastic