Entity Framework 5 - code first array navigation property one to many with Interface Type - ef-code-first

These are my classes:
public class Post : IPost
{
public int Id { get; set; }
public virtual int[] DuplicateOf { get; set; }
public virtual ICommentInfo[] Comments { get; set; }
}
public class CommentInfo : ICommentInfo
{
public virtual string Author { get; set; }
public virtual int Id { get; set; }
public virtual string Text { get; set; }
public virtual int PostId{ get; set; }
[ForeignKey("PostId")]
public virtual Post Post { get; set; }
}
With this CommentConfiguration added to OnModelCreate():
HasRequired(c => c.Post)
.WithMany(b=>(ICollection<CommentInfo>) b.Comments)
.HasForeignKey(b=>b.PostId)
.WillCascadeOnDelete(true);
I really cannot understand why the property Comments is always null, and why EF doesn't initialize it since it's virtual.
I tried disabling lazy loading too, but when i try loading the navigation property with context.Post.Include("Comments") an error tells me that "There is not a navigation property called Comments".
So I tried using Entity Framework Power Tools Beta 3 to see the Entity Data Model, and I discovered that there is not a navigation end for table "Post" even if there is the relationship between the two tables and there's the Comment table end too.
I sincerly don't know which way to turn, could be a problem of Array?? Should I use an Icollection property??
Though I cannot change the type of that property because Post is implementing an Interface.
Every sample I look at is clear and easy to make work. Please help me.. Thank you in advance.
EDIT:
This is what I changed after looking at the link I posted yesterday.
public class Post : IPost
{
public int Id { get; set; }
public virtual int[] DuplicateOf { get; set; }
public virtual ICollection<CommentInfo> Comments { get; set; }
ICommentInfo[] IPost.Comments {
get { return Comments ; }
set { Comments = (CommentInfo[])value; } }
}
The exception is: System.ObjectDisposedException :The ObjectContext instance has been disposed and can no longer be used for operations that require a connection and raises when the application tries to get the Comments.
If I remove the virtual key the exception disappear but the property remain always null and the values don't persist in any way.
EDITv2
I've solved my problem adding a new property and map my old property to it.
public class Post : IPost
{
public int Id { get; set; }
public virtual int[] DuplicateOf { get; set; }
public ICommentInfo[] Comments
{
get { return ListComments.ToArray(); }
}
public List<CommentInfo> ListComments {get;set;}
}
In my PostConfiguration OnModelCreate() I used the ListComments property as a navigation property like this:
HasMany(b => b.ListComments)
.WithRequired(c=>c.Post)
.HasForeignKey(c=>c.PostId)
.WillCascadeOnDelete(true);
Now it perfectly works, it was simpler than I expected and when I try to receive the Comments Collection, if I include the "ListComments" property, I get the array of Post.
Thank you for your help!

I can't access the link in your comment, but I assume you changed
public virtual ICommentInfo[] Comments { get; set; }
into the common way to define navigation properties:
public virtual ICollection<CommentInfo> Comments { get; set; }
because entity framework does not support interfaces in its conceptual model.
The exception about the disposed context means that you access this property after fetching Post objects from the database and disposing the context. This triggers lazy loading while the connection to the database is lost. The solution is to use Include:
var posts = context.Posts.Include(p => p.Comments).Where(...)
Now posts and comments are fetched in one go.

Related

Adding multiple Models in a Single view

