Good way to do "either/or" relationship in Entity Framework (SQL Server)
Let's say I have two entity objects "table" and "chicken."
Now let's say, I have a "wing" object, and I want that wing to have a 0..1-1 relationship with table and chicken. In otherwords, I want a nullable table.wing and a nullable chicken.wing.
Is there a good way, using Entity Framework 4, to make the wing object have the restriction that it can either be associated with a table OR a chicken?
Note: I don't want to have a wingedobjects 开发者_JS百科baseclass in my dictionary- this needs to be a "has one" not an "is one."
My thought is that I can't make a unique restraint on the collection of references, so I'll have to wrap the Entity properties with something like:
public partial class Wing:
...
public Table Table
{
get { return this.Table; }
set {
//make sure Chicken is null
this.Table = value;
}
}
...
}
This strikes me as pretty hacky and not too clean, so I was looking for a better, if not best, practices solution.
Edit:
To be clear, I currently have a 0..1-1 relationship between table and wing, and a 0..1-1 relationship between chicken and wing. Thus, I can create a table.wing and I can then look at wing.table. What I want is to ensure that I ALWAYS have a null value if I query table.wing.chicken or chicken.wing.table. The wing must be associated with EITHER one table OR one wing.
Example of current behavoir:
In response to @morganppdx's comment:
Given this Entity Diagram:
And the following in Program.cs:
class Program
{
static void Main(string[] args)
{
Model1Container container = new Model1Container();
Wing chickenwing = new Wing { Shape = "birdlike" };
Chicken chicken1 = new Chicken { Breed = "Andalusian", Wing = chickenwing };
Table table1 = new Table { Style = "Mission", Wing = chickenwing }; // Should throw exception!
container.AddToChickens(chicken1);
container.AddToTables(table1);
container.SaveChanges();
Console.Write(String.Format("Table {0}'s wing has a {1} shape...", table1.Id, table1.Wing.Shape));
Console.Write(String.Format("Table {0} has {1} chicken wings!", table1.Id, table1.Wing.Chicken.Breed));
Console.ReadLine(); //wait for input to give us time to read
}
}
The resulting console will show:
Table 1's wing has a birdlike shape...Table 1 has Andalusian chicken wings!
This result is what I wish to avoid. It should throw an exception when chickenwing is associated with table1 because it is already associated with chicken1, and cannot be associated with both a table and with a chicken.
It is quite possible that I am building the relationship incorrectly, and thus not getting @morganpdx's stated exception where I want it.
The code is available at: https://github.com/mettadore/WingThing
Off the top of my head, my suggestion would be to create child objects that extend the Wing object, and use those instead of your Wing object:
public class ChickenWing : Wing
{
public Table Table { get { throw new NoTablesAllowedException; }}
}
public class TableWing: Wing
{
public Chicken Chicken { get { throw new NoChickensHereException; }}
}
The code you posted would then look like this:
class Program
{
static void Main(string[] args)
{
Model1Container container = new Model1Container();
ChickenWing chickenwing = new ChickenWing { Shape = "birdlike" };
TableWing tablewing = new TableWing { Shape = "circular" };
Chicken chicken1 = new Chicken { Breed = "Andalusian", Wing = chickenwing };
Table table1 = new Table { Style = "Mission", Wing = tablewing };
container.AddToChickens(chicken1);
container.AddToTables(table1);
container.SaveChanges();
Console.Write(String.Format("Table {0}'s wing has a {1} shape...", table1.Id, table1.Wing.Shape));
Console.Write(String.Format("Table {0} has {1} chicken wings!", table1.Id, table1.Wing.Chicken.Breed));
Console.ReadLine(); //wait for input to give us time to read
}
}
I have not done anything like this to date, but I believe this should work. Essentially the Wing object acts as an Interface to describe the ChickenWing and TableWing objects, but those are discreet objects used for discreet purposes.
Looking on your model I think you can simply make Table and Wing Ids as IDENTITY with different seed and increment 2 - one will have only even and second only odd Ids and in such case there will never be wing which will be related to both.
The point is that one-to-one relation in EF is always built on primary keys so wing must have primary key of either table or chicken and when defining exclusive sequences it will never happen that wing can have both table an chicken.
精彩评论