Navigation Property Null after query in CTP4 Code First EF4 Feature - c#

I just started playing around with the CTP4 and Code-First. I have the following setup for a possible dating site:
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string LoginName { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
public string Street { get; set; }
[Required]
public string Zip { get; set; }
[Required]
public string City { get; set; }
[Required]
public bool Gender { get; set; }
[Required]
public int SoughtGender { get; set; }
[Required]
public string Password { get; set; }
[Required]
public double Latitude { get; set; }
[Required]
public double Longitude { get; set; }
}
public class Vote
{
[Key]
public int ID { get; set; }
[Required]
public User Voter { get; set; }
[Required]
public User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
public class MySQLContext : DbContext
{
public MySQLContext (string constring)
: base(constring)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Vote> Votes { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<Vote>().HasRequired(b => b.Voter).WithMany();
modelBuilder.Entity<Vote>().HasRequired(b => b.TargetUser).WithMany();
base.OnModelCreating(modelBuilder);
}
}
Now the framework does a nice job of creating the DB with all the proper keys. Now I inserted some dummy data and fired up the following query:
public override IEnumerable<Domain.Vote> FindVotes(Domain.User user)
{
var query = from v in context.Votes where v.Voter.Id == user.Id select v;
return from v in query.AsEnumerable() select v;
}
The query does return the proper Vote entities but the two User properties of the Vote object are Null. Shouldnt the framework populate those properties with the foreign keys of the users referenced in the Vote table?

Let me give you some background on EF so you can understand how this works. EF from day 1 only supported explicit load like one below
Customer.Orders.Load();
Hmm, the feedback was not welcomed by the community and developers wanted lazy loading. To support Lazy Loading EF team said you must mark your navigation property as virtual. So at runtime, Ef creates a proxy object that derives from your entity and overrides the virtual property. Below is an example of such code.
public class Customer
{
public string Name{get;set;}
public virtual ICollection<Order> Orders{get;set;}
}
At runtime there is a proxy that implements IEntityWithChangeTracker and concrete type of the collection is an entitycollection which has been around since version 1.
public class CustomerProxy:Customer,IEntityWithChangeTracker
{
private ICollection<Order> orders;
public override ICollection<Order> Orders
{
if(orders == null)
{
orders = new EntityCollection<Order>();
orders.Load();
}
return orders;
}
}

change your class to the follow
public class Vote {
[Key]
public int ID { get; set; }
[Required]
public virtual User Voter { get; set; }
[Required]
public virtual User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
Notice I've added virtual to the Voter && TargetUser properties and you should be good to go.

Related

Update problem for one to many dependencies in Entity Framework 6

I have some problems to correctly use Entity Framework with one to many relationships.
here is a part of the model :
public class User
{
[Required]
public int UserID { get; set; }
[Required]
public string Login { get; set; }
//one to many relationship
public virtual Group Group { get; set; }
}
public class Group
[Required]
public int GroupID { get; set; }
[Required]
public string Name { get; set; }
public virtual Group ParentGroup { get; set; }
public virtual List<Group> ChildGroups { get; set; }
[Required]
public User CreatedBy { get; set; }
}
public class OtherClass
[Required]
public int OtherClassID { get; set; }
[Required]
public User CreatedBy { get; set; }
}
I have a question about the update an OtherClass entity.
When I get an entity to update, it doesn't have dependencies loaded, I have to add them with an Include like this :
using (var db = new DalContext())
{
var test = db.OtherClasses.Include(o => o.CreatedBy).Single....
}
But if I want to update an OtherClass entity, why all dependencies have to be loaded?
To resume, when I want to update an OtherClass entity, CreatedBy dependency must be completely loaded, with the group, and for the group, the parent group and child groups, with users...
Is there a possibility to add or update an entity with only ID attributes filled?
Thank you for your help.
I'm not an expert, but here is what I would try:
I believe you should have a UserId in your Group and OtherClass classes, like this:
public class User
{
[Required]
public int UserID { get; set; }
[Required]
public string Login { get; set; }
//one to many relationship
public virtual Group Group { get; set; }
}
public class Group
[Required]
public int GroupID { get; set; }
[Required]
public string Name { get; set; }
public virtual Group ParentGroup { get; set; }
public virtual List<Group> ChildGroups { get; set; }
[Required]
public int UserId { get; set; }
//If you want to lazy-load the user when you load the Group entity, use virtual
//Then you don't need the .Include(g=>g.User)
public virtual User CreatedBy { get; set; }
}
public class OtherClass
[Required]
public int OtherClassID { get; set; }
[Required]
public int UserId { get; set; }
public virtual User CreatedBy { get; set; }
}
To update the OtherClass, you update/set the Id properties, not the User property.

EF TPT generating duplicate foreign keys

