I’ve been coding a very sizable project and I have run into trouble with EF4 and joiner table operations.
Suppose we have the following SQL table defintions:
CREATE TABLE [dbo].[SQLEntity](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Field1] [nvarchar](128) NOT NULL,
[Field2] [nvarchar] (256),
[DateAdded] [datetime] NOT NULL,
CONSTRAINT [PK_SQLEntity] PRIMARY KEY CLUSTERED (
[Id] ASC
) WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Util_LookupValues](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Description] [nvarchar](64) NOT NULL,
CONSTRAINT [PK_Util_LookupValues] PRIMARY KEY CLUSTERED (
[Id] ASC
)WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Xref_EntityValues](
[SQLEntityId] [bigint] NOT NULL,
[LookupId] [int] NOT NULL,
CONSTRAINT [PK_Xref_PositionBenefits] PRIMARY KEY CLUSTERED (
[SQLEntityId] ASC,
[LookupId] ASC
)WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Xref_EntityValues] WITH CHECK ADD CONSTRAINT [FK_Xref_EntityValues_Entity] FOREIGN KEY([SQLEntityId])
REFERENCES [dbo].[SQLEntity] ([Id])
GO
ALTER TABLE [dbo].[Xref_EntityValues] CHECK CONSTRAINT [FK_Xref_EntityValues_Entity]
GO
ALTER TABLE [dbo].[Xref_EntityValues] WITH CHECK ADD CONSTRAINT [FK_Xref_EntityValues_Util_LookupValues] FOREIGN KEY([LookupId])
REFERENCES [dbo].[Util_LookupValues] ([Id])
GO
ALTER TABLE [dbo].[Xref_EntityValues] CHECK CONSTRAINT [FK_Xref_EntityValues_Util_LookupValues]
GO
Once a domain model is crated based on these tables you’d end up with two Entities:
SqlEntity and Util_LookupValues.
At this point that the Util_LookupValues is a table whose values have been defined for lookups only! the Xref_EntityValues is a joiner table that would tie together the Entity object to the lookup value, allowing us to have a meny to meny relationship between the two, preserving the “lookup” functionality for the lookup table.
Util_LookupValues contents
Id Description
--- ------------
1 Person
2 Car
If no changes are added to the domain model (Lets for the purpose of this question call it DataEntities) then tying up a SQLEntity with Util_LookupValues objects whose PK is 1 and 2 would be done as follows:
IEnumerable<Util_LookupValues> lookupValues = DataEntities.Util_LookupValues.Where( lv => lv.Id == 1 || lv.Id == 2);
SQLEntity entity = new SQLEntity();
entity.Field1 = "some field";
entity.Field2 = "another field";
entity.DateAdded = DateTime.Now;
foreach(Util_LookupValues val in lookupValues)
{
entity.Util_LookupValues.Add(val);
}
DataEntities.SQLEntities.Add(entity);
DataEntities.SaveChanges();
Problem though is that instead of just adding values to the Xref_EntityValues, this code also adds a copy of 1 and 2 to the Util_LookupValues with new keys!
The resulting database is as follows:
SQL Entity:
Id Field1 Field2 DateAdded
-- ------- ------ ----------
1 some field another field 04/04/2012
Xref_EntityValues:
SQLEntityId LookupId
----------- ---------
1 3
1 4
and Util_LookupValues:
Id Description
--- ------------
1 Person
2 Car
3 Person
4 Car
How do I make it that Util_LookupValues only has the 2 original records and the Xref_EntityValues has the proper foreign keys?
Xref_EntityValues:
SQLEntityId LookupId
----------- ---------
1 1
1 2
Util_LookupValues:
Id Description
--- ------------
1 Person
2 Car
Does this line…
…and this line…
… use the same instance of the
DataEntitiescontext? Your code snippet indicates that but perhaps it’s “pseudo code”.If the instances are different you get the duplication of entities, yes.
So, you have three options:
Attach the returned
Util_LookupValuesto the second context:Don’t perform the first query at all. Instead create “stub” entities and attach them:
This work because EF only needs to know the primary key property value when you attach an object to the context to establish a new relationship.