0

Example situation (I have asked this more directly and received no feedback, so please excuse the abstract example but I am just trying to pose the question better):

For brevity (actual models are hundreds of lines). A database is modeled to represent houses. A house can have rooms, windows, and yards. A room can have furniture or electronics. Furniture can be a couch, table, or chair. A couch has a pattern, material, and dimensions.

Mr. F. Bar's house is a show room for Bar's Crazy Couches. Every month Mr. Bar's rooms display hundreds of couches. Mr. Bar likes to know when people like his couches, and feedback is kept for every room setup.

Mr. Ed, Mr. Bar's brother, runs his database management system. Mr. Ed decides to remove a couch that hasn't been used for a while!

Assume that this deletion is accepted, or archived, or placed in a partition, or flagged as inactive (i.e. it is not blocked which is an option here).

How can the absence of this dependent couch be determined in a house which has a room that references the removed couch from a linq query using Entity Framework 4.1?

The calling query could look like this (for brevity I have left only the topical parts), but it would have an exception that is hard to catch.

public House getHouse(object id){
 using( DbContext context = new FooBarContext()){
  DbSet<House> dbSet = context.Set<House>();
  IQueryable<House> query = dbSet;
  query = query.Where(h => h.HouseKey == id);
  query = query.Include(h => h.Room);
  query = query.Include(h => h.Room.Couch);
 }
 return query.ToList();
}

var house = getHouse(9).FirstOrDefault();

The house will populate with the correct house. It will include the proper set of rooms. However, one room will have a reference to a couch which is broken. Any try{}catch{} up to this point will result in no exceptions caught. This is just for house with index number 9. At this point in code, this could easily be a list of many different house compositions.

How can I make sure that the house containing a room containing a removed couch is caught?

Travis J
  • 81,153
  • 41
  • 202
  • 273
  • You can't right? These includes have to be done manually to include logic for screening and cannot be done through linq. – Travis J Apr 10 '12 at 19:49
  • Do you know the id of the couch in question? – Steve Mallory Apr 10 '12 at 19:52
  • @SteveMallory - Not beforehand. – Travis J Apr 10 '12 at 20:08
  • well I was thinking of doing a manual left join on the rooms entity and then testing for the missing couch, but obviously, you need to know what to test for. How do you determine that a couch is dependent? That is was at one time in the list? – Steve Mallory Apr 10 '12 at 20:14
  • For simplicity, lets just say that each room can have only one couch. So the room would hold a foreign key to a single couch. If that couch were to be removed, but the information of the room setup were to be retained, the foreign key would hold a broken reference to a primary key which does not exist in the list of couches. – Travis J Apr 10 '12 at 20:19

1 Answers1

1

As you might imagine, this will not be pretty. Basically, you have to do a left join, then access all couches from all the houses/rooms returned. When you try to access the bad couch you'll get a SystemException:

A relationship multiplicity constraint violation occurred: 
    An EntityReference expected at least one related object, 
    but the query returned no related objects from the data store.

try
{
    var test = (from h in context.Set<House>()
                join r in context.Set<Room>()
                  on h.Room.Id equals r.Id into houseRoom
                from joinHouseRoom in houseRoom.DefaultIfEmpty()
                join c in context.Set<Couch>()
                  on r.Couch.Id equals c.Id into houseRoomCouch
                from joinHouseRoomCouch in houseRoomCouch.DefaultIfEmpty()
                select h).ToList()
                         .Select(x => x.Room.Couch.Material)
                         .ToList();
}
catch(SystemException se)
{
    Console.WriteLine(se.Message);
}
Steve Mallory
  • 4,245
  • 1
  • 28
  • 31
  • Impressive :) It looks like I might need to do some database normalization though, as I read this type of problem is indicative of bad design. – Travis J Apr 10 '12 at 21:41
  • @TravisJ I would definitely recommend having foreign keys between these tables - let the database server do what it was designed to do. It would prevent this senario from happinging at all. – Steve Mallory Apr 10 '12 at 23:25
  • The real issue here is cascade deleting, and the implications of that in the real world. Although cascading is required to maintain integrity, it can cause problems with the removal of important data. Avoiding the cascade (hammer) approach and merely deleting a single record (scalpel) is where this scenario comes about. I looked at normalization but my schema is 5NF and I still have the problem where the removal of a dependent record causes an inconsistency. The industry standard for this is either "don't remove the record" or "remove the record and anyone who ever heard of it" – Travis J Apr 10 '12 at 23:32
  • I get your point. But depending on your db, there is a third way. It's to set the foriegn key to null when the related record is deleted. Of course, you have to allow the foreign key to be null; not always possible. – Steve Mallory Apr 10 '12 at 23:37
  • I have been bouncing back and forth between three options. 1) Implement soft-delete, allow records to be flagged as deleted and to not show up but also not be removed from the table. 2) Implement an archiving process where deleted records are archived and logic to include archived records in reports if they arent found in the production database. 3) Don't let anyone delete. – Travis J Apr 10 '12 at 23:37
  • Yeah, the FK = null is an option but it really makes the records with that FK null deprecated without having any hopes of becoming relevant. – Travis J Apr 10 '12 at 23:38
  • I am reluctantly going to end up pursuing the first option. See this question for an aspect that is troubling me: http://stackoverflow.com/q/10097948/1026459 – Travis J Apr 10 '12 at 23:41