I have an inheritance chain rooted A with sub types B and C. Next, I've an entity E like this:
public class E
{
public Guid Id {get; set;}
public B B {get; set;}
public C C {get; set;}
}
The navigation properties should be mapped to two one-2-one associations where E is the principal and B, C the dependents. This won't work because EF will use the value of E.Id as foreign keys for B and C, which will result in a duplicate key in the table A. It doesn't matter if I use TPT, TPC, or TPH.
If I would map it two two one-2-many associations and use distinct foreign keys in in E, i.e. with the mapping:
Entity<E>().HasRequired(x=>x.B).WithMany().HasForeignKey(x=>x.BId);
Entity<E>().HasRequired(x=>x.C).WithMany().HasForeignKey(x=>x.CId);
Than cascading delete wont work the in the right direction. That is E.B and E.C should be deleted when deleting e.
Any suggestions how to deal with this?
EF requires one mapped column per relationship (inheritance or association). You use one column up for the inheritance, therefore this column is not available in subclasses for association relationships (because you have to delete it from the subclasses).
You have 2 choices:
You can map E's 1-1 relationship with B and C by linking it to A (the superclass). But this is not an accurate representation of your model because the {B,C} relationships are to E, not to A.
Add another column to B and C that will be used for the associations to E. This new column should be unique and not null - because effectively you are creating another primary key. Both the existing primary key and the new column uniquely identify the record (for B or C). In the database, create the constraints between {B,C} and E using this new column. Now EF will be able to both represent the inheritance {A<-B, A<-C} and the association {E--B, E--C}
Related
I have an EF 4 data model with TPT mapping.
I have a strange behaviour about the generated SQL of a query .
Lets say entity A is a base entity , A has two derived entities B and C , also A has many associations with other entities (say E,D).
When I make a simple select on A , Context.A.First() , I profiled the generated SQL from this entity and it has all the joins with the other entities.
Do you have a ny suggestion why this happen ? fixes ? any tip.
Thanks in advance ...
Context.A is the set of all A entities - including all B and C entities because every B and C is an A. It is not the set of all A entities that are not a B or C.
Therefore, if you request the first A in the database by Context.A.First() it could be a B or C or just an A. To find the concrete type of that first A the only way with TPT inheritance is to check if there are related records in the B or C table that have the same primary key like the first record in the A table. If there are related records this A is of type B (or C) and all column values from the record in table B (or C) have to be loaded together with the column values from the base record in table A in order to materialize an entity object of the correct type B (or C). If there are no related records in table B or C the concrete type is just an A.
In any case a join to the related B or C tables is required to figure out if there is a record or not and to determine the concrete type of the first A.
So, the joins you are seeing are expected behaviour when you use TPT inheritance and you can't avoid them. It has a negative impact on performance, yes, which is the biggest downside of TPT modeling.
I have a table Person with a Primary Key of PersonId. I have another table CheckDetails with a column named DirectorId. There is a Foreign Key between Person.PersonId and CheckDetails.DirectorId. It is a 1-to-Many relationship
I created 3 entities: Person, Director, and CheckDetail.
Person maps back to the Person table and has PersonId as its key.
Director inherits from Person and has an ICollection of CheckDetail.
CheckDetail has Director property named Director.
I used the following when mapping Director:
HasRequired( d => d.CheckDetails ).WithMany( ).HasForeignKey( d => d.PersonId );
but I get the following error: The foreign key component 'PersonId' is not a declared property on type 'Director'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
How do I create this association?
I have the following model: An entity of type A has an association to many pairs of entities of types (B, C). Each B entity appears in at most one pair in A, but the same C entity can show in multiple pairs. For example:
A1 --> (B1, C1)
--> (B2, C2)
--> (B3, C1)
A2 --> (B1, C3)
--> (B2, C4)
--> (B3, C4)
This follows the semantics of a java.util.Map<B, C> stored in A. Further, each C only appears for one entity A, so the A to C relationship is OneToMany. I would like to persist such a Map, and a similar example appears in the Java EE6 doc. As far as I can tell, my code (below), is essentially identical to the example:
#Entity
public class A implements Serializable {
#OneToMany(fetch= FetchType.EAGER)
#JoinTable(name="A_BC_MAP",
joinColumns=#JoinColumn(name="A_ID"),
inverseJoinColumns=#JoinColumn(name="C_ID"))
#MapKeyJoinColumn(name="B_ID")
private Map<B, C> pinnedCourses = new HashMap<Course, ScheduleTerm>();
...
}
The database schema for A_BC_MAP emerges as expected, with a three-column join table (A_ID, B_ID, C_ID). However, the primary key constraint consists of the (A_ID, C_ID) pair. Thus, I cannot store more than one (map key) B in the database with the same (map value) C, as I can in the map.
Is this the expected behavior? For map semantics, I would expect the primary key to consist of (A_ID, B_ID). Am I doing something wrong?
I'm currently using EclipseLink 2.3.0.
I assume you are using EclipseLink to define your DDL? You could always use your own DDL script.
Having duplicates in a OneToMany is unusual, but is probably possible with the map key, you could log a bug to have the DDL generate define the map key as the primary key instead of the target foreign key.
Disclaimer: Strictly speaking, what I have here is not a many-to-many relationship, but given that it's using an associative table, I figured it would be better to be slightly less accurate in order to give a better idea of what I'm doing.
I have the equivalent of the following three tables in my database:
Customer
---------
CustomerID PK
...
CustomerAddress
---------
CustomerID PK, FK -> Customer
AddressNo PK
... address columns ...
CustomerPrimaryAddress
--------------
CustomerID PK, FK -> Customer
AddressNo FK -> CustomerAddress (with CustomerID, so CustomerID
participates in both relationships)
If it's not obvious, the intent here is to allow for multiple addresses per customer, while designating at most one address per customer as "primary". I'm using an associative table to avoid placing a nullable PrimaryAddressNumber column on Customer, then creating a foreign key from Customer to CustomerAddress.
This is all well and good, but EF then places the CustomerPrimaryAddress entity in my model. Since its one and only purpose is to serve as an associative table, I have no need to represent this table in code. I removed the CustomerPrimaryAddress table from the conceptual model, then created an association between Customer and CustomerAddress like so:
Table Customer CustomerAddress
Multiplicity 1 0..1
I then mapped the association to use the CustomerPrimaryAddress table from the storage model, and all of the columns mapped just fine, or so I thought.
My issue is that now EF is complaining that CustomerID in CustomerPrimaryAddress is being mapped to two locations in my association, as it's mapped to both Customer and CustomerAddress.
Is there any way around this? I would prefer not to add a nullable column to Customer to represent the primary address number, as not only is this not a pleasant option from a DBA perspective, EF will also complain about having a cyclical relationship and I'll have to break inserts up in the code.
Thinking out loud here:
Customer
---------
CustomerID PK
...
CustomerAddress
---------
AddressNo PK
CustomerID FK -> Customer, non-nullable
... address columns ...
CustomerPrimaryAddress
--------------
CustomerID PK, FK -> Customer
AddressNo FK -> CustomerAddress
This seems like it should get the cardinalities right, but I may have missed something.
If I have 2 tables 1 with a composite primary key where one of the keys is also a foreign key in another table:
Table 1:
A (PK, FK - maps to X in Table 2)
B (PK)
C
Table 2:
X (PK)
Y
Because A is both the PK in table 1 and FK in table 2, when I use EF to generate the entity model, I have both a Scalar AND a Navigation property for A in table 1. I cannot seem to remove A as a scalar (I think because it is a primary key).
The problem I am having is that if I create a table1Entity and set A's scalar property to a new value, A's navigation property will not be changed automatically (and vice versa).
Ideally I just want A to expose the navigation property - which is the way it behaves if A was not also part of the composite primary key anyway. Is there any way to achieve this?
Am I correct in assuming that Table1 derives from Table2? If so, I would do it like so:
(I'd also change the PK for both tables to the same name, since they probably have the same meaning - for the instance of this, I'll use the example ID)
First, create the model with the default relationships (I usually just import the two tables from the database)
In the designer, right click the base type, add inheritance, select the derived type.
Delete the one to zero or one association
Then, since the base type already has column ID, delete it from the derived type.
Go to table mapping for the derived type, and map the ID property to the ID of the table.
Well, not really. Create the view with schemabinding and create a clustered index on the view (SQL Server 2008 or later, earlier versions I'm not sure can do that). The clustered index will be recognised as a primary key, thus tricking EF(VS) into believing the view is a real table.
Have you expicity set the Ids of the composite key and referenced these in your configuration?
i.e
public class Table1
{
public Table2 A{get;set}
public int AId {get;set;}
public int BId {get;set;}
}
I assume you'll need something like:
HasKey(pc => new { pc.AId, pc.BId});
HasRequired(x => x.A).WithMany().HasForeignKey(x => x.AId);
Instead of mapping to table 1 directly, add a view to your database that's got all of table 1's fields, plus an extra copy of A (A2).
Then, map the scalar key to A2 and the nav key to A.
(You'll run into a problem where if you use a view, Visual Studio can't find a primary key; fix this by manually editing the XML of the edmx file and adding a <Key><PropertyRef ... /></Key> to the <EntityType> for table A)
I know - it's hacky and horrible... but hey - it works!