-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Model configuration: A property that will be shadow and is used as part of an FK must be configured as a shadow property first #6823
Comments
Another situation, different error, likely same cause (as in: HasForeignKey is broken): Poco: // <auto-generated>This code was generated by LLBLGen Pro v5.1.</auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace AW.EntityClasses
{
/// <summary>Class which represents the entity 'UnitMeasure'.</summary>
public partial class UnitMeasure : CommonEntityBase
{
/// <summary>Gets or sets the ModifiedDate field. </summary>
public System.DateTime ModifiedDate { get; set;}
/// <summary>Gets or sets the Name field. </summary>
public System.String Name { get; set;}
/// <summary>Gets or sets the UnitMeasureCode field. </summary>
public System.String UnitMeasureCode { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'BillOfMaterial.UnitMeasure - UnitMeasure.BillOfMaterials (m:1)'</summary>
public List<BillOfMaterial> BillOfMaterials { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.UnitMeasure - UnitMeasure.Products (m:1)'</summary>
public List<Product> Products { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.UnitMeasure_ - UnitMeasure.Products_ (m:1)'</summary>
public List<Product> Products_ { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductVendor.UnitMeasure - UnitMeasure.ProductVendors (m:1)'</summary>
public List<ProductVendor> ProductVendors { get; set;}
}
}
//------------------------------------------------------------------------------
// <auto-generated>This code was generated by LLBLGen Pro v5.1.</auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace AW.EntityClasses
{
/// <summary>Class which represents the entity 'Product'.</summary>
public partial class Product : CommonEntityBase
{
/// <summary>Gets or sets the Class field. </summary>
public System.String Class { get; set;}
/// <summary>Gets or sets the Color field. </summary>
public System.String Color { get; set;}
/// <summary>Gets or sets the DaysToManufacture field. </summary>
public System.Int32 DaysToManufacture { get; set;}
/// <summary>Gets or sets the DiscontinuedDate field. </summary>
public Nullable<System.DateTime> DiscontinuedDate { get; set;}
/// <summary>Gets or sets the FinishedGoodsFlag field. </summary>
public System.Boolean FinishedGoodsFlag { get; set;}
/// <summary>Gets or sets the ListPrice field. </summary>
public System.Decimal ListPrice { get; set;}
/// <summary>Gets or sets the MakeFlag field. </summary>
public System.Boolean MakeFlag { get; set;}
/// <summary>Gets or sets the ModifiedDate field. </summary>
public System.DateTime ModifiedDate { get; set;}
/// <summary>Gets or sets the Name field. </summary>
public System.String Name { get; set;}
/// <summary>Gets or sets the ProductId field. </summary>
public System.Int32 ProductId { get; set;}
/// <summary>Gets or sets the ProductLine field. </summary>
public System.String ProductLine { get; set;}
/// <summary>Gets or sets the ProductNumber field. </summary>
public System.String ProductNumber { get; set;}
/// <summary>Gets or sets the ReorderPoint field. </summary>
public System.Int16 ReorderPoint { get; set;}
/// <summary>Gets or sets the Rowguid field. </summary>
public System.Guid Rowguid { get; set;}
/// <summary>Gets or sets the SafetyStockLevel field. </summary>
public System.Int16 SafetyStockLevel { get; set;}
/// <summary>Gets or sets the SellEndDate field. </summary>
public Nullable<System.DateTime> SellEndDate { get; set;}
/// <summary>Gets or sets the SellStartDate field. </summary>
public System.DateTime SellStartDate { get; set;}
/// <summary>Gets or sets the Size field. </summary>
public System.String Size { get; set;}
/// <summary>Gets or sets the StandardCost field. </summary>
public System.Decimal StandardCost { get; set;}
/// <summary>Gets or sets the Style field. </summary>
public System.String Style { get; set;}
/// <summary>Gets or sets the Weight field. </summary>
public Nullable<System.Decimal> Weight { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'BillOfMaterial.Product - Product.BillOfMaterials (m:1)'</summary>
public List<BillOfMaterial> BillOfMaterials { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'BillOfMaterial.Product_ - Product.BillOfMaterials_ (m:1)'</summary>
public List<BillOfMaterial> BillOfMaterials_ { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductCostHistory.Product - Product.ProductCostHistories (m:1)'</summary>
public List<ProductCostHistory> ProductCostHistories { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductDocument.Product - Product.ProductDocuments (m:1)'</summary>
public List<ProductDocument> ProductDocuments { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductInventory.Product - Product.ProductInventories (m:1)'</summary>
public List<ProductInventory> ProductInventories { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductListPriceHistory.Product - Product.ProductListPriceHistories (m:1)'</summary>
public List<ProductListPriceHistory> ProductListPriceHistories { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.ProductModel - ProductModel.Products (m:1)'</summary>
public ProductModel ProductModel { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductProductPhoto.Product - Product.ProductProductPhotos (m:1)'</summary>
public List<ProductProductPhoto> ProductProductPhotos { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductReview.Product - Product.ProductReviews (m:1)'</summary>
public List<ProductReview> ProductReviews { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.ProductSubcategory - ProductSubcategory.Products (m:1)'</summary>
public ProductSubcategory ProductSubcategory { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ProductVendor.Product - Product.ProductVendors (m:1)'</summary>
public List<ProductVendor> ProductVendors { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'PurchaseOrderDetail.Product - Product.PurchaseOrderDetails (m:1)'</summary>
public List<PurchaseOrderDetail> PurchaseOrderDetails { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'ShoppingCartItem.Product - Product.ShoppingCartItems (m:1)'</summary>
public List<ShoppingCartItem> ShoppingCartItems { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'SpecialOfferProduct.Product - Product.SpecialOfferProducts (m:1)'</summary>
public List<SpecialOfferProduct> SpecialOfferProducts { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'TransactionHistory.Product - Product.TransactionHistories (m:1)'</summary>
public List<TransactionHistory> TransactionHistories { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.UnitMeasure - UnitMeasure.Products (m:1)'</summary>
public UnitMeasure UnitMeasure { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'Product.UnitMeasure_ - UnitMeasure.Products_ (m:1)'</summary>
public UnitMeasure UnitMeasure_ { get; set;}
/// <summary>Represents the navigator which is mapped onto the association 'WorkOrder.Product - Product.WorkOrders (m:1)'</summary>
public List<WorkOrder> WorkOrders { get; set;}
}
} Mappings: /// <summary>Defines the mapping information for the entity 'Product'</summary>
/// <param name="config">The configuration to modify.</param>
protected virtual void MapProduct(EntityTypeBuilder<Product> config)
{
config.ToTable("Product", "Production");
config.HasKey(t => t.ProductId);
config.Property(t => t.ProductId).HasColumnName("ProductID").ValueGeneratedOnAdd();
config.Property(t => t.Name).HasMaxLength(50).IsRequired();
config.Property(t => t.ProductNumber).HasMaxLength(25).IsRequired();
config.Property(t => t.MakeFlag);
config.Property(t => t.FinishedGoodsFlag);
config.Property(t => t.Color).HasMaxLength(15);
config.Property(t => t.SafetyStockLevel);
config.Property(t => t.ReorderPoint);
config.Property(t => t.StandardCost);
config.Property(t => t.ListPrice);
config.Property(t => t.Size).HasMaxLength(5);
config.Property(t => t.Weight);
config.Property(t => t.DaysToManufacture);
config.Property(t => t.ProductLine).HasMaxLength(2);
config.Property(t => t.Class).HasMaxLength(2);
config.Property(t => t.Style).HasMaxLength(2);
config.Property(t => t.SellStartDate);
config.Property(t => t.SellEndDate);
config.Property(t => t.DiscontinuedDate);
config.Property(t => t.Rowguid).HasColumnName("rowguid");
config.Property(t => t.ModifiedDate);
config.HasOne(t => t.ProductModel).WithMany(t => t.Products).HasForeignKey("ProductModelID").IsRequired();
config.HasOne(t => t.ProductSubcategory).WithMany(t => t.Products).HasForeignKey("ProductSubcategoryID").IsRequired();
config.HasOne(t => t.UnitMeasure).WithMany(t => t.Products).HasForeignKey("SizeUnitMeasureCode").IsRequired();
config.HasOne(t => t.UnitMeasure_).WithMany(t => t.Products_).HasForeignKey("WeightUnitMeasureCode").IsRequired();
}
//...
/// <summary>Defines the mapping information for the entity 'UnitMeasure'</summary>
/// <param name="config">The configuration to modify.</param>
protected virtual void MapUnitMeasure(EntityTypeBuilder<UnitMeasure> config)
{
config.ToTable("UnitMeasure", "Production");
config.HasKey(t => t.UnitMeasureCode);
config.Property(t => t.UnitMeasureCode).HasMaxLength(3);
config.Property(t => t.Name).HasMaxLength(50).IsRequired();
config.Property(t => t.ModifiedDate);
} Gives error when running any query:
I have no idea how it thinks it's a |
For config 1: |
They're mapped alphabetical: https://gist.github.com/FransBouma/23ebe450d7e09dc1812fc2ff3c546b86 So MapSpecialOfferProduct is mapped after MapSalesOrderDetail and MapProduct is mapped before MapUnitMeasure. It shouldn't matter btw, as there might not be an order in which they can be mapped in a single pass, nor should the user (if it's done by hand) worry about that as topological sorting isn't a human's strong point ;) (or a model might not be a DAG) |
(the file in the gist has FK fields, the order is the same for when no FK fields are present, the situation of this issue). |
@FransBouma - Order does matter in some cases. It is not about topological sorting which user has to worry about. For config1 if you are configuring composite PK before FK then it should work. |
No, sorry, this is silly. It's saying EF can't do a proper model check after everything is mapped and I have to do it for EF. How am I suppose to know what the order is in a dense model like adventureworks? Impossible without using a graph with a sort operation. But, I do have defined a composite PK on the principal end. It just hasn't been mapped yet. EF should take care of this, not me as a developer. Interestingly, if I generate the model with FK fields, everything is fine, even though the same applies: the PK hasn't been defined yet, so it can't verify whether the PK is compatible either! So in short, the EF team is suggesting a user of your framework should manually do ordering of the mappings in such a way that the EF model verifier is satisfied because you guys can't implement a 2-pass system? How about this: you keep track of a list with lambdas which perform work for you to do after the modelbuilder has been completed. In the case like situation 1, it can't decide yet what to do, so it stores a lambda which will perform what to do at that point in the list. Then when the modelbuilder is run, you run the lambdas in the list till the list is empty. It's the same as deserializing a graph from e.g. xml or binary sources: sometimes you run into a reference that's not been deserialized yet. You can give up and say to the user they should serialize things in the 'right' order, but you can also solve it with a simple 2 pass system. If I can come up with things like this, so can a whole team at microsoft. (as my designer can load entity models and derived models without problems, in any order. It's not a hard problem, you know that).
Maybe that's not possible (not a DAG, although it's rare). For a human to figure out the order this way is impossible. For 2 entities, it's fine. For 20 it's not. My designer can spit out the code in the 'right' order, but the reason why is, sorry, beyond comprehension.
How is the PK reconfigured? It's defined once. |
@FransBouma Use Property calls to map your FK fields as shadow properties. |
Ok, and then it will magically be solved? (as the properties are already known and have a type) ? I can do that. Will try tomorrow. You guys seriously need to update the docs, it's completely unclear this is needed. |
Agreed, I will do this. We have some improvements in 1.1 that may help in this scenario... but in general our guidance should be to define the shadow FK properties and then use them in |
@FransBouma Using The first scenario should work in 1.1, and we can make the second scenario work in the future. |
Mapping the non-PK fk fields as shadow properties first makes it work indeed. I'll leave it open as it's not documented at all. |
@AndriySvyryd assigning this to you to assess how much work we should do to enable this. I will update the docs to recommend calling Property first to add the shadow property |
Our docs are currently migrating to a new doc platform, so opened dotnet/EntityFramework.Docs#280 to update them once the migration is done. |
…the principal type. Fixes #6823
…the principal type. Fixes #6823
Steps to reproduce
AdventureWorks 2008.
I have this mapping to map SalesOrderHeader:
This mapping to SalesOrderDetail:
For completeness: the SpecialOfferProduct mapping, with the compound PK
The FK fields aren't part of the POCO:
Running this simple query:
gives:
However, this is odd, as there's no way to specify the type for the field in HasForeignKey(). It also shouldn't have to, as the type can be derived from the PK field it corresponds with (which is mapped properly).
The issue
Mapping fails, which shouldn't. Likely compound PK handling.
Further technical details
EF Core version: 1.0.1, running on .NET full 4.5.2
Operating system: windows 8.1
Visual Studio version: 2015
The text was updated successfully, but these errors were encountered: