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; }
}
Related
I have developed an app for tracking multi-party coordination on proposed change requests.
I only use two table, with a one-to-one relationship. One table correlates to fields on an existing official paper form, while the other table tracks additional information in a one-to-one relationship.
I previously developed this app as a standalone project, using MS Access, but now, I am adding the app to a "one-stop shopping" SQL Server database environment.
My problem comes in my DbSet statements. The table names which the DBA chose result in errors which I never had when the app was stand-alone:
Below is the C# code for the DbContext portion:
namespace FormTracker
{
public class ApplicationDbContext:DbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<T__AODMS_1067_tracking_fields> T__AODMS_1067_tracking_fieldss { get; set; }
public DbSet<T__AODMS_1067_tracking_non_1067_fields> T__AODMS_1067_tracking_non_1067_fields_Recordss { get; set; }
}
}
The portions between the <> are what is being flagged when build is executed.
Any ideas? possibly something totally obvious that I'm not seeing?
My database has a one-to-many relation between "UsageRecord" and "Dimension"
This is modelled as follows (using a Database-First approach):
public partial class Dimension
{
...
public virtual ICollection<UsageRecord> UsageRecord { get; set; }
}
Usage Record class:
public partial class UsageRecord
{
public long Id { get; set; }
...
public long DimensionId { get; set; }
public virtual Dimension Dimension { get; set; }
}
So, if i query the list of UsageRecords (EagerLoading):
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).ToList()
i get a list of UsageRecord objects I can navigate through during debug:
Please notice that the Dimension object is null, and this is correct since i haven't included it in the query.
Now, If i try to include it, the application crashes:
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).Include(p => p.Dimension).ToList();
Postman exits with a 502 error, and the VS Debug first shows a list of question marks "?" before crashing.
I think this is due to the fact that by Including the Dimension object, this loops through the list of UsageRecords attached and then the Dimension again and again.
How can I avoid it?
In order to retrieve your result from LINQ query you can solve your issue in these ways:
Configure your serializer to ignore loops
Create a view model for your controller's action
Use anonymous type from Select result in your controller's action
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!
i have been using code first technique in ASP.NET MVC3 app. here it is very basic issue i.e. how to update a navigation property. following is detailed code.
public class Destination
{
public int ID {get;set;}
// some other properties
public Country {get;set;}
}
public class Country
{
int ID {get;set;}
string Name {get;set;}
}
//i have simple structure as above. when i go to update destination entity. Country is not getting updated.even i tried following:
_db.Entry(Destination.Country).State = System.Data.EntityState.Modified;
_db.Entry(Destination).State = System.Data.EntityState.Modified;
//_db.ChangeTracker.DetectChanges();
_db.SaveChanges();
Secondly when i go for Add it works fine. is there any need to have foriegnKey relationship explicityly required?
You can add entity in three ways:
Calling the Add() method on DbSet. This puts the entity into the Added state, meaning that it will be inserted into the database the next time that SaveChanges() is called.
context.Destination.Add(yourDestination);
Changing its state to Added.
context.Entry(yourDestination).State = EntityState.Added;
Adding a new entity to the context by hooking it up to another entity that is already being tracked
context.Destination.Country.Add(yourCountry);
Regards.
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();