Defining a one-to-one-or-zero relationship in Entity Framework with Code First - entity-framework-4.3

I know there are a lot of questions around on this subject, but I've not managed to find one that actually explains how to solve my particular problem. Which I suppose means that it might be insoluble (I think it might be 'backwards' to EF's way of thinking), but I have to ask.
I have a model with three (abbreviated) POCOs as so:
[Table("People")]
public class Person {
public int PersonID { get; set; }
[Required]
public string PersonName { get; set; }
}
public class Location {
public int LocationID { get; set; }
public int LocationTypeID { get; set; }
public virtual LocationType LocationType { get; set; }
}
public class Van : Location {
public int PartyID { get; set; }
public virtual Party Party { get; set; }
}
These are backed by (abbreviated) database tables (we write these by hand):
CREATE TABLE People (
PersonID INTEGER IDENTITY NOT NULL,
PersonName VARCHAR(255) NOT NULL,
PRIMARY KEY (PersonID)
)
CREATE TABLE Locations (
LocationID INTEGER IDENTITY NOT NULL,
LocationTypeID INTEGER NOT NULL,
FOREIGN KEY (LocationTypeID) REFERENCES LocationTypes(LocationTypeID)
)
CREATE TABLE Vans (
LocationID INTEGER NOT NULL,
PersonID INTEGER NOT NULL,
PRIMARY KEY (LocationID),
FOREIGN KEY (LocationID) REFERENCES Locations(LocationID),
FOREIGN KEY (PersonID) REFERENCES People(PersonID)
)
You can probably imagine what LocationTypes looks like.
Locations is the root of a table-per-type hierarchy - there are also check constraints in place to enforce this. Vans are a kind of location, as are other things irrelevant here like Warehouse.
Now, a Van belongs to a Person, in that we issue a van to an employee and it's their responsibility to fill it up with fuel, not crash it, take it to customer sites and order more stuff when they've used up all their supply of screws, drill bits and armoured DC cable. However, not every Person has a van (some of them work in pairs in one van), and the Person table doesn't have a foreign key which points to the Van - it's the other way around. This is in some sense a historical accident, but it models the situation quite neatly because while a Person doesn't have to have a Van, a Van most assuredly has to have a person.
So to my question: how do I get Person to have a navigation property with their Van in it?
public virtual Van Van { get; set; }
I've done a lot of playing around with data annotations and the fluent API, and the closest I've got is this in OnModelCreating:
modelBuilder.Entity<Van>()
.HasRequired(v => v.Person)
.WithOptional(p => p.Van);
Unfortunately this tries to populate the Van property with a proxy that yields a Location object. It might even be the right Location object (I haven't been able to check), but it's not realised that it should be looking for vans. I do suspect, however, that it might be trying to match PersonID against LocationID when it does the lookup - without the Fluent API mapping, I just get no vans at all, which is what I'd expect (all PersonID values are lower than the lowest LocationID values which correspond to vans so couldn't possibly find anything).
This would no doubt be quite easy if Person had a nullable foreign key to Van, but then we'd have foreign keys in both directions, and if we took the one out of Van then we'd not be modelling the absolutely essential constraint that a Van has a Person.
So, I suppose, Van owns this relationship, and the Van property on Person is an inverse navigation property, but it seems EF isn't very good at this kind of trick with one-to-ones even if one end is optional. Is there a way to make it work, or do I have to accept a compromise?
We generally refuse to compromise the database model for the sake of Entity Framework's missing features. What I really need is a way to tell EF that the Van property on Person can be populated by joining to Vans on Vans.PersonID = Person.PersonID.