I am writing an application which uses inheritance and I'm trying to map this to a SQL Server database with TPT structure.
However, for some reason EF generates duplicate foreign keys in both the superclass and subclass tables.
I have these classes:
public abstract class Answer
{
public int AnswerId { get; set; }
[Required]
[MaxLength(250, ErrorMessage = "The answer cannot contain more than 250 characters")]
public String Text { get; set; }
[Required]
[MaxLength(1500, ErrorMessage = "The description cannot contain more than 1500 characters")]
public String Description { get; set; }
public User User { get; set; }
}
public class AgendaAnswer : Answer
{
[Required]
public AgendaModule AgendaModule { get; set; }
}
public class SolutionAnswer : Answer
{
[Required]
public SolutionModule SolutionModule { get; set; }
}
public abstract class Module
{
// for some reason EF doesn't recognize this as primary key
public int ModuleId { get; set; }
[Required]
public String Question { get; set; }
public String Description { get; set; }
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class AgendaModule : Module
{
public IList<AgendaAnswer> AgendaAnswers { get; set; }
}
public class SolutionModule : Module
{
public IList<SolutionAnswer> SolutionAnswers { get; set; }
}
public class User
{
public int UserId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public int Zip { get; set; }
public bool Active { get; set; }
public virtual IList<AgendaAnswer> AgendaAnswers { get; set; }
public virtual IList<SolutionAnswer> SolutionAnswers { get; set; }
}
And this is the content of my DbContext class:
public DbSet<AgendaModule> AgendaModules { get; set; }
public DbSet<SolutionModule> SolutionModules { get; set; }
public DbSet<AgendaAnswer> AgendaAnswers { get; set; }
public DbSet<SolutionAnswer> SolutionAnswers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Module>().HasKey(m => m.ModuleId);
modelBuilder.Entity<Answer>().HasKey(a => a.AnswerId);
modelBuilder.Entity<AgendaAnswer>().Map(m =>
{
m.ToTable("AgendaAnswers");
});
modelBuilder.Entity<SolutionAnswer>().Map(m =>
{
m.ToTable("SolutionAnswers");
});
}
When I run my application, EF creates the tables how I want them (TPT), but it duplicates the foreign key to users in each of them (see picture).
Thanks in advance
As noted in my comment above, the solution is to remove the AgendaAnswers and SolutionAnswers properties from the User class.
If you want to keep those collections in the User class, you might have to remove the User and UserId properties from the Answer class and instead duplicate them in the AgendaAnswer and SolutionAnswer classes. See this SO question for more information.

Entity framework(Code first) - & ASP.net Membership Provider

I am currently trying to build a new application using EF6 Code First, I manage to create the database based on my models no problem.
Now what I want to do is use asp.net membership features. So I will have to have the database schema inside my database.
How can I do this?
There is information around the internet on using the simplemembershipprovider with EF, but it is very confusing.
Just so you have an idea of what I have done so far...(below)
So what is the best way to use membership with EF?
namespace AsoRock.Data.DTOs
{
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string HomeNumber { get; set; }
public string MobileNumber { get; set; }
public string Gender { get; set; }
public DateTime Dob { get; set; }
}
public class Order
{
[Key]
public int OrderId { get; set; }
public Address Address { get; set; }
public Customer Customer { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
}
public class Address
{
[Key]
public int AddressId { get; set; }
public Customer Customer { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string PostCode { get; set; }
public string LastUsed { get; set; }
public bool isDefault { get; set; }
}
public class AsoRockContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Order> Orders { get; set; }
// public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasMany(p => p.Products).WithMany().Map(m =>
{
m.MapLeftKey("OrderId").MapRightKey("ProductId").ToTable("OrderdProducts");
});
base.OnModelCreating(modelBuilder);
}
}
}
If you are using MVC 5 then you can change your
public class AsoRockContext : DbContext
to be
public class AsoRockContext : IdentityDbContext<ApplicationUser>
Then create your ApplicationUser
public class ApplicationUser : IdentityUser
{ //You can add extra properties in if you want
public string FirstName { get; set; }
public string LastName { get; set; }
//Or add a link to your customer table
public Customer CustomerDetails { get; set; }
}
Asp.net MVC 4+ comes with asp.net membership provider.
So you context name is TestConnection then
in the AccountModel in Model Directory, Change the defaultConnection to TestConnection
like this, then it will create membership table in your DB.
public UsersContext()
: base("TestConnection")
{
}
to know more about how it is achieve see the code, see the AccountController file and find the following in top
[InitializeSimpleMembership]
public class AccountController : Controller
Inspect the [InitializeSimpleMembership] attribute to learn how it is implemented.

Create one to one relation using code first approch in entitiy framework

I have below two classes and i wanted to make a one to one relation between these two entities using Code First approch. Anybody can suggest/help me on how to create ONE to ONE relation between these Patient & Address entities ? Thanks in advance...
public partial class Patient
{
public Patient()
{
this.Clinicals = new List<Clinical>();
}
public int PatientId { get; set; }
public string MaritalStatus { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual ICollection<Clinical> Clinicals { get; set; }
[Required]
public virtual Address Address { get; set; }
}
public partial class Address
{
public int AddressId { get; set; }
[ForeignKey("Patient")]
public int PatientId { get; set; }
public string AddressLine1 { get; set; }
public string State { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual Patient Patient { get; set; }
}
If you run your application Entity Framework will create one-to-one relationship automatically for you.Because you have added necessary properties for this. Anyway, if you want to create it manually you can use Fluent API:
modelBuilder.Entity<Patient>()
.HasRequired(a => a.Address)
.WithRequiredPrincipal(p => p.Patient);

Relationships in Entity Framework Code First

yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}

Categories

Resources