This is most likely one for all you sexy DBAs out there:
How would I effieciently model a relational database whereby I have a field in an "Event" table which defines a "SportType"?
This "SportsType" field can hold a link to different sports tables E.g. "FootballEvent", "RubgyEvent", "CricketEvent" and "F1 Event".
Each of these Sports tables have different fields specific to that sport.
My goal is to be able to genericly add sports types in the future as required, yet hold sport specific event data (fields) as part of my Event Entity.
Is it possible to use an ORM such as NHibernate / Entity framework / DataObjects.NET which would reflect such a relationship?
I have thrown together a quick C# example to express my intent at a higher level:
public class Event<T> where T : new()
{
public T Fields { get; set; }
public Event()
{
EventType = new T();
}
}
public class FootballEvent
{
public Team CompetitorA { get; set; }
public Team CompetitorB { get; set; }
}
public class TennisEvent
{
public Player CompetitorA { get; set; }
public Player CompetitorB { get; set; }
}
public class F1RacingEvent
{
public List<Player> Drivers { get; set; }
public List<Team> Teams { get; set; }
}
public class Team
{
public IEnumerable<Player> Squad { get; set; }
}
public class Player
{
public string Name { get; set; }
public DateTime DOB { get; set;}
}
DataObjects.Net supports automatic mappings for open generics. Some details on this are described here.
You can do this by having all the Event types inherit from an abstract Event base class. This make sense to me because all the events share some common properties: date, venue, etc. You can use a table per concrete class or table per subclass strategy to store the objects in a relational database. Here are some links to articles describing inheritance mapping with NHibernate:
Chapter 8. Inheritance Mapping
Fluent NHibernate and Inheritance Mapping
NHibernate Mapping – Inheritance
The example converted to DO4 must look as follows:
// I'd add this type - adding an abstract base makes design more clean + allows you to share
// the behavior among all the descendants
[Serializable]
[HierarchyRoot]
public abstract class EventBase : Entity
{
[Key]
Guid Id { get; private set; } // Or some other type
}
[Serializable]
public class Event<T> : EventBase
where T : IEntity, new() // IEntity indicates DO4 must try to map its descendants automatically
// Although I'd put some stronger requirement, e.g. by using IEventData instead of IEntity here
{
public T Data { get; set; }
public Event(T data)
{
Data = data;
}
}
[Serializable]
[HierarchyRoot]
public class FootballEvent
{
// You need [Key] here
public Team CompetitorA { get; set; }
public Team CompetitorB { get; set; }
}
[Serializable]
[HierarchyRoot]
public class TennisEvent
{
// You need [Key] here
public Player CompetitorA { get; set; }
public Player CompetitorB { get; set; }
}
[Serializable]
[HierarchyRoot]
public class F1RacingEvent
{
// You need [Key] here
public EntitySet<Player> Drivers { get; private set; }
public EntitySet<Team> Teams { get; private set; }
}
[Serializable]
[HierarchyRoot]
public class Team
{
// You need [Key] here
public EntitySet<Player> Squad { get; set; }
}
[Serializable]
[HierarchyRoot]
public class Player
{
public string Name { get; set; }
public DateTime DOB { get; set; }
}
In this case Event instances will be available (= mapped automatically) for all suitable Ts from model. E.g. in this case they'll be:
- EventBase // Yes, even it, coz it's suitable
- FootballEvent
- TennisEvent
- F1RacingEvent
- Team
- Player
If you'd like to restrict this to just certain types, you must do the following:
- Add an interface inherited from IEntity all these types will support, e.g. IEventData.
- Use it as generic parameter constraint for generic parameter T in Event.
Cross Posted from: http://forum.x-tensive.com/viewtopic.php?f=29&t=5820,
Answer By Alex Yakunin,
Chief Executive DataObjects.NET
There are a bunch of options like XML columns and EAV (also known as database within a database), but none of which will translate well with an ORM to traditional static object-oriented languages, and all of which have drawbacks with respect to data type safety and referential integrity at the database level.
If you need this level of dynamic structure in both the database and the client, you might need to go with an object or document database (and language) which is much more dynamic by design - relational databases tend to work best with static relationships and data models.
Related
In short; we need a cache, that cache has related entities. We also need to then store the expired items in a separate table for compliance and archival reasons.
Imagine the following four classes:
public abstract class Cache
{
public int Id { get; set; }
public Cache()
{
Entities = new HashSet<Entity>();
}
public ICollection<Entity> Entities { get; set; }
}
public class AvailableCache : Cache
{
}
public class ArchivedCache : Cache
{
}
public class Entity
{
public int Id { get; set; }
public int? CacheId { get; set; }
public AvailableCache { get; set; }
public ArchivedCache { get; set; }
}
Together with the following DbContext:
public partial class DbCacheContext : DbContext
{
public virtual DbSet<AvailableCache> { get; set; }
public virtual DbSet<ArchivedCache> { get; set; }
public virtual DbSet<Entity> Entities { get; set; }
protected override void OnModelCreatning(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ArchivedCache>(p =>
{
p.Property(p => p.Id)
.ValueGeneratedOnAdd();
p.HasMany(p => p.Entities).WithOne(p => p.ArchivedCache).HasForeignKey(p => p.CacheId);
}
modelBuilder.Entity<AvailableCache>(p =>
{
p.HasMany(p => p.Entites).WithOne(p => p.AvailableCache).HasForeignkey(p => p.CacheId);
}
}
A recurring job populates the cache, checks for expired items and archives them. First attempt a column was added to ArchivedCache
public class ArchivedCache : Cache
{
public int OriginalId { get; set; }
}
This was then used as to save the original ID to define the relationship toward Entity. Then I tried to remove that, adding the ValueGeneratedOnAdd() and give it the same ID as AvailableCache.
The issue is that when trying to insert an Entity the foreign key constraint won't allow it since it doesn't exist in that table.
Since AvailableCache and ArchivedCache basically same object I would like to keep the relationship simple toward Entity.
Currently as I see it I have three options, I don't like any of them:
Define both IDs on Entity (I don't like this option because I don't want to clutter Entity with two ID:s that in the end point toward the same object, just in different tables/times)
Skip the ArchivedCache relationship and let the DBA worry about it since he was the one that specifically requested this.
I could remove the abstract class, resulting in one table with a discriminator but the DBA insisted on two tables.
But before I do either I wanted to check if there's perhaps something in EF Core that would allow this.
Overview
I am designing a mechanism for generating dynamic controls in an ASP.NET MVC application that uses ADO.NET Entity Framework. However, my question has nothing to do with MVC and a little to do with the Entity Framework. It is about comparing two object models.
Problem Statement
In my app, a user must have the ability to interact with Web page A to specify that he wants to add such and such HTML controls to Web Page B.
When he browses Web Page B next, he must see those controls and be able to use them.
What Is Not The Challenge
I have written the code to generate the controls. That was the easy part. I used the Tag Builder, Partial Views, HtmlHelper extensions and Display & Editor templates.
The Challenge
The challenge is in arriving at a database design and an object model generated by Entity Framework to hold the metadata about the controls that need to be generated.
I have come up with a database design as shown below:
You may ignore the User and Permissions tables. They are not relevant to our discussion.
Entity Framework generates the following entities based on the above database design.
Let's call my database design as Design Option A.
I would have wanted a design that looked more like this:
Let's call this second design as Design Option B.
The code (stripped down version) for this second option would look like this:
namespace DynamicControls
{
public class DynamicControlGroup
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Controller { get; set; }
public IEnumerable<string> Actions { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public User CreatedByUser { get; set; }
public DateTime CreationDateTime { get; set; }
public User LastModifiedBy { get; set; }
public DateTime ModificationDateTime { get; set; }
// Navigational
public ICollection<DynamicControl<T>> DynamicControls { get; set; }
}
public class DynamicControl<T>
{
public long Id { get; set; } //db Id
public string HtmlId { get; set; }
public bool ValueRequired { get; set; }
public virtual ControlType ControlType { get; protected set; }
// Every control is capable of having a default value but of a different
// type. Most controls have default values of type text (string). The
// multi-select ones (checkboxes, multi-select lists, etc.) have a default
// value of type IEnumerable<string>. So, I want to leave this generic.
// But I am not that hung-up on this. I am fine if I am required to move
// this property DefaultValue from the base class and make it a concrete
// (not generic) property for each individual child class.
// Mostly I just want the heirarchy. And before that, I want to know
// if it is a good idea to model this heirarchy. Or is it better to just
// work with what my Entity Framework produced for my db?
// Should I change my db? I can because I thought-up the design for
// those tables.
public virtual T DefaultValue { get; set; }
// Navigational
public DynamicControlGroup DynamicControlGroup { get; set; }
}
public class TextBox : DynamicControl<string>
{
public override ControlType ControlType
{
get
{
return DynamicControls.ControlType.TextBox;
}
}
public string Label { get; set; }
public int MaxLength { get; set; }
}
public class PasswordControl : TextBox
{
public override ControlType ControlType
{
get
{
return DynamicControls.ControlType.Password;
}
}
}
public class TextArea : TextBox
{
public override ControlType ControlType
{
get
{
return DynamicControls.ControlType.TextArea;
}
}
public int Rows { get; set; }
}
public class DropDownList: DynamicControl<string>
{
public override ControlType ControlType
{
get
{
return ControlType.DropDownList;
}
}
// I want something like this. That I should be able to say
//
// myDropDownListObject.Options...
//
// You'll notice that given my current database design, I have
// no direct way of accessing the options of a, say, drop down list.
// To do that, I have to make a round-about Linq query.
public ICollection<DynamicControlOption> Options { get; set; }
}
public class DynamicControlOption
{
public long Id { get; set; } // db Id
public string OptionHtmlId { get; set; }
public string OptionValue { get; set; }
public string OptionText { get; set; }
// Navigational property
public DynamicControl<IEnumerable<string>> TheControlWhoseOptionIAm { get; set; }
}
public class User
{
}
public class Permission
{
}
public enum ControlType
{
TextBox,
TextArea,
Password,
RadioButton,
Checkbox,
DropDownList,
MultiSelectList,
DatePicker,
TimePicker,
DateTimePicker
}
}
My Question
1) I feel that I'd like Design Option B better. Am I feeling right?
2) I know I can work with Design Option A just as fine but it'll involve a little round-about way to do some things. For example, to get all the options for a drop down list, there's not navigational property on the DropDownList class in Design Option A. I'll have to write a round-about Linq query to do that.
3) Is it possible to have Entity Framework come close to generating Design Option B? How? What changes will I need to make to my database design to achieve that?
Now we are working on a Project like this at our company...
If I got your meaning correctly and If I were you...I implemented inherited structure as my database design like below.
Now you Classes are inheritance but your database design is not.
I have removed Id in TextBox and I have put ControlId as PK and FK in the same time. (not just FK).
in fact,ControlId is both PK for TextBox and FK from DynamicControl
and also this way for PasswordControl and TextArea
and Now ControlId in TextBox is not Identity. It gets it's ControlId from DynamicControl
I also accept Design Option B .I'm always more comfortable than using Design Option A.in my idea It's true and main structure
Easier to show by example -- I'm using code-first to construct a database. I have the following classes:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public List<Post> Posts { get; set; }
public string BlogCode
{
get
{
return Title.Substring(0, 1) + ":" + AuthorName.Substring(0, 1);
}
}
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public virtual Blog Blog { get; set; }
}
I don't understand why Post needs a public virtual Blog Blog. Does it act as a foreign key in the database to link back to the Blog? It seems like if that were the case you would use the Blog Id.
It does allow the two tables to be related and places a foreign key on Post relating to Blog.
Having public virtual Blog Blog { get; set; } allows you to reference the Blog object from a Post object and then get access to all the properties of the Blog. E.g. myPost.Blog.Id If it used public virtual int BlogId { get; set; }, you would not be able to do this since BlogId would just be an int value.
If your domain objects are lazy loaded, myPost.Blog will not actually be hydrated with data from the database (i.e. no call to the Blog table) until that property is used. As soon as it is used, Entity Framework will make the database call for you and hydrate the object with data from the Blog table. This is part of the beauty of using an ORM... it allows you to focus on the code while it takes care of the database operations.
I would like a DomainService as follows:
[EnableClientAccess]
public class FamilyService : DomainService
{
public IQueryable<Person> GetPeople() // for the time being fake out a set of people
{
var people = new[]{ new Person(), ... };
return people.AsQueryable();
}
}
The Person class should be simple enough:
public class Person
{
[Key]
public Guid Id { get; set; }
public Person Parent { get; set; }
public String Name { get; set; }
public Person[] Children { get; set; }
}
In the Silverlight client, the Person class that is generated does not include the Parent or Children properties. What am I missing?
(Perhaps I should point out that while this is obviously a mock example, I am planning on using a loosely coupled approach, that this mimics. In fact most of my data doesn't reside in a database at all A significant majority of the RIA samples use an end-to-end parity of model from the database to the UI, which is not an option in my case.)
Try adding the [Include] attribute to the Parent & Children properties.
public class Person
{
[Key]
public Guid Id { get; set; }
[Include]
public Person Parent { get; set; }
public String Name { get; set; }
[Include]
public Person[] Children { get; set; }
}
If the Person class is coming out of the DB (in your real world app) and you are using EF, then you need to force them to be loaded eagerly, using the .Include() method.
I'm using NHibernate to map my database tables with my entities and NHibernate Validator to validate my entities. It works sweet when validating the properties of the entities, however, is it possible to make database lookup validation with NHibernate Validator?
(Poor) Example: I have an Animal class and an AnimalType class (type= {cat, dog, horse}).
An animal of the same type has to have a unique name. So if there exists a horse in the database with the name "Jolly Jumper" I would like to receive an error message if I try to create another horse with that name.
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public AnimalType Type { get; set; }
}
public class AnimalType
{
public int TypeId { get; set; }
public string TypeName { get; set; }
}
public class AnimalDef : ValidationDef<Animal>
{
public AnimalDef()
{
Define(e => e.AnimalName).ShouldBySomeMagicReturnFalseIfThereExistsAnimalOfTheSameTypeHavingTheSameName();
}
}
Yes, it's possible.
Here's an example from Fabio Maulo: http://fabiomaulo.blogspot.com/2009/10/validation-through-persistence-not.html