The problem is that this is at the moment not supported. You mentioned that you didn't managed to find any question where would be your problem solved. I wonder if you find any question mentioning that EF doesn't support unique constraints / candidate key which is absolutely necessary to solve this type of one-to-one relations.
In database one-to-one relation can be achieved only if FK in dependent table is unique. This can be done by two ways: placing unique constraint (index) on FK column in dependent table or using PK in dependent table as FK to principal table.
EF enforces same rules for referential integrity as database but in case of one-to-one relationships and lack of support for unique constraint it doesn't support the former way. EF can model one-to-one relationship only by using PK of dependent table as FK to principal table.
You can vote for support of Unique constraints / candidate keys on Data UserVoice.
How to solve your particular issue? By cheating EF. Let EF think that you have one-to-many relation and place unique constraint on PersonID in Van table. Than update your Person like this:
[Table("People")]
public class Person {
public int PersonID { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Van> Vans { get; set; }
[NotMapped]
public Van Van
{
get { return Vans.FirstOrDefault(); }
set
{
Vans.Clear();
if (value != null)
{
Vans.Add(value);
}
}
}
}
It is pretty ugly workaround because Vans collection is still public. You can play with its visibility but make sure you understand few things:
Once Vans is not public you must map it in OnModelCreating and for that context must be able to see the property or you must provide mapping configuration which does that. Check this and this for some more information.
Vans property must not break rules for proxy creation to support lazy loading
Eager loading must use Vans property

Related

Why does LINQ throw a NotSupportedException?

Let's say we have a table called Car with columns such as ID, Identification, ModelName, OwnerId etc where OwnerId points to the primary key in the Owner table. This is all good, but then we want to add a Driver to the car, since we want to know who drives each car at a given time.
Sounds straight forward, right? Just create a Driver table and add a new nullable (there's no driver if the car is in the garage etc) int column called DriverId to the Car table, connect it with a foreign key and we're good to go.
I did this, and updated the EDMX in the model designer so the new table, column and foreign key showed up. All looks good. The DriverId property and the Driver navigation property are both there in the generated code and the new Driver class is also generated.
Now when I tried to use this new table and connect drivers to cars there's something very wrong. It looks like LINQ doesn't know about the DriverId column or the foreign key (navigation property) to Driver.
If I try getting a car with a given driver:
Car car = (from c in db.Cars.Where(x => x.DriverId == driverId) select c).FirstOrDefault();
I expect to get a car if the driver is currently driving a car or null otherwise.
What I get is this error message:
System.NotSupportedException: The specified type member 'DriverId' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Let's say we want to add a new car with a driver:
Car car = new Car{ blah, blah, etc, DriverId = driverId };
db.Cars.Add(car);
db.SaveChanges();
This seems to work fine. The new car gets inserted into the database. The only thing is the DriverId column is null, so obviously it doesn't work fine...
I'm guessing these things are connected. I just don't see what the issue is. Anyone know why or got some suggestions as to what I can try?
EDIT:
The Car and Driver classes is purely generated code, so I'm sure they look familiar:
[DataContract(IsReference = true)]
[KnownType(typeof(Owner))]
[KnownType(typeof(Driver))]
public partial class Car
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Identification { get; set; }
[DataMember]
public Nullable<int> OwnerId { get; set; }
[DataMember]
public Nullable<int> DriverId { get; set; }
//Navigation properties
[DataMember]
public virtual Owner Owner { get; set; }
[DataMember]
public virtual Driver Driver { get; set; }
}
[DataContract(IsReference = true)]
[KnownType(typeof (Car))]
public partial class Driver
{
//Constructor
public Driver()
{
this.Cars = new HashSet<Car>();
}
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
//Navigation properties
[DataMember]
public virtual HashSet<Car> Cars { get; set; }
}
Since the problem seems to be pinned and the work is in progrees, I'm pasting here trimmed discussion as a temporary "answer", just to leave the trace of work and to be sure that the comments with facts/thoughts don't not evaporate. Feel free to add/trim whatever here.
Eirik: if you manage to narrow it down to exact causes, please write a follow up answer explaining what was wrong and why did it compile silently with no errors. Just don't forget to "accept" it ;) Or if you like, add the info at the bottom of this "chatlog". I communitized this answer, so no points will be awarded.
Me: I too think one of your models went desynchronized. (..) One common thing was duplication of entries or not-updating the identifiers between model spaces. Could you try regenerating the EDMX from scratch, or do you have too many manual changes?
Eirik:
I've experienced bad updates earlier and solved them by removing the tables involved from the model and then running another update to get them readded. As for regenerating the EDMX from scratch...no. There's a bazillion manual changes, so I simply don't have the time to change it all back. I have already tried to remove the tables involved in this issue and readded them without luck. Same error/issue.
Me: Since nav-props are generated too, I assume you have a Driver property? Have you tried using objects instead? I mean, db.Cars.Where(x => x.Driver.Id == driverId) select c and Car car = new Car{ blah, blah, etc, Driver = driverObject };? If that worked, it'd indicate property<->column naming clash somewhere (...)
Eirik:
Changing x.DriverId == driverId to x.Driver.ID == DriverId results in the same error message (only initializers, members and navigation properties allowed). Driver should be recognized as a navigation property, but it isn't. DriverId should be recognized as a member, but it isn't. Insert also silently fails. That is, it inserts but with Car.DriverId being null.
Me: Nav props does not work. Your EDMX is screwed up, (..) I'd now chew through the three EDMX sections and verify all column, props, navs, and so on are correctly referring each other. Can also do another test: create a new edmx that contains only these tables, dont touch it, leave generated names and (..) compare contents of the new edmx with old big edmx and look for difference. (..)
Eirik:
I've created a new test project and generated the EDMX from scratch and stuff works as intended. So I'm guessing this is just a really bad case of update mess in the model designer. I'm currently going through both EDMX files and comparing the content. No solution yet, but I suspect I will find it eventuelly...zzZzzZ..thanks for the input so far!

Entity Splitting For One-To-Many table relationships

Following this article (What are best practices for multi-language database design?), I have all my database tables splitted in two: the first table contains only language-neutral data (primary key, etc.) and the second table contains one record per language, containing the localized data plus the ISO code of the language. The relationship between the two tables is one to many.
Here a screenshot of the datamodel: https://dl.dropboxusercontent.com/u/17099565/datamodel.jpg
Because the website has 8 languages, for each record in table "CourseCategory" I have 8 record in table "CourseCategoryContents". The same happens with "Course" and "CourseContent"
Then I use Entity Splitting in order to have only one entity for the Course Category and one entity for the Course:
public class CourseCategoryConfiguration : EntityTypeConfiguration<WebCourseCategory>
{
public CourseCategoryConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.Order, i.Online });
m.ToTable("CourseCategories");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords });
m.ToTable("CourseCategoryContents");
});
}
}
public class CourseConfiguration : EntityTypeConfiguration<WebCourse>
{
public CourseConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.CategoryId, i.Order, i.Label, i.ThumbnailUrl, i.HeaderImageUrl });
m.ToTable("Courses");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords, i.Online });
m.ToTable("CourseContents");
});
}
}
Then to retrive the courses in a desired language including their category I do this:
using (WebContext dbContext = new WebContext())
{
// all courses of all categories in the desired language
return dbContext.Courses
.Include(course => course.Category)
.Where(course => course.LanguageCode == lan
&& course.Category.LanguageCode == lan)
.ToList();
}
}
Entity splitting works fine with one-to-one relationships, but here I have one-to-many relationships.
The website has contents (CourseCategories and Courses) in 3 languages ("en", "de", "fr").
EF correctly returns all the Courses with their Category in the right language (eg. in english), but returns each record 3 times. This is because I have the CourseCategory in 3 languages too.
The only one working solution I came up is avoiding using ".Include(Category)", getting all the courses in the desired language in first, then, in a foreach cycle, for each Course retriving its Category in language. I don't like this lazy loading approach, I would like to retrive all the desired data in one shot.
Thanks!
The best solution is to map tables to the model as it then in your model Course class will have a navigation property ICollection<CourseCategoryContent>.
In this case you just project this model to DTO or ViewModel "according to your application design"
e.g.
Your model will look like this
public class Course
{
public int Id {get; set;}
public int Order {get; set;}
public ICollection<CourseCategoryContent> CourseCategoryContents {get; set;}
}
public class CourseCategoryContent
{
public string LanguageId {get; set;}
public string Name {get; set;}
}
Then just create new DTO or ViewModel like :
public class CourseDTO
{
public int Id {get; set;}
public int Order {get; set;}
public string Name {get; set;}
}
Finally do the projection
public IQueryable<CourseDTO> GetCourseDTOQuery ()
{
return dbContext.Courses.Select(x=>new CourseDTO{
Id = x.Id,
Order = x.Order,
Name = x.CourseCategoryContents.FirstOrDefault(lang => lang.LanguageId == lang).Name,
});
}
And note that the return type is IQueryable so you could do any filter, Order or grouping operation on it before hitting the database.
hope this helped
No fix-all answer i'm afraid, every way has a compromise.
I've used both the database approach (10+ language dependent tables) and the resource file approach in fairly large projects, if the data is static and doesn't change (i.e you don't charge a different price or whatever) I would definately consider abstracting language away from your database model and using Resource keys then loading your data from files.
The reason or this is the problem you are experiencing right now where you can't filter includes (this may have changed in EF6 perhaps? I know it's on the list of things to do). You might be able to get away with reading it into memory and filtering them though like you're doing but this meant it wasn't very performant for us and I had to write Stored Procedures that I just passed the iso language and executed in EF.
From a maintenance point of view it was easier as well, for the DB project I had to write an admin console so people could log on and edit values for different languages etc. Using resource files I just copy-pasted the values into excel and emailed them to the people we use to translate.
It depends on the complexity of your project and what you prefer, i'd still consider both approaches in future.
TLDR: options that i've found are:
1) filter in memory
2) lazy load with filter
3) write stored procedure to EF and map that result
4) use resources instead
Hope this helps
EDIT: After looking at diagram it looks like you may need to search against the language dependant values? In that case resources probably won't work. If you're just letting them navigate off a menu then you're good to go.

