How to model many-to-many relationships with a relationship entity in EF 4.1 Code First - many-to-many

Popular example: In the issue tracker JIRA, issues can be linked to other issues. The link itself has some data attached, in particular a type.
Example:
Issue A -> depends on -> Issue B
Issue B <- is depended on by <- Issue A
We are introducing the same kind of relationship for an entity in our C# ASP.NET MVC application using EF 4.1 CodeFirst, and I'm wondering how to best model this relationship?
Details:
There are some particularities about this situation:
A link has some data attached, so we can't simply model a many-to-many relationship between issues and issues. We rather have to introduce a new entity Link, which represents a relationship between two issues.
A link, by definition, links two instances of the same entity, it is a "two-to-many" relationship (a link has two issues, an issue can have many links).
The link is directed, which means, if Issue A depends on Issue B, then Issue B is depended on by Issue A.
We will certainly have a Link entity that looks like this:
public class Link
{
public int ID { get; set; }
public Issue IssueA { get; set; }
public Issue IssueB { get; set; }
public LinkType Type { get; set; }
}
The Issue class might look like this:
public class Issue
{
public int ID { get; set; }
public virtual ICollection<Link> Links { get; set; }
}
Currently there would be only one link type: dependency. So, the link type would look like this:
public class LinkType
{
public int ID { get; set; }
public string ForwardName { get; set; } // depends on
public string BackwardName { get; set; } // is depended on by
}
Now for the big question:
If I want EF to automatically manage Issue.Links, I have to tell it what Foreign key on the Link table to use. Either I use IssueA, or I use IssueB. I can't use both, can I?
Either I define:
modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueA);
or I define:
modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueB);
Possible approaches - I am curious about your feedback on whether some of them will lead to troubles, cannot be implemented, or whether any of these approaches can be regarded as "best practice":
Add two Collections to the Issue, ICollection<Link> OutgoingLinks, ICollection<Link> IncomingLinks. This way the collections can be maintained by EF, but from a business logic point of view they don't make much sense.
Only add one collection and configure EF 4.1 to add incoming and outgoing links to it, if that is possible.
Only add one collection and implement it on my own:
ICollection<Link> AllLinks { return _context.Links.Where(l => l.IssueA == this || l.IssueB == this).ToList(); }
The problem with this approach is that the domain entity executes data access tasks which is bad in terms of seperation of concerns.
Any other?

Option (1) is the way to go in my opinion, together with a readonly helper perhaps which combines the two collections:
public class Issue
{
public int ID { get; set; }
public virtual ICollection<Link> OutgoingLinks { get; set; }
public virtual ICollection<Link> InComingLinks { get; set; }
public IEnumerable<Link> Links // not mapped because readonly
{
get { return OutgoingLinks.Concat(InComingLinks); }
}
}
Option (2) isn't possible because you cannot map one navigation property to two different ends/navigation properties.

Related

Is it possible to map an ASP.NET MVC application to more than 1 database?

If we want to map a model(class) to a particular table(in a database) it can be done using [table] attribute,but it will be only confined to one database.
What if we have say 4 classes and 2 databases.I want to map class1 to table1(DB1) ,class2 to table2(DB1) , class3 to table3(DB2) and again class4 to table4(DB2) . Since there are 2 databases we are dealing with ,will we have to create different data contexts or is there a different approach?
I don't see how you could get around (and why you would want to get around) using one context per DB since using two databases also requires two connection strings.
You mention the table attribute, are you using code-first (the question is tagged with database-first)? In database-first approach you would just use the wizard to create an additional context, in code-first you would use something like this (along with DB1Connection / DB2Connection connection strings in your config):
public class DB1Entities: DbContext {
public DB1Entities() : base("DB1Connection") {
}
public DbSet<Table1> Table1 { get; set; }
public DbSet<Table2> Table2 { get; set; }
}
public class DB2Entities: DbContext {
public DB2Entities() : base("DB2Connection") {
}
public DbSet<Table3> Table3 { get; set; }
public DbSet<Table4> Table4 { get; set; }
}

What are Complex Types?

I am Creating a Application in MVC3 . I am Using Entity Framework as ORM. Can anyone tell me What are the Complex Types in ENtity Framework ?
i am not Getting what is Complex Type .
I picked this simple definition from EntityFramework book. (Definition is in context with EF code first)
Complex Types Convention
When Code First discovers a class definition where a primary key cannot be inferred, and no primary key is registered through data annotations or the fluent API, then the type is automatically registered as a complex type. Complex type detection also requires that the type does not have properties that reference entity types and is not referenced from a collection property on another type. Given the following class definitions Code First would infer that Details is a complex type because it has no primary key.
public partial class OnsiteCourse : Course
{
public OnsiteCourse()
{
Details = new Details();
}
public Details Details { get; set; }
}
public class Details
{
public System.DateTime Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}
Rest you can easily find out googling about EF complex types, As suggested by #Slauma.

Handling references in breezejs

