I've spend several days now, trying to solve this problem. While making a simple project to exemplify my problem, I stumbled upon a possible solution. So, this is sort of a double question.
But first, a little background info:
I just started using Entity Framework 4.1 (EF) and Code First to create the models for my ASP.NET MVC project. I need some models similar to this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestApp.Models
{
public class Family
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Father> Fathers { get; set; }
public virtual ICollection<Mother> Mothers { get; set; }
}
public class Mother
{
public int ID { get; set; }
public string Name { get; set; }
public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Father
{
public int ID { get; set; }
public string Name { get; set; }
public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Child
{
public int ID { get; set; }
public string Name { get; set; }
public int MotherID { get; set; }
public int FatherID { get; set; }
public virtual Mother Mother { get; set; }
public virtual Father Father { get; set; }
}
}
And the DbContext:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace TestApp.Models
{
public class TestContext : DbContext
{
public DbSet<Family> Families { get; set; }
public DbSet<Mother> Mothers { get; set; }
public DbSet<Father> Fathers { get; set; }
public DbSet<Child> Children { get; set; }
}
}
(Please excuse the lame example, that's what my Friday fried brain was able to come up with.)
A family can have several mothers and several fathers. And a child has a mother and a father. I checked with one of the .NET gurus at my work, who agreed that there is nothing extraordinary in this. At least as far as we can see.
But when I run the code, I get this Exception:
System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = Mother_Family ]
I do see the cycle: Family - Mother - Child - Father - Family. But if I created the database tables myself (which I prefer not to, that's what I like about Code First) it would be a perfectly valid data structure, as far as I can tell.
So, my first question is: Why is this a problem when using code first? Is there a way to tell EF how to properly handle the cycle?
Then, as I write initially, while creating a simple project to exemplify my problem, I incidentally stumbled upon a possible solution. I simply forgot some of the properties when defining my models. For clarity in the following example, instead of removing them, I've commented out the parts of the models I forgot:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestApp.Models
{
public class Family
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Father> Fathers { get; set; }
public virtual ICollection<Mother> Mothers { get; set; }
}
public class Mother
{
public int ID { get; set; }
public string Name { get; set; }
// public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Father
{
public int ID { get; set; }
public string Name { get; set; }
// public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Child
{
public int ID { get; set; }
public string Name { get; set; }
// public int MotherID { get; set; }
// public int FatherID { get; set; }
public virtual Mother Mother { get; set; }
public virtual Father Father { get; set; }
}
}
So, removing these SomethingID reference properties seems to solve my problem. As you can see in the controller of the sample project I'm linking to in the end of this post, I'm still able to cycle all the way around and do stuff like mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name without any problems. But all tutorials and examples about EF and Code First modeling I've been looking at (e.g. this one by Scott Guthrie) include these properties, so it feels wrong not to use them.
And so, my second question is: Will there be any drawbacks and problems I haven't discovered yet doing this?
Download example project here: http://blackfin.cannedtuna.org/cyclical-reference-test-app.zip, and open TestSolution.sln. The properties are commented out in the example project. Uncomment the lines in TestModels.cs to add the properties, resulting in the cyclical reference exception.
NB: The solution is creating and seeding a SQL CE database located at c:\TestApp.sdf
Update, December 2011:
I never solved this problem technically, but I quit my job and found another job where I don't have to use Microsoft technologies. That sort of solved my problem :)
As the tech support at the old place used to write when fixing issues: "A workaround or solution has been provided".
But if I created the database tables myself (which I prefer not to,
that's what I like about Code First) it would be a perfectly valid
data structure, as far as I can tell.
This is something you should double check. The exception comes directly from the database and not from Entity Framework. It's likely that also a table structure with the same constraints created by hand will be invalid. Keep in mind that your foreign key properties Mother.FamilyID, Father.FamilyID, Child.MotherID and Child.FatherID are not nullable, so they represent required relationships and the corresponding columns in the database are also not nullable.
When you remove all these properties from your model classes your relationships become suddenly optional because the navigation properties can be null. This is another model now since the FK columns in the DB can be nullable! Apparently this is an allowed model.
If you want to have still foreign key properties in your model which represent optional instead of required relationship you can use nullable types: public int? FamilyID { get; set; }, public int? MotherID { get; set; }, etc.
This is a known problem and you're not the first to bump into it. From what I've heard they are working on a better solution in the upcoming version of WCF, however for the time being from my experience you are much better off creating DataContracts that represent the data to be sent over the wire thereby changing the data structure to remove the cyclic reference.
I know it's a pain, but there are other benefits to be had in that you most likely will want to make other changes to structures that your clients consume anyway instead of letting them play with the objects as they exist in your db
I had much the same problem however I solved it using the advice in this answer Entity Framework Code First - two Foreign Keys from same table which works better than changing the type of the key columns to optional.
Related
I am learning C# and having trouble with viewmodels and AutoMapper.
I currently have two model classes and one viewmodel as below:
using System.ComponentModel.DataAnnotations;
namespace CarShowroom.Models
{
public class CarModel
{
[Key]
public int ModelID { get; set; }
public string Manufactuer { get; set; }
public string Model { get; set; }
public int ModelYear { get; set; }
public virtual Manufacturer manufacturer { get; set; }
public static int ManufacturerID { get; internal set; }
}
}
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace CarShowroom.Models
{
public class Manufacturer
{
[Key]
public int ManufacturerID { get; set; }
public string ManufacturerName { get; set; }
public virtual List<CarModel> carModels { get; set; }
}
}
using System.Collections.Generic;
namespace CarShowroom.Models
{
public class CarViewModel
{
public IList<CarModel> CarModel { get; set; }
public Manufacturer Manufacturer { get; set; }
}
}
The problem is when I try to add a controller using the CarViewModel class, I get the error message
CarViewModel: EntityType 'CarViewModel' has no key defined. Define the key for this EntityType.
CarViewModels: EntityType: EntitySet 'CarViewModels' is based on type 'CarViewModel' that has no keys defined.'
I have looked into how to create viewmodels online and have found you do not need to key as this is not a DB Table. Please can someone advise where I have gone wrong with making a viewmodel and why EF thinks this should be a table?
I have also tried to use AutoMapper but the tutorials I found were so old they use features which have now been removed and I have been unable to find a new tutorial which really explains how to implement the mapping and also where I should be writing the 'Maps'.
If anyone could advise on either of the two solutions so I am able to continue learning I would be very grateful as I have been stuck on this problem for weeks. I have also been looking for a small completed project/tutorial which uses AutoMapper so I can read through and understand how AutoMapper works and how/where it should be written but I am unable to find anything.
Thanks,
Danny.
The main goal is the ability to have a many to many relationship between the table Mucle and Exercise. I want an Exercise to have both a primary and a secodary muscle group.
Is it possible to have two icollections in one model and only one in the other?
If someone could help with the "fluent configuration" as well, I would appreciate it!
Here is the code I have got right now.
public class Muscle
{
public int MuscleID { get; set; }
public bool IsFront { get; set; }
public string Name { get; set; }
public virtual ICollection<Exercise> Exercises { get; set; }
}
public class Exercise
{
public int ExerciseID { get; set; }
// ExerciseCategory
public int ExerciseCategoryID { get; set; }
public DateTime CreationDate { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public virtual ExerciseCategory ExerciseCategory { get; set; }
public virtual ICollection<Muscle> Muscles { get; set; }
public virtual ICollection<Muscle> MusclesSecondary { get; set; }
}
No way to map the model you described.
To map your model (2 n-m relationship) you would need a Junction table with a discriminator and you can't do it with EF.
You have several way to change your model to make it work with EF
You create a model (a class) for the Junction table and insert a discriminator in it. Your model changes (and I think that the new model is less clear)
Why is there a Muscles and MusclesSecondary? Can it be discriminated with an attribute of Muscle? In this case you can have the attribute in Muscle and remove Exercise.MusclesSecondary Then you have only an n-m relationship that EF handles with a Junction table.
If you want this model you can add 2 collections to Muscle (for example ExcercisesMuscle and ExercisesMusclesSecondary) and a 3rd not mapped collection where you have the content of ExcercisesMuscle and ExercisesMusclesSecondary toghether. About the ExcercisesMuscle and ExercisesMusclesSecondary they can be observable collections so you can cache the content of Exercises collection in an efficient way.
I'm an EF noob (any version) and my Google-foo has failed me on finding out how to do this. Which makes me think I must be doing this wrong, but here is the situation:
I'm definitely in an environment that is database first and the schema won't be updated by us coders. I'm also not a fan of 'automatic' code generation, so I've stayed away from the designer or the EF powertools (though I did run through them just to see them work).
To learn I imported the Northwind DB into my LocalDB to have something to play with while creating some simple Web API 2 endpoints. This all went well as I created slimmed down models of the Employees, Shippers, & Region tables in Northwind. Region was particularly interesting as it wasn't plural and EF had issues with that. Anyway, I got by that.
My trouble now is; I want to use a view instead of a table as my source and whatever I'm doing just doesn't seem to work. What I tried was setting it up just like I did the tables. But that produces a ModelValidationException error. I tried looking at the auto-generated code from the designer, but got no insight.
My models:
//-- employee, shipper, & region work as expected
public class employee {
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
public class shipper {
public int ShipperID { get; set; }
public string CompanyName { get; set; }
public string Phone { get; set; }
}
public class region {
public int RegionID { get; set; }
public string RegionDescription { get; set; }
}
//-- invoice is a view (actual viewname is 'Invoices')
//-- so i followed the same rules as i did for employee & shipper
//-- i have tried uppercase 'I' as well as a plural version of the model
public class invoice {
public string CustomerID { get; set; }
public string CustomerName { get; set; }
public string Salesperson { get; set; }
public int OrderID { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
}
My Context looks like this:
public class NorthwindDBContext : DbContext {
public DbSet<Employee> Employees { get; set; }
public DbSet<shipper> Shippers { get; set; }
public DbSet<region> Regions { get; set; }
public DbSet<Invoice> Invoices { get; set; } //-- offending line of code
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//--- fix for Region being singular instead of plural
modelBuilder.Entity<region>().ToTable("Region");
}
}
If I comment out the public DbSet<Invoice> Invoices { get; set; } line in the context everything works. Just by having the line present (even if i don't reference the Invoices property) I receive the ModelValidationException error when using the context in anyway.
Can anybody tell me what I'm doing wrong here?
Thanks.
Update: I tried this in one of my controllers, but I am too noob'ish to know if this is the right path either, though it worked as far as getting records.
using (var dbContext = new NorthwindDBContext()) {
return dbContext.Database.SqlQuery<Invoice>("select * from invoices").ToList();
}
Code-first conventions will look for an ID or InvoiceID property to use as a key. Your Invoice model has neither, while the others do. This is the specific reason your code is failing.
The less-specific one is that you can't have entities in EF which lack a unique key. If you can, have the view define a key. Otherwise, you may still be able to work around the issue.
I have the entities:
public class Plugin
{
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public virtual Plugin ParentPlugin { get; set; }
public virtual IEnumerable<Setting> Settings { get; set; }
}
public class Setting
{
public virtual int Id { get; set; }
public virtual Plugin Plugin { get; set; }
public virtual int Version { get; set; }
}
The problems is that when I added ParentPlugin, it added a ParentPlugin_Id to the Setting table in the DB. I can't figure out why it did this, however I'd like for Setting to only have a Plugin_Id not both Plugin_Id and ParentPlugin_Id in the database.
So, I would like to know 2 things to receive bounty:
1. How can I remove the ParentPlugin_Id reference from Setting?
2. And why did it do this in the first place (link to doc is ok, I could not find one explaining this)?
I'm using Fluent Nhibernate's automapping, mostly all defaults.
By specifying the exact FK name I was able to remove the extra ParentPlugin_Id. A bit counter intuitive to have to add an FK instead of removing one but...
.Override<Plugin>(m => m.HasMany(c => c.Settings).KeyColumn("Plugin_Id"))
This was figured out by a deeper understanding of the Foreign Key Conventions (https://github.com/jagregory/fluent-nhibernate/wiki/Auto-mapping)
I just declared some code-first models for a new project that uses EntityFramework.
public class BlogEntry
{
public long Id { get; set; }
public long AuthorId { get; set; }
public DateTime PublishedStamp { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public virtual User Author { get; set; }
}
public class User
{
public long Id { get; set; }
public string Email { get; set; }
// ...
}
class BlogDb : DbContext
{
public DbSet<BlogEntry> Entries { get; set; }
public DbSet<User> Users { get; set; }
}
Now suppose I want to retrieve the 10 most recent blog entries:
var entries = new BlogDb().Entries.OrderByDescending(...).Take(10).ToList();
The problem now is that accessing entry.Author will cause another database query. You wouldn’t want a separate such query for every blog entry. Now, it is my understanding that the purpose of Include is exactly this case, so I can say:
var entries = new BlogDb().Entries.Include(e => e.Author).(...).ToList();
However, that method doesn’t seem to exist. There is only an Include(string), like this:
var entries = new BlogDb().Entries.Include("Author").(...).ToList();
but this is annoying because it’s not compile-time checked and will be missed by the rename refactoring. Surely the version with the lambda is the “correct” approach.
Where did that method go? Is it no longer included in EntityFramework?
(I know that I can write an extension method for myself to achieve this, so you don’t have to. I’d just like to know whether I’m missing something.)
using System.Data.Entity;
It's in EF v4.1 and above, but you need a reference as it is an extension method.
Edit (thanks to #EastonJamesHarvey)
If using EF Core the import should be:
using Microsoft.EntityFrameworkCore;