How Do I Query Objects With Nested Properties Using NHibernate? - c#

Okay, I've seen some similar questions to this, but the answers either confused me or seemed completely over-engineered, so I'd like to ask my own question.
I have a class called Tree, which has an object property from the class Plot, which has an object property from the class Year, which has an object property from the class Series, which has a string property called Id. This is summarized below.
public class Tree {
public virtual Plot Plot { get; set; }
// other properties...
}
public class Plot {
public virtual Year Year { get; set; }
// other properties...
}
public class Year {
public virtual Series Series { get; set; }
// other properties...
}
public class Series {
public virtual string Id { get; set; }
// other properties...
}
Each of these classes corresponds to the table of a database, and properties correspond to foreign key fields (for example, the Trees table has a field called PlotKey, which refers to a record in the Plots table). All I want to do is load all trees from the database whose corresponding Series have the Id "Adrian_2012" or "IPED Sample". I thought this would be a pretty easy taking using the following code:
IList<Tree> trees = session.CreateCriteria<Tree>()
.Add(Expression.Or(
Expression.Eq("Plot.Year.Series.Id", "Adrian_2012")
Expression.Eq("Plot.Year.Series.Id", "IPED Sample")
))
.List<Tree>();
But this is throwing: "NHibernate.Exceptions.GenericADOException : could not execute query". I have tried using Expression.Disjunction, I have tried using Aliases, Restrictions, and SimpleExpressions, and I know that nothing stupid like unmapped properties or misspelled criteria is occurring. The only other thing I've seen that might help is the ISession.QueryOver<>() function, but I get very confused by lambda expressions. Does anyone have a solution for me that would use just a simple CreateCriteria<> statement like that above?
Thanks in advance!

One not nice side of the Criteria queries is, that we have to define associations chain explicitly. I.e. we have to introduce JOIN:
15.4. Associations (cite:)
You may easily specify constraints upon related entities by navigating associations using CreateCriteria().
So to have a JOIN we need syntax like this
var trees = session
.CreateCriteria<Tree>()
.CreateCriteria("Plot", "p")
.CreateCriteria("Year", "y")
.CreateCriteria("Series", "s")
.Add(Expression.Or(
Expression.Eq("s.Id", "Adrian_2012")
Expression.Eq("s.Id", "IPED Sample")
))
.List<Tree>();
Also, check this:
NHibernate - CreateCriteria vs CreateAlias

Related

How to retrieve data from multiple tables and display in a view using viewmodel