Programmatically Change Database Table EntityFramework Model Object Refers to

Question is in the title. Can we programmatically change the database table which an object in the Model class, like one below, refers to and continue to operate on the new table?
public class Word
{
public int ID { get; set; }
public string Text { get; set; }
}
This originally refers to "Words" table automatically in EntityFramework, is there a way to change it before/during runtime? If so, how?
EDIT:
I get all the string used in Views in the project from the database table, "Words", by their ID's. Now, what I want is, a user enters a new language to system, and a new table will be created, for example WordsEnglish. From then, the Word object will refer to WordEnglish, if user selects English as language.
It would be desirable with a use case to better understand what you are trying to accomplish, but here goes...
In the DbContext.OnModelCreating method you can configure the model, e.g.
// Removes pluralization convention for all tables.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
or
// Specific table name for Word Entity.
modelBuilder.Entity<Word>().ToTable("TableContainingWords");
If you are changing your model, Code First Migrations might be what you need.
I havent found a way to truly dynamically extend an EF model at runtime. Given what goes on in DB context inherited class, the use of generated views for performance and a model class approach, avoiding recompilation seems hard. I have generated code, compiled and access this using assembly discovery approaches. But this is all unsatisfactory from my viewpoint , so i have stopped investigating this path. Very clunky outcome.
Ironically the topic you provide as a use case for such a problem, is one that doesnt need dynamic EF in my view.
I have exactly the same use case, language specific look for messages/labels etc Ie a language specific textpool.
Why not add language to the class/table.
Use a table or Enum for supported languages.
Use Language in the Textpool table/s
Use a different model class for presentation. (view model).
So you can present it the way like .
public class Word
{
Guid ID {get;set;} // logical key is WordID + Language
public int WordID { get; set; } // implement with new id or 2 field key
public Language Language {get;set;} // see cultureInfo for more details
public bool IsMaster {get;set;}
public string Text { get; set; } // consider renaming due to reserved word implications
}
public class language
{
int ID,
String Lang
}
}