We are developing a single page app using ASP.NET MVC4 with Web Api + Ko + Breeze using EF Code First.
Our (simplified) data model looks like this
class Product {
public String Name { get; set; }
public ICollection<ImageCollection> ImageSets { get; set;}
public Image DefaultImage { get; set; }
}
class ImageCollection {
public ICollection<Image> Images { get; set; }
}
class Image {
public String ImageUrl { get; set; }
}
DefaultImage is a navigation property (with foreign key) and is one of the images in the ImageSets.
We are exposing a Web API method of Products() and with default Breeze configuration. JSON serialized output on the wire has references for objects (i.e., PreserveReferencesHandling.Object) so when I want to bind the ImageUrl ko is unable to resolve the value.
Html looks like this
<img data-bind="attr: { src: DefaultImage().ImageUrl, title: Name}" />
When I switch the serializer to do PreserveReferencesHandling.None, the binding works.
Question: how do I make the default config to work? or if I switch to PreserveReferencesHandling.None for Breeze what are the gotchas/downsides?
Thanks!
In general, you do NOT want to switch PreserveReferencesHandling to None because you will lose the ability to serialize circular references and your payloads will get much larger.
I don't actually understand why your binding would begin to work after setting this. The first step to understanding this is probably to check that the ko objects actually contain the correct data after your query.
Remember that breeze navigation properties are lazy-loaded, so you may not have loaded them with your initial query. Take a look at the "EntityAspect.loadNavigationProperty" method.

SQL Server CE identifies a cyclical reference with Entity Framework Code First but SQL Server 2008 does not

I am working on an Entity Framework Code First project that has a fairly complex Data Model which deploys absolutely fine on SQL Server 2008.
However when creating an SQL Server CE database for some local End-To-End testing I get the following error message when EF creates the database:
System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = FK_Sites_Persons_PersonId ].
I have disabled the ManyToManyCascadeDeleteConvention in my DataContext model creation method, so that isn't the cause of the problem. The trouble I have is that the relationship in question looks fine in the SQL Server 2008 database- it appears to be a normal foreign key from what I can tell and I can't see anything flowing back in the other direction, although it is not impossible that there is a longer-path circular reference. I don't know why CE would fail and 2008 would succeed.
It turns out the problem was very simply solved- although I had disabled ManyToManyCascadeDeleteConvention I also needed to disable the OneToManyCascadeDeleteConvention to avoid the circular reference problem.
You might also consider explicitly defining the cascading updates and deletes rather than disabling them globally. Assume a model:
namespace Models
{
public class Parent
{
public Parent() { this.Children = new HashSet<Child>(); }
public int id { get; set; }
public string description { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int id { get; set; }
public string description { get; set; }
public Parent Parent { get; set; }
}
}
Override the OnModelCreating in your context and use the fluent api to specify the cascade options for a given relationship.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().HasMany<Child>(p => p.Children).WithRequired(c => c.Parent).WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Of course this is a simple example, but you can apply the same principle to your down-level entities and specifically exclude the cascaded delete that causes the circular reference.

Can I specify a discriminator column with a table-per-type mapping?

I have a class hierarchy that I want to map across several tables using Entity Framework 4.1 Code First. It's like table-per-type (TPT) but I also want a discrimator column.
The hierarchy looks something like:
public class Event
{
public Guid Id { get; set; }
public string Code { get; set; } // discriminator
public DateTime Date { get; set; }
}
public class Party : Event
{
public int AttendeeCount { get; set; }
}
public class BirthdayParty : Party
{
public int Age { get; set; }
}
public class WeddingParty : Party
{
public string Surname { get; set; }
}
This is a pretty weak example but I hope it makes sense. There'll be an "Events" table, a "Parties" table and a table for each kind of party. However, the discriminator column ("Code") will have a known value for each kind of event, like "BIRTH" for birthday parties or "WEDDING" for wedding parties.
The idea is that if I query for just birthday parties on a given date, EF would know to add Code = 'BIRTH' to my query instead of doing a bunch of UNIONs and JOINs to work out which rows it needs.
I map my lowest-level classes like this:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.ToTable("BirthdayParties");
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
I now need to specify the discriminator value in there somehow. I've tried this:
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
});
... but that complains that I haven't specified the table name inside the call to Map. So I tried moving the ToTable call into there:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
cfg.ToTable("BirthdayParties");
});
... and now it thinks I want a "Code" column in the "BirthdayParties" table, which is not correct. I've already told it that the "Code" column is in the "Events" table.
Is this even possible? Can I combine the use of a discriminator column with a table-per-type mapping?
Unfortunately this is not supported. Discriminator column can be used only in TPH. TPT differs entity types by mapped tables and it always produces those terrible queries. It could be nice feature so perhaps suggestion on Data UserVoice would make it implemented one day.
Update
There is already a suggestion on user voice for this titled "Discriminator column support in TPT inheritance".
I did an override on SaveChanges to accomplish something similar. I simply added an attribute onto the abstract class called Descriminator and set it based on the Concrete Class Name anytime something new is added.
public class MyContext : DbContext
{
public override int SaveChanges()
{
foreach (var item in ChangeTracker.Entries().Where(x=>x.Entity is MyAbstractClass && x.State == EntityState.Added))
{
((MyAbstractClass)item.Entity).Descriminator = item.Entity.GetType().Name;
}
return base.SaveChanges();
}
}