In my ASP.NET Web forms app, I have a Model that contains a property of Collection of other Model :
public class FloorPattern
{
public FloorPattern()
{ }
public FloorPattern(string projId, int flrNum)
{
ProjectId = projId;
FloorNumber = flrNum;
}
public int FloorNumber { get; set; }
public int Units { get; set; }
public ICollection<UnitPattern> UnitPattern { get; set; }
I scafolded FloorPattern model, but it doesn't contain any thing for UnitPatern input/dispaly . I am looking at when user enters value in "Units" property, the UnitPattern creates a list of that many items & lets user enter details.
How can I get this am not able to get it ? Can you please help me in the same ???
Any help is highly appreciated.
Thanks
Not used to webforms, apologies if this does not help.
What about making it virtual? Does lazy loading
public virtual ICollection<UnitPattern> UnitPattern { get; set; }

Many-to-Many Relationship, junction table not recognized

I would like to set up a many to many relationship in ASP.NET MVC4.
The goal is to extend the default UserProfile class with a list of Timeline objects, which belong to the user.
A Timeline could be shared by multiple users, so the Timeline class should have an list of UserProfile objects aswell.
Timeline class:
namespace MvcApplication1.Models
{
public class Timeline
{
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public List<UserProfile> Users { get; set; }
}
public class TimelineContext : DbContext
{
public DbSet<Timeline> Timelines { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
// Added the following because I saw it on:
// http://www.codeproject.com/Tips/548945/Generating-Many-to-Many-Relation-in-MVC4-using-Ent
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Timeline>()
.HasMany(c => c.Users)
.WithMany(s => s.Timelines)
.Map(mc =>
{
mc.ToTable("TimelineOwners");
mc.MapLeftKey("TimelineId");
mc.MapRightKey("UserId");
});
}
}
}
UserProfile class (default class with an added property):
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Timeline> Timelines { get; set; }
// Added the following because I saw it on:
// http://www.codeproject.com/Tips/548945/Generating-Many-to-Many-Relation-in-MVC4-using-Ent
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserProfile>()
.HasMany(c => c.Timelines)
.WithMany(s => s.Users)
.Map (mc =>
{
mc.ToTable("TimelineOwners");
mc.MapLeftKey("UserId");
mc.MapRightKey("TimelineId");
});
}
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public List<Timeline> Timelines { get; set; }
}
I have a connection table with foreign keys:
When creating an instance of Timeline, the Users list is null:
Timeline timeline = db.Timelines.Find(id); // timeline.Users = null
Can somebody please enlighten me, how should I set this up working?
I'm totally new to ASP.NET MVC4.
Edit 1: I understand I should not extend UserProfile but create another class to store users. Once the many-to-many relationship works, I will refactor and go into that direction.
But first I would like to know why is it not working.
Edit 2:
The double context also caused problems, two databases were created for the two contexts and the pure join table was empty in one of them.
I suggest that you work through this article about the options how you can load navigation properties with Entity Framework. This is very basic knowledge which is important for every kind of relationship, not only many-to-many relationships.
Looking at that article you will find then that this line...
Timeline timeline = db.Timelines.Find(id);
...does not load any related entities. So, it's expected that timeline.Users is null, even if the entities are related in the database.
If you want to load the Users you can use eager loading:
Timeline timeline = db.Timelines.Include(t => t.Users)
.SingleOrDefault(t => t.Id == id);
This is a single database query. Or to enable lazy loading you have to mark your navigation properties as virtual:
public virtual List<UserProfile> Users { get; set; }
//...
public virtual List<Timeline> Timelines { get; set; }
You can then use your original code:
Timeline timeline = db.Timelines.Find(id); // first query
var users = timeline.Users; // second query
This will run two separate queries. The second is performed as soon as you access the navigation property for the first time.
BTW: Is there a reason why you have two context classes - TimelineContext and UsersContext? "Normally" you need only one context.
I'm not a fan of messing with the working of the internal userprofile. I would suggest creating your own user class, linking it to the simplemembershipprovider and adding functionality there. At max you'll extend the accountclasses a little to add more fields to register with, but that's about it.
Follow this extremely handy guide to get things working and let me know if you encounter an error.

ASP.NET Web API - Entity Framework - 500 Internal Server Error On .Include(param => param.field)

I am currently working on a Web API project with a Database-First method using Entity Framework (which I know is not the most stable of platforms yet), but I am running into something very strange.
When the GET method within my APIController tries to return all records in a DbSet with a LINQ Include() method involved such as this, it will return a 500 error:
// GET api/Casinos
public IEnumerable<casino> Getcasinos()
{
var casinos = db.casinos.Include(c => c.city).Include(c => c.state);
return casinos.AsEnumerable();
}
Yet, this method works fine, and returns my data from within my database:
// GET api/States
public IEnumerable<state> Getstates()
{
return db.states.AsEnumerable();
}
So I have proved in other instances that if it returns the entities without LINQ queries, it works great, yet when there is an Include method used upon the DbContext, it fails.
Of course, trying to find this error is impossible, even with Fiddler, Chrome/Firefox dev tools, and adding in GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
If anyone has resolved this, it would be nice to know a nice resolution so I can start returning my data! Thanks!:)
P.S. I am using SQL Server 2012
This is happening due to error in serialization (Json/XML). The problem is you are directly trying to transmit your Models over the wire. As an example, see this:
public class Casino
{
public int ID { get; set; }
public string Name { get; set; }
public virtual City City { get; set; }
}
public class State
{
public int ID { get; set; }
public string Name { get; set; }
[XmlIgnore]
[IgnoreDataMember]
public virtual ICollection<City> Cities { get; set; }
}
public class City
{
public int ID { get; set; }
public string Name { get; set; }
public virtual State State { get; set; }
[XmlIgnore]
[IgnoreDataMember]
public virtual ICollection<Casino> Casinos { get; set; }
}
public class Context : DbContext
{
public Context()
: base("Casino")
{
}
public DbSet<Casino> Casinos { get; set; }
public DbSet<State> States { get; set; }
public DbSet<City> Cities { get; set; }
}
Pay attention to the XmlIgnore and IgnoreDataMember. You need to restrict avoiding serialization so it doesn't happen in circular manner. Further, the above model will still not work because it has virtual. Remove virtual from everywhere namely City, Cities, Casinos and State and then it would work but that would be inefficient.
To sum up: Use DTOs and only send data that you really want to send instead of directly sending your models.
Hope this helps!
I had same problem in ASP.Net Core Web Api and made it working with this solution:
Add Microsoft.AspNetCore.Mvc.NewtonsoftJson nuget package to web api project.
and in Startup.cs class in ConfigureServices method add this code:
services.AddControllersWithViews().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

