I have a problem with Pocos and nullable foreign keys . I have 2 tables (orders and products) each table have a composite primary key (orderid,orderid2) and (productid,productid2) And I have set a 0,1..* association between the two tables. One order can be related to 0 or 1 product. And one product has * orders related to him.
How to crash :
- Create a new product using CreateObject().
- Add the new product to then entityset.
- Create a new order usung CreateObject().
- Add the new Order to the entityset.
When I add an order to the product's orders list, it crashes trying to fixup the association (setting the product navigation property on the new order)
CREATE TABLE [dbo].[Products](
[productid] [int] IDENTITY(1,1) NOT NULL,
[productid2] [int] NOT NULL,
[productname] [nchar](10) NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[productid] ASC,
[productid2] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Orders](
[orderid] [int] NOT NULL,
[orderid2] [int] NOT NULL,
[ordername] [nchar](10) NULL,
[productid] [int] NULL,
[productid2] [int] NULL,
CONSTRAINT [PK_orders] PRIMARY KEY CLUSTERED
(
[orderid] ASC,
[orderid2] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Code to crash :
var product = context.CreateObject<Products>();
context.Products.AddObject(product);
var order = context.CreateObject<Orders>();
context.Orders.AddObject(order);
product.Orders.Add(order);
if (order.Product != product) Console.WriteLine("error");
Exception :
System.Data.EntityException was unhandled
Message=Unable to set field/property Product on entity type System.Data.Entity.DynamicProxies.Orders_A0290D8629F0336D278E5AEF2C0F2A91FF56726ED5E3A9FA668AC902696A8651. See InnerException for details.
Source=System.Data.Entity
StackTrace:
at System.Data.Objects.Internal.PocoPropertyAccessorStrategy.SetNavigationPropertyValue(RelatedEnd relatedEnd, Object value)
at System.Data.Objects.Internal.EntityWrapper`1.SetNavigationPropertyValue(RelatedEnd relatedEnd, Object value)
at System.Data.Objects.DataClasses.EntityReference`1.AddToObjectCache(IEntityWrapper wrappedEntity)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
at System.Data.Objects.DataClasses.EntityCollection`1.Add(TEntity entity)
at Proxies.CSharp.Program.Main(String[] args) in Program.cs:line 20
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.NullReferenceException
开发者_高级运维 Message=Object reference not set to an instance of an object.
Source=Proxies.CSharp
StackTrace:
at Proxies.CSharp.Orders.FixupProduct(Products previousValue, Boolean skipKeys) in Orders.cs:line 134
at Proxies.CSharp.Orders.set_Product(Products value) in Orders.cs:line 106
at System.Data.Entity.DynamicProxies.Orders_A0290D8629F0336D278E5AEF2C0F2A91FF56726ED5E3A9FA668AC902696A8651.SetBasePropertyValue(String , Object )
at lambda_method(Closure , Object , String , Object )
at System.Data.Objects.Internal.EntityProxyFactory.TrySetBasePropertyValue(Type proxyType, String propertyName, Object entity, Object value)
at System.Data.Objects.Internal.EntityProxyFactory.<>c__DisplayClass8.<CreateBaseSetter>b__7(Object entity, Object value)
at System.Data.Objects.Internal.PocoPropertyAccessorStrategy.SetNavigationPropertyValue(RelatedEnd relatedEnd, Object value)
Note : It works with entytobjects, it works with self-tracking entities, and It works if the key is not composite or not nullable.
Am I doing something wrong or it it a real bug ?
I had to correct the poco TT :
Chage the lines (381,441 and 475) :
<#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(principalProperty)#>;
by the line :
<#=code.FieldName(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(principalProperty)#>;
I am assuming that you are using this poco entity generotor, or doing some similar relationship fixing. If so, it is a bug AND you are doing something wrong (i.e. there are ways to do what you want without hitting the said bug).
If you use:
order.Product = product;
instead of
product.Orders.Add(order);
it'd do what you want.
The problem is in FixupProducts method of the Order object (or rather in its interaction with productid setter). Fixup method calls productid setter, and the setter sets Product to null, and when the Fixup method goes on to set productid2, it takes the null pointer exception trying to use that Product.
Of course you can hit similar issues in other cases (although I cannot think of any right now). I'm sure you can find a proper solution if you take a look at the generated classes. Off the top of my head I'd say skipping the setter in fixups would solve the problem but I haven't thought of the implications or tested this so try at your own risk.
Try replacing:
productid = Product.productid;
productid2 = Product.productid2;
with
_productid = Product.productid;
_productid2 = Product.productid2;
精彩评论