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!
Related
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
}
}
I just installed VS 2012 Premium and I've been following along with the PluralSight ASP.Net MVC 4 Tutorial. I'm on chapter 4 and the project creates a database automatically using EF 4 Code First.
The strange thing is that it creates extra database columns rather than matching the properties in my Restaurant.cs model file.
In Restaurant.cs I have:
namespace OdeToFood.Models
{
public class Restaurant
{
public int ID { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
}
However, in the OdeToFoodDB dbo.Restaurants table it generates:
ID (PK, int, not null)
Name (nvarchar(max), null)
Address_Street (nvarchar(max), null)
Address_City (nvarchar(max), null)
Address_State (nvarchar(max), null)
Address_Country (nvarchar(max), null)
but there are no Street, City, State, and Country properties in the class. In the video the database only has the ID, Name, and Address columns, but I don't know if that might have to do with the fact that he was using EF while it was in the prerelease state. Are there some big assumptions that the new EF makes?
Does anyone have any ideas what is going on here?
I've already deleted the generated database, files in obj and bin, recreated a Restaurant.cs class, uninstalled EF4, and added EF5, but nothing has changed the end result. I'm still getting those extra columns that have nothing to do with my Restaurant.cs file.
What you see is by design. Address is a Complex Type and this is how Complex Types are represented in a database table.
Look here for an explanation about this very example.
Going through the Tutorial v2.0, I've just added data access to the sdf database. When I click on the Stores link after running the app, I get an ArgumentNullException on the following line in my StoreController:
var genres = storeDB.Genres.ToList();
In the watch window, I'm seeing that the StoreDB is not null, but the Albums & Genres are null. At first I thought this was a connection string issue, but I've pulled the database from the completed code file along with the connection string. Running the complete solution works fine, but mine fails as described above. I'm not seeing any differences in the code I entered. In fact, most of what I entered was copied and pasted.
How do I zero in on what the actual problem is?
The class MusicStoreEntities was not inheriting from DbContext. IOW, I had left off :DbContext from the class definition and did not notice it:
public class MusicStoreEntities: DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
}
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
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();