How can I name my Database using EF Code First?

I've got my EF Code First working exactly as expected aside from one small bit. I'm not sure how to name my Database File.
I'm using SQL CE, but I'm sure this applies to all forms of EF Code First.
Here's my DbContext
namespace MyApp.Domain.EntityFramework
{
public class DataContext : DbContext
{
//...
}
}
And when the database is created it's created as
MyApp.Domain.EntityFramework.DataContext.sdf
I'd prefer to just have it named
MyApp.sdf
Now I'm sure this is simple, but my Googling skills keep turning up examples where the database name is auto generated like mine.
http://www.hanselman.com/blog/SimpleCodeFirstWithEntityFramework4MagicUnicornFeatureCTP4.aspx
You need to specify a connection string (for example by creating a connection string named DataContext (your class name) in your config file, and set the desired name there.
I was looking to do the same. Managed to end up with this:
public class ShopDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Subcategory> Subcategories { get; set; }
public DbSet<Information> OrderInformation { get; set; }
public ShopDbContext() : base("Shop")
{
}
}
It will name your database "Shop" so just replace what is in the base("Shop") with whatever you want to call your database. Hope this helps.

EF 4.1 messing things up. Has FK naming strategy changed?

I've just installed the new Entity Framework 4.1 NuGet package, thus replacing the EFCodeFirst package as per NuGet intructions and this article of Scott Hanselman.
Now, imagine the following model:
public class User
{
[Key]
public string UserName { get; set; }
// whatever
}
public class UserThing
{
public int ID { get; set; }
public virtual User User { get; set; }
// whatever
}
The last EFCodeFirst release generated a foreign key in the UserThing table called UserUserName.
After installing the new release and running I get the following error:
Invalid column name 'User_UserName'
Which of course means that the new release has a different FK naming strategy. This is consistent among all other tables and columns: whatever FK EFCodeFirst named AnyOldForeignKeyID EF 4.1 wants to call AnyOldForeignKey_ID (note the underscore).
I don't mind naming the FK's with an underscore, but in this case it means having to either unnecessarily throw away the database and recreate it or unnecessarily renaming al FK's.
Does any one know why the FK naming convention has changed and whether it can be configured without using the Fluent API?
Unfortunately, one of the things that didn't make it to this release is the ability to add custom conventions in Code First:
http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-release-candidate-available.aspx
If you don't want to use the fluent API to configure the column name (which I don't blame you), then most straight forward way to do it is probably using sp_rename.
Why don't you do the following?
public class User
{
[Key]
public string UserName { get; set; }
// whatever
}
public class UserThing
{
public int ID { get; set; }
public string UserUserName { get; set; }
[ForeignKey("UserUserName")]
public virtual User User { get; set; }
// whatever
}
Or, if you don't want to add the UserUserName property to UserThing, then use the fluent API, like so:
// class User same as in question
// class UserThing same as in question
public class MyContext : DbContext
{
public MyContext()
: base("MyCeDb") { }
public DbSet<User> Users { get; set; }
public DbSet<UserThing> UserThings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserThing>()
.HasOptional(ut => ut.User) // See if HasRequired fits your model better
.WithMany().Map(u => u.MapKey("UserUserName"));
}
}

Resources