How to access extra data in Many-to-Many relationship in CoolStorage?

I'm using CoolStorage in a project where I have some Many-to-Many relationships. Some of the join tables have extra data on them which describe the relationship.
For example: Table Alpha, Beta, and AlphaBeta.
Many-to-Many relationship between Alpha and Beta is stored in AlphaBeta. Primary key of AlphaBeta is the combination of the keys from Alpha(AlphaID) and Beta(BetaID), which is (AlphaID, BetaID).
But AlphaBeta also has some additional data like 'DisplayOrder INT NOT NULL'
in the data classes, I have the Many-to-Many relationships defined using the [ManyToMany("AlphaBeta", pure=true)] attribute, but how can I access the DisplayOrder for each?
I don't think it matters, but this is a Windows Phone app using SQLite.
If you have additional fields in your link table, you have to set "pure = false" and add a data object for the link table.
Your link data object might look like this:
[MapTo("AlphaBeta")]
public abstract class AlphaBeta : CSObject<AlphaBeta>
{
[ManyToOne]
public abstract Alpha Aplha { get; set; }
[ManyToOne]
public abstract Beta Beta { get; set; }
public abstract int DisplayOrder { get; set; }
}

Beginner EF4 / CodeFirst / MVC3 help

Although I love what I'm learning, I'm finding it a struggle and need some help
I've been using these two tutorials which I think are awesome:
http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx
http://msdn.microsoft.com/en-us/data/gg685467
Currently my main problem/confusion is:
I have a CodeFirst table/entity I don't know how to correctly get data from other tables/entities to show in my views:
public class Car {
public int ID { get; set; }
public string Name { get; set; }
public int EngineID { get; set; }
public virtual Engine { get; set; }
}
public class Engine {
public int ID { get; set; }
public string Name { get; set; }
public string Manufacturer { get; set; }
// (plus a whole lot of other things)
}
Now when I create a View for Cars (using the List type/option) I get a nice autogenerated list
#foreach (var item in Model) {
<tr>
<td>#item.ID</td>
<td>#item.Name</td>
<td>#item.EngineID</td>
</tr>
Perfect... except EngineID is mostly worthless to the viewer, and I want to show Engine.Name instead
So I assumed I could use EF lazy loading:
<td>#item.Engine.Name</td>
Unfortunately when I tried that, it says my ObjectContext has been disposed so can't get any further data requiring a connection
Then I tried going to the controller and including the Engine.Name
var cars = (from c in db.Cars.Include("Engine.Name") select c;
Which tells me: Entities.Engine does not declare a navigation property with the name 'Name'
... ? Lies
Include("Engine") works fine, but all I want is the Name, and Include("Engine") is loading a large amount of things I don't want
Previously in a situation like this I have created a view in the DB for Car that includes EngineName as well. But with CodeFirst and my noobness I haven't found a way to do this
How should I be resolving this issue?
I thought perhaps I could create a Model pretty much identical to the Car entity, but add Engine.Name to it. This would be nice as I could then reuse it in multiple places, but I am at a loss on how to populate it etc
Wanting to learn TDD as well but the above is already frustrating me :p
Ps any other tutorial links or handy things to read will be greatly appreciated
It isn't lies as you are actually trying to include a property that's a 2nd level down withouth giving it a way to navigate. If you let EF generate your DB with this structure, it would likely have made a navigation table called something like Car_Engine and if you include the name without the object it HAS mapped, then it's not got a navigation property in your new object.
The simple way around this is to go:
(from c in db.Cars.Include("Engine") select new { c, EngineName = c.Engine.Name }
If you still get navigation property errors then you might need to make sure your are mapping to your schema correctly. This can be done with EntityTypeConfiguration classes using the fluent API - very powerful.
This of course won't help in strongly typing your car object to show in MVC.
If you'd like to get around this, your gut feeling is right. It's pretty common to use viewmodels that are read only (by design, not necessarily set to readonly) classes that provide simple views of your data.
Personally I keep my model quite clean and then have another project with viewmodels and a presentation project to populate. I'd avoid using overlapping entities in your core model as it might lead to unpredictable behaviour in the data context and at least a peristance nightmare when updating multiple entities (ie who's responsible for updating the engine name?).
Using you viewmodels, you can have a class called CarSummaryView or something with only the data you want on it. This also solves the issue of being vulnerable to overposting or underposting on your site. It can be populated by the above query quite easily.
PS There's a bunch of advantages to using viewmodels beyond just not loading full heirarchies. One of the biggest is the intrinsic benefit it gives you with avoiding over and underposting scenarios.
There's loads of ways to implement viewmodels, but as a simple CarView example:
public class CarView
{
public int ID { get; set; }
public string Name { get; set; }
public string EngineName { get; set; }
}
This should be clearly seperated from your entity model. In a big project, you'd have a single viewmodels project that the presenter can return, but in a smaller one you just need them in the same layer as the service code.
To populate it directly from the query, you can do the following
List<CarView> cars = (from c in db.Cars.Include("Engine.Name") select new CarView() { ID = c.ID, Name = c.Name, EngineName = c.Engine.Name }).ToList();

Resources