I'm trying to develop a messeging system to my mvc application using mvc 5. I have tables called Event, EventUser, EventObject. Each of those tables have following;
Event
ID
CreatedBy
StartTime
IsShared
Budget
EventUser
EventID
UserID
IsAccepted
EventObject
EventID
ObjectID
in my messageController i have the index method which receive the parameter of the user id.i need to display every event that user has invited using this method..
namespace MvcApp.Controllers
{
public class MessageController : Controller
{
private EPlannerDatabaseEntities db = new EPlannerDatabaseEntities();
// GET: /Message/
public ActionResult Index(int UId)
{
/* linq expressions */
return View();
}
}
}
when the parameter has passed in, i want to;
*Select from EventUser table where UID=UserID and join the result with Event and EventObject tables by using EventID attribute.
*Finally by using the final result i need to display every event's infomation that user has invited; like CreatedBy , StartTime, Budget,other users,objects etc..
i'm new to mvc and viewmodel concept.I heard that viewmodel concept can help with these situations.can i overcome this problem by using viewmodel concept.if yes what are the things i need to add in view model?? otherwise what are the other ways to do this?
one way i can see of doing this is creating a custom return object and using EF to join all the tables together. Example
public class MyObject{
public DateTime DateCreated{get;set}
// add remaining properties here
// properties to get back
}
then in code you would use Entity Framework to create a joined data set into a nice list of objects. Example:
var results = (from b in bla join bla2 in (Some Second Query Here)
from SomeSecondQueryHere
where cond1 and cond2 Select new MyObject{
// add properties in here})
where you would replace the bla and bla2,etc with respective table names needed. Then all you need to do is
return View(results);
And the changes will be accessible in the View
If you question is regarding querying with an ORM like Entity Framework, you need to post your entities, not your table schemas. The whole purpose of an ORM is to abstract away the underlying database structure, so while the schema will often be similar to the entity class, it can also be quite different. As a result, I'll have to make assumptions about your entity classes.
To query everything, you just need something like the following:
var events = db.Events.Where(m =>
m.EventUsers.Any(u => u.UserID == UId && u.IsAccepted)
).Include(m => m.EventObjects);
That assumes entity classes along the lines of:
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public int UserID { get; set; }
public bool IsAccepted { get; set; }
}
You end up with an enumerable of Event. If you need to access the EventObjects for an individual event, you have to use the appropriate collection property. For example:
foreach (var item in events)
{
foreach (var obj in item.EventObjects)
{
// do something with `obj` (an invidual `EventObject` instance)
}
}
If you need the actual User object, you're better object querying that first and including related Events and EventObjects:
var user = db.Users.Include("EventUsers.Event.EventObjects").SingleOrDefault(m => m.UserID == UId);
That assumes entities like:
public class User
{
...
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public virtual Event Event { get; set; }
}
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
}
With that method, however, there's no way to filter the included Events by whether they're accepted or not. There's a potential way around that, but it requires disabling lazy-loading of EventUsers entirely and complicates querying the information you need. If you need to go that route, see: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter.
Otherwise, you can just exclude non-accepted events before iterating over the collection:
var events = user.EventUsers.Where(m => m.IsAccepted).Select(m => m.Event);
Really you don't need a view model, per se, for any of this. As you can either pass the lists of events (which will include any related EventObjects) or the the single user instance (which includes related events and related EventObjects) directly to your view.
A very high level description of how to solve your scenario using Entity Framework would be something like this:
First you've got to create a series of entity data objects that will represent your tables in the EF data model using EF Code first techniques.
Then you create DbContext objects with DbSets for your previously created entities.
Then you create at least one Service class that will have a property representing DbContext and a set of methods encapsulating Linq queries to your entities.
In the MVC controller you call an instance of Service that you previously create and assign it to a property ant Controller's construction time. Finally, in the Action method you should call the correct Service method and pass any result to the view.
( I am assuming this is a small Ad-Hoc system with a handful of tables , an elaborate System with production quality would require using IoC techniques).

Where do derived or inferred properties belong in an application?

I'm building an app using code first and generating the DB.
I can no longer modify the DB so, I can't add/change columns and tables. But the Domain Model (not sure if I'm using the term correctly) requires new properties (that are part of the domain) that can be inferred from the database data, but do not exist explicitly.
My database stores sales info for houses. So I have two tables, Houses and Sales. The tables are related by houseID. Now I want houses to have a property called LastSaleDate, but I can't change the underlying database.
So, How would I properly construct this new property and add it into the appropriate layer? Here is what my poco/entities look like. Just pseudo coded...
[I am trying to learn all I can about the tools and methods I use. I may be completely wrong on all my assumptions and maybe I am to add it to my pocos. If that is the case please explain how that would work]
[Table("HOUSE_TABLE")]
public class house {
//some properties
public int HouseID {get;set;}
}
[Table("SALE_TABLE")
public class sale {
//some properties
public int HouseID {get;set;
public int SaleID {get;set;}
public datetime SaleDate {get;set;}
public virtual House House {get;set;}
}
I almost feel like this would create 2 levels of mapping. Though, I don't believe I've ever seen this done in any code I've seen online.
poco -> AutoMapper?? -> entities -> Automapper -> viewModels
This logic most likely belongs on the Entity. Entities should have both data and behaviour. What you seem to be describing is some behaviour that is exposed as a property. So, you should add a property for the derived value to your entity. By default, if the property only has a getter, then EF will not try to map the value to the database.
For example:
[Table("HOUSE_TABLE")]
public class house
{
//some properties
public int HouseID {get;set;}
public virtual ICollection<Sale> Sales { get; set; }
public DateTime LastSaleDate
{
get
{
return this.Sales.OrderByDescending(s => s.SaleDate).First();
}
}
}

Entity Framework associations with multiple (separate) keys on view

I'm having problems setting up an Entity Framework 4 model.
A Contact object is exposed in the database as an updateable view. Also due to the history of the database, this Contact view has two different keys, one from a legacy system. So some other tables reference a contact with a 'ContactID' while other older tables reference it with a 'LegacyContactID'.
Since this is a view, there are no foreign keys in the database, and I'm trying to manually add associations in the designer. But the fluent associations don't seem to provide a way of specifying which field is referenced.
How do I build this model?
public class vwContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class LegacyObject
{
public virtual vwContact Contact { get; set; }
public string ContactId { get; set; } //references vwContact.LegacyKeyField
}
ModelCreatingFunction(modelBuilder)
{
// can't set both of these, right?
modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
modelBuilder.Entity<vwContact>().HasKey(x => x.LegacyKeyField);
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).???
//is there some way to say which key field this reference is referencing?
}
EDIT 2: "New things have come to light, man" - His Dudeness
After a but more experimentation and news, I found using a base class and child classes with different keys will not work by itself. With code first especially, base entities must define a key if they are not explicitly mapped to tables.
I left the suggested code below because I still recommend using the base class for your C# manageability, but I below the code I have updated my answer and provided other workaround options.
Unfortunately, the truth revealed is that you cannot accomplish what you seek without altering SQL due to limitations on EF 4.1+ code first.
Base Contact Class
public abstract class BaseContact
{
// Include all properties here except for the keys
// public string Name { get; set; }
}
Entity Classes
Set this up via the fluent API if you like, but for easy illustration I've used the data annotations
public class Contact : BaseContact
{
[Key]
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class LegacyContact : BaseContact
{
public int KeyField { get; set; }
[Key]
public string LegacyKeyField { get; set; }
}
Using the Entities
Classes that reference or manipulate the contact objects should reference the base class much like an interface:
public class SomeCustomObject
{
public BaseContact Contact { get; set; }
}
If later you need to programmatically determine what type you are working with use typeof() and manipulate the entity accordingly.
var co = new SomeCustomObject(); // assume its loaded with data
if(co.Contact == typeof(LegacyContact)
// manipulate accordingly.
New Options & Workarounds
As I suggested in comment before, you won't be able to map them to a single view/table anyway so you have a couple options:
a. map your objects to their underlying tables and alter your "get/read" methods on repositories and service classes pull from the joined view -or-
b. create a second view and map each object to their appropriate view.
c. map one entity to its underlying table and one to the view.
Summary
Try (B) first, creating a separate view because it requires the least amount of change to both code and DB schema (you aren't fiddling with underlying tables, or affecting stored procedures). It also ensures your EF C# POCOs will function equivalently (one to a view and one to table may cause quirks). Miguel's answer below seems to be roughly the same suggestion so I would start here if it's possible.
Option (C) seems worst because your POCO entities may behave have unforseen quirks when mapped to different SQL pieces (tables vs. views) causing coding issues down the road.
Option (A), while it fits EF's intention best (entities mapped to tables), it means to get your joined view you must alter your C# services/repositories to work with the EF entities for Add, Update, Delete operations, but tell the Pull/Read-like methods to grab data from the joint views. This is probably your best choice, but involves more work than (B) and may also affect Schema in the long run. More complexity equals more risk.
Edit I'm not sure this is actually possible, and this is why:
The assumption is that a foreign key references a primary key. What you've got is two fields which are both acting as primary keys of vwContact, but depending on which object you ask it's a different field that's the primary key. You can only have one primary key at once, and although you can have a compound primary key you can't do primary key things with only half of it - you have to have a compound foreign key with which to reference it.
This is why Entity Framework doesn't have a way to specify the mapping column on the target side, because it has to use the primary key.
Now, you can layer some more objects on top of the EF entities to do some manual lookup and simulate the navigation properties, but I don't think you can actually get EF to do what you want because SQL itself won't do what you want - the rule is one primary key per table, and it's not negotiable.
From what you said about your database structure, it may be possible for you to write a migration script which can give the contact entities a consistent primary key and update everything else to refer to them with that single primary key rather than the two systems resulting from the legacy data, as you can of course do joins on any fields you like. I don't think you're going to get a seamlessly functional EF model without changing your database though.
Original Answer That Won't Work
So, vwContact contains a key KeyField which is referenced by many SomeObjects and another key LegacyKeyField which is referenced by many LegacyObjects.
I think this is how you have to approach this:
Give vwContact navigation properties for SomeObject and LegacyObject collections:
public virtual ICollection<SomeObject> SomeObjects { get; set; }
public virtual ICollection<LegacyObject> LegacyObjects { get; set; }
Give those navigation properties foreign keys to use:
modelBuilder.Entity<vwContact>()
.HasMany(c => c.SomeObjects)
.WithRequired(s => s.Contact)
.HasForeignKey(c => c.KeyField);
modelBuilder.Entity<vwContact>()
.HasMany(c => c.LegacyObjects)
.WithRequired(l => l.Contact)
.HasForeignKey(c => c.LegacyKeyField);
The trouble is I would guess you've already tried this and it didn't work, in which case I can't offer you much else as I've not done a huge amount of this kind of thing (our database is much closer to the kinds of thing EF expects so we've had to do relatively minimal mapping overrides, usually with many-to-many relationships).
As for your two calls to HasKey on vwContact, they can't both be the definitive key for the object, so it's either a compound key which features both of them, or pick one, or there's another field you haven't mentioned which is the real primary key. From here it's not really possible to say what the right option there is.
You should be able to do this with two different objects to represent the Contact view.
public class vwContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class vwLegacyContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class LegacyObject
{
public virtual vwLegacyContact Contact { get; set; }
public string ContactId { get; set; } //references vwLegacyContact.LegacyKeyField
}
ModelCreatingFunction(modelBuilder)
{
// can't set both of these, right?
modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
modelBuilder.Entity<vwLegacyContact>().HasKey(x => x.LegacyKeyField);
// The rest of your configuration
}
I have tried everything that you can imagine, and found that most solutions won't work in this version of EF... maybe in future versions it supports referencing another entity by using an unique field, but this is not the case now. I also found two solutions that work, but they are more of a workaround than solutions.
I tried all of the following things, that didn't work:
Mapping two entities to the same table: this is not allowed in EF4.
Inheriting from a base that has no key definitions: all root classes must have keys, so that inherited classes share this common key... that is how inheritance works in EF4.
Inheriting from base class that defines all fields, including keys, and then use modelBuilder to tell wich base-properties are keys of the derived types: this doesn't work, because the methos HasKey, Property and others that take members as parameters, must reference members of the class itself... referencing properties of a base class is not allowed. This cannot be done: modelBuilder.HasKey<MyClass>(x => x.BaseKeyField)
The two things that I did that worked:
Without DB changes: Map to the table that is source of the view in question... that is, if vwContact is a view to Contacts table, then you can map a class to Contacts, and use it by setting the key to the KeyField, and another class mapping to the vwContacts view, with the key being LegacyKeyField. In the class Contacts, the LegacyKeyField must exist, and you will have to manage this manually, when using the Contacts class. Also, when using the class vwContacts you will have to manually manage the KeyField, unless it is an autoincrement field in the DB, in this case, you must remove the property from vwContacts class.
Changing DB: Create another view, just like the vwContacts, say vwContactsLegacy, and map it to a class in wich the key is the LegacyKeyField, and map vwContacts to the original view, using KeyField as the key. All limitations from the first case also applies: the vwContacts must have the LegacyKeyField, managed manually. And the vwContactsLegacy, must have the KetField if it is not autoincrement idenitity, otherwise it must not be defined.
There are some limitations:
As I said, these solutions are work-arounds... not real solutions, there are some serious implications, that may even make them undesirable:
EF does not know that you are mapping two classes to the same thing. So when you update one thing, the other one could be changed or not, it depends if the objects is cached or not. Also, you could have two objects at the same time, that represents the same thing on the backing storage, so say you load a vwContact and also a vwContactLegacy, changes both, and then try to save both... you will have to care about this yourself.
You will have to manage one of the keys manually. If you are using vwContacts class, the KeyFieldLegacy is there, and you must fill it. If you want to create a vwContacts, and associate is with a LegacyObject, then you need to create the reference manually, because LegacyObject takes a vwContactsLegacy, not a vwContacts... you will have to create the reference by setting the ContactId field.
I hope that this is more of a help than a disillusion, EF is a powerfull toy, but it is far from perfect... though I think it's going to get much better in the next versions.
I think this may be possible using extension methods, although not directly through EF as #Matthew Walton mentioned in his edit above.
However, with extension methods, you can specify what to do behind the scenes, and have a simple call to it.
public class LegacyObject
{
public virtual vwContact Contact { get; set; }
public string ContactId { get; set; } //references vwContact.LegacyKeyField
}
public class LegacyObjectExtensions
{
public static vwContact Contacts(this LegacyObject legacyObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in legacyObject
join c in dbContext.vwContact
on o.ContactId == c.LegacyKeyField
select c;
return contacts;
}
}
and
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class SomeObjectExtensions
{
public static vwContact Contacts(this SomeObject someObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in someObject
join c in dbContext.vwContact
on o.ContactId == c.KeyField
select c;
return contacts;
}
}
Then to use you can simply do like this:
var legacyContacts = legacyObject.Contacts();
var someContacts = someObject.Contacts();
Sometimes it makes more sense to map it from the other end of the relationship, in your case:
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.LegacyKeyField);
however this will require that u.LegacyKeyField is marked as a primary key.
And then I'll give my two cents:
if the Legacy db is using LegacyKeyField, then perhaps the legacy db will be read only. In this case we can create two separate contexts Legacy and Non-legacy and map them accordingly. This can potentially become a bit messy as you'd have to remember which object comes from which context. But then again, nothing stops you from adding the same EF code first object into 2 different contexts
Another solution is to use views with ContactId added for all other legacy tables and map them into one context. This will tax performance for the sake of having cleaner context objects, but this can be counteracted on sql side: indexed views, materialized views, stored procs, etc. So than LEGACY_OBJECT becomes VW_LEGACY OBJECT with CONTACT.ContactId brought over, then:
modelBuilder.Entity<LegacyObject>().ToTable("VW_LEGACY_OBJECT");
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.ContactId);
I personally would go with creating "mapper views" with CustomerId on legacy tables, as it's cleaner from c# layer perspective and you can make those views look like real tables. It is also difficult to suggest a solution without knowing what exactly is the scenario that you have a problem with: querying, loading, saving, etc.

Setting what table a DbContext maps to

In an application I'm working on, I have what are essentially a bunch of lookup tables in a database which all contain two things: The ID (int) and a Value (string).
There's only a handful of them, but I want to map all of them to a single Context which depends on the table name. Something like:
class LookupContext : DbContext
{
public DbSet<Lookup> Lookups { get; set; }
public LookupContext(String table)
{
// Pseudo code:
// Bind Lookups based on what table is
Lookups = MyDatabase.BindTo(table);
}
}
So if I create a new LookupContext("foo"), it binds against the foo table. If I do new LookupContext("bar") it uses the bar table, and so forth.
Is there any way to do this? Or do I have to create a separate context + model for every table I have?
This is more or less my first time doing this, so I'm not really sure if what I'm doing is right.
The answer we should be able to give you is to use enums, but that's not available quite yet - it's in the next version of EF. See here for details: http://blogs.msdn.com/b/adonet/archive/2011/06/30/walkthrough-enums-june-ctp.aspx
With earlier versions of EF, you can simply create a class per lookup value (assuming state as an example) and have code that looks something like the following:
public class State
{
public int StateId {get;set;}
public string StateName {get;set;}
}
public class LookupContext : DbContext
{
public DbSet<State> States {get;set;}
// ... more lookups as DbSets
}
This will allow you to use one context but will still require one class per table. You can also use the fluent API if you want your table/column names to differ from your class/property names respectively. Hope that helps!
I actually realized I was completely over complicating things beyond reason. There was no reason for storing multiple tables with two columns.
I'm better off storing my data as:
public class LookupValue
{
public string LookupValueId { get; set; }
public string Value { get; set; }
public string LookupType { get; set; }
}
Where the third field was simply the name of the table that I was previously storing in the database.
I'm still interested in the idea of mapping a single Context class to multiple tables, but I believe what I described above is the least convoluted way of accomplishing what I need.

How do you break circular associations between entities?

my first time on the site so apologies if it's tagged incorrectly or been answered elsewhere...
I keep running into particular situation on my current project and I was wondering how you guys would deal with it. The pattern is: a parent with a collection of children, and the parent has one or more references to particular items in the child collection, normally the 'default' child.
A more concrete example:
public class SystemMenu
{
public IList<MenuItem> Items { get; private set; }
public MenuItem DefaultItem { get; set; }
}
public class MenuItem
{
public SystemMenu Parent { get; set; }
public string Name { get; set; }
}
To me this seems like a good clean way of modelling the relationship, but causes problems immediately thanks to the circular association, I can't enforce the relationship in the DB because of the circular foreign keys, and LINQ to SQL blows up due to the cyclic association. Even if I could bodge my way round this, it's clearly not a great idea.
My only idea currently is to have an 'IsDefault' flag on MenuItem:
public class SystemMenu
{
public IList<MenuItem> Items { get; private set; }
public MenuItem DefaultItem
{
get
{
return Items.Single(x => x.IsDefault);
}
set
{
DefaultItem.IsDefault = false;
value.DefaultItem = true;
}
}
}
public class MenuItem
{
public SystemMenu Parent { get; set; }
public string Name { get; set; }
public bool IsDefault { get; set; }
}
Has anyone dealt with something similar and could offer some advice?
Cheers!
Edit: Thanks for the responses so far, perhaps the 'Menu' example wasn't brilliant though, I was trying to think of something representative so I didn't have to go into the specifics of our not-so-self-explanatory domain model! Perhaps a better example would be a Company/Employee relationship:
public class Company
{
public string Name { get; set; }
public IList<Employee> Employees { get; private set; }
public Employee ContactPerson { get; set; }
}
public class Employee
{
public Company EmployedBy { get; set; }
public string FullName { get; set; }
}
The Employee would definitely need a reference to their Company, and each Company could only have one ContactPerson. Hope this makes my original point a bit clearer!
The trick to solving this is to realize that the parent does not need to know about all of the methods of the child, and that the child does not need to know all the methods of the parent. Therefore you can use the Interface Segregation Principle to decouple them.
In short, you create an interface for the parent that has only those methods that the child needs. You also create an interface for the child that has only those methods that the parent needs. Then you have the parent contain a list of the child interfaces, and you have the child point back to the parent interface. I call this the Flip Flob Pattern because the UML diagram has the geometry of an Eckles-Jordan flip-flop (Sue me, I'm an old hardware engineer!)
|ISystemMenu|<-+ +->|IMenuItem|
A 1 \ / * A
| \/ |
| /\ |
| / \ |
| / \ |
| / \ |
|SystemMenu| |MenuItem|
Notice that there is not cycle in this diagram. You cannot start at one class and follow the arrows back to your starting point.
Sometimes, in order to get the separation just right, you have to move some methods around. There might be code that you thought should have been in the SystemMenu that you move to the MenuItem, etc. But in general the technique works well.
Your solution seems quite reasonable.
Another thing to think about is that your objects in memory don't have to exactly match the database schema. In the database you can have the simpler schema with the child properties, but in memory you can optimize things and have the parent with references to the child objects.
I don't really see your problem. Clearly you're using C#, which holds objects as references not instances. This means it's perfectly fine to have cross-referencing, or even self-referencing.
in C++ and other languages where objects are more compositied then you can have problems, which are typically solved using references or pointers, but C# should be fine.
More than likely your problem is that you're trying to follow all references somehow, leading to a circular reference. LINQ uses lazy loading to address this issue. For instance, LINQ won't load the Company or the Employee until you reference it. You just need to avoid following such references further than one level.
However, you can't really add two tables as each others foreign key, otherwise you would never be able to delete any record, since deleting an employee would require deleting the company first, but you can't delete the company without deleting the employee. Typically, in this case, you would only use one as a real foreign key, the other would simply be a psuedo-FK (that is, one that is used as an FK but doesn't have constraints enabled). You have to decide which is the more important relationship.
In the company example, you would likely want to delete the employee but not the company, so make the company->employee FK the constraint relationship. This prevents you from deleting the company if there are employees, but you can delete employees without deleting the company.
Also, avoid creating new objects in the constructor in these situations. For instance, if your Employee object creates a new Company object, which includes a new employee ojbect created for the employee, it will eventually exhaust memory. Instead, pass the objects already created to the constructor, or set them after construction, possibly by using an initalization method.
For instance:
Company c = GetCompany("ACME Widgets");
c.AddEmployee(new Employee("Bill"));
then, in AddEmployee, you set the company
public void AddEmployee(Employee e)
{
Employees.Add(e);
e.Company = this;
}
Maybe a self-referential GoF Composite pattern is an order here. A Menu has a collection of leaf MenuItems, and both have a common interface. That way you can compose a Menu out of Menus and/or MenuItems. The schema has a table with a foreign key that points back to its own primary key. Works with walking menus that way, too.
In code, you need to have references both ways to reference things both ways. But in the database, you only need the reference one way to make things work. Because of the way joins work, you only need to have the foreign key in one of your tables. When you think about it, every foreign key in your database could be flipped around, and create and create a circular reference. Best to just pick one record, in this case probably the child with a foreign key to the parent, and just be done.
In a domain driven design sense way, you can choose to avoid bidirectional relations between entities where it's possible. Choose one "aggregate root" to hold the relations, and use the other entity only when navigation from the aggregate root. I try to avoid bidirectional relations where it's possible. Because of YAGNI, and it will make you ask the question "what was first, the chicken or the egg?" Sometimes you will still need bidirectional associations, then choose one of the solutions mentioned earlier.
/// This is the aggregate root
public class Company
{
public string Name { get; set; }
public IList<Employee> Employees { get; private set; }
public Employee ContactPerson { get; set; }
}
/// This isn't
public class Employee
{
public string FullName { get; set; }
}
You can enforce foreign keys in the database where two tables refer to each other. Two ways come to mind:
The default child column in the parent is initially null and is only updated once all the child rows have been inserted.
You defer constraint checking until commit time. This means you can insert first the parent with an initially broken reference to the child, then insert the child. One problem with deferred constraint checking is that you can end up with database exceptions being thrown at commit time which is often inconvenient in many db frameworks. Also, it means you need to know the primary key of the child before you insert it which may be awkward in your setup.
I've assumed here that the parent menu item lives in one table and the child in a different table but the same solution would work if they are both in the same table.
Many DBMS's support deferred constraint checking. Possibly yours does too although you don't mention which DBMS you are using
Thanks to all who answered, some really interesting approaches! In the end I had to get something done in a big hurry so this is what I came up with:
Introduced a third entity called WellKnownContact and corresponding WellKnownContactType enum:
public class Company
{
public string Name { get; set; }
public IList<Employee> Employees { get; private set; }
private IList<WellKnownEmployee> WellKnownEmployees { get; private set; }
public Employee ContactPerson
{
get
{
return WellKnownEmployees.SingleOrDefault(x => x.Type == WellKnownEmployeeType.ContactPerson);
}
set
{
if (ContactPerson != null)
{
// Remove existing WellKnownContact of type ContactPerson
}
// Add new WellKnownContact of type ContactPerson
}
}
}
public class Employee
{
public Company EmployedBy { get; set; }
public string FullName { get; set; }
}
public class WellKnownEmployee
{
public Company Company { get; set; }
public Employee Employee { get; set; }
public WellKnownEmployeeType Type { get; set; }
}
public enum WellKnownEmployeeType
{
Uninitialised,
ContactPerson
}
It feels a little cumbersome but gets around the circular reference issue, and maps cleanly onto the DB which saves trying to get LINQ to SQL to do anything too clever! Also allows for multiple types of 'well known contacts' which is definitely coming in the next sprint (so not really YAGNI!).
Interestingly, once I came up with the contrived Company/Employee example it made it MUCH easier to think about, in contrast to the fairly abstract entities that we're really dealing with.

Categories

Resources