4

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 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:

enter image description here

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

JohnMetta
  • 18,782
  • 5
  • 31
  • 57
  • Your Table and Chicken objects should have an association to the Wing object. Wouldn't the fact that the parent objects Table and Chicken have a reference to a Wing object be enough to restrict the relationship? I mean if you tried to reference Car.Wing...or am I not understanding the problem being solved? – morganpdx May 11 '11 at 18:44
  • Of course that would create two different Wing objects...a Table.Wing and a Chicken.Wing. Hrm. – morganpdx May 11 '11 at 18:46
  • Re: your edit: Can you even do that? Query table.wing.chicken? It should throw an exception if you do that, I think? I suppose what I'm driving at is I'm not sure you *need* to enforce that restriction; EF will enforce it for you! Yay~! – morganpdx May 11 '11 at 19:06

2 Answers2

1

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.

morganpdx
  • 1,856
  • 2
  • 29
  • 46
0

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.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Interesting idea, but then if I add another class, I need to change that to ensure that my ID-based uniqueness system continues to be valid. It seems like it would work in the special case of two objects, but no the general case of n objects. – JohnMetta May 11 '11 at 21:28
  • Yes but let say that your scenario is already a very special case. – Ladislav Mrnka May 11 '11 at 21:31