I have some model created, which is supposed to store GUIDs (strings) as identifiers and I want the class that has a foreign key with it to also have the object to be accessed. I remembered while using LinqToSql was pretty simple, but now I'm running into some troubles with EF.
I have tried putting the object as virtual but I think I have to do something on the DbContext, because when I try to create the migration it fails due to recursion limit (I guess this is due to making sort of circular references).
For example, I have the following classes:
public class Calculation
{
[Required]
public Guid Id { get; set; }
[Required]
public Guid UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
[Required]
public string Title { get; set; }
[Required]
public Guid CausativeId { get; set; }
[ForeignKey("CausativeId")]
public virtual Causative Causative { get; set; }
[Required]
public bool Active { get; set; }
}
public class Causative
{
[Required]
public Guid Id { get; set; }
[Required]
public string Document { get; set; }
public virtual Calculation Calculation { get; set; }
}
The idea is that the Calculation knows who is it's Causative, but I also want to be able to do it the other way around (this was something pretty useful on LinqToSql), if this is not possible, just automatically filling the Causative property with the corresponding data without the need of explicitly making another call to the database.
Should I do something in the DbContext instance? I have this in place:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Calculation>().HasOne(calculation => calculation.Causative).WithOne(causative => causative.Calculation);
}
I'm sure I'm doing something wrong, but I'm not sure what.
Thanks in advice!
For that model you created, you should change this one for OnModelCreating:
modelBuilder.Entity<Causative>()
.HasOne(p => p.Calculation)
.WithOne(i => i.Causative)
.HasForeignKey<Calculation>(b => b.CausativeId);
and this is for one to many if you have IEnumerable<Calculation> in Causative:
modelBuilder.Entity<Calculation>()
.HasOne(p => p.Causative)
.WithMany(b => b.Calculation);
I think that you just need to replace this line:
public virtual Calculation Calculation { get; set; }
with this:
public virtual List<Calculation> Calculations { get; set; }
or this:
public virtual ICollection<Calculation> Calculations { get; set; }
this will create for you the right navigation property and FK
Related
I'm missing something when using the data annotations.
This is my first class
[Table("PriceFeed")]
public class PriceFeed : History
{
public PriceFeed()
{
this.Votes = new List<PriceVote>();
this.History = new List<PriceFeed__History>();
}
[Key]
public long Id { get; set; }
[ForeignKey("Store")]
public long Store_Id { get; set; }
[ForeignKey("Item")]
public long Item_Id { get; set; }
[Required]
public decimal Price { get; set; }
public Store Store { get; set; }
public Item Item { get; set; }
public virtual ICollection<PriceFeed__History> History { get; set; }
}
And this is my second class
[Table("PriceFeed__History")]
public class PriceFeed__History : History
{
[Key]
public long Id { get; set; }
[ForeignKey("PriceFeed")]
public long PriceFeed_Id { get; set; }
[Required]
public decimal Price { get; set; }
public virtual PriceFeed PriceFeed { get; set; }
}
When I run the add-migration, it creates the database correctly but when I try to access PriceFeed.History it gives me an error
{"Message":"An error has occurred.","ExceptionMessage":"A specified Include path is not valid. The EntityType 'Verdinhas.Web.Contexts.PriceFeed' does not declare a navigation property with the name 'PriceFeed__History'."
I always worked with API Fluent and typed by myself the code like
.Entity<Student>()
.HasRequired<Standard>(s => s.Standard)
.WithMany(s => s.Students)
.HasForeignKey(s => s.StdId);
But now I'm using the data annotations and when I generate the migration, it does not create the "withmany" like the above.
What am I doing wrong?
The issue has nothing to do with Data Annotations which seems to be correct in your model.
As mentioned in the comments, the exception is caused by a code that tries to use Include method with string "'PriceFeed__History" - you seem to think that you should specify the related entity types, but in fact you need to specify the navigation property names, which in your case is "History".
Courses have many prerequisites, and simultaneously a particular course can be a prerequisite for many courses. I've tried to establish the many-to-many relationship (in OnModelBCreating) using EF code-first with the following:
modelBuilder.Entity<Course>()
.HasMany(e => e.Prerequisites)
.WithMany(e => e.Postrequisites)
.Map(m => m.ToTable("CourseRequisiteMappings")
.MapLeftKey("CourseId").MapRightKey("CourseId")); // EDIT: THIS LINE IS THE PROBLEM. SEE MARKED ANSWER AND MY COMMENT ON IT.
Also, here is the Course class:
public class Course
{
public int CourseId { get; set; }
public string Name { get; set; }
public string InstitutionCode { get; set; }
public string Description { get; set; }
public bool IsElective { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Module> Modules { get; set; }
public virtual ICollection<Course> Prerequisites { get; set; }
public virtual ICollection<Course> Postrequisites { get; set; }
}
When I implemented this and went to update the database, it gave me the following errors:
CourseId: Name: Each property name in a type must be unique. Property
name 'CourseId' is already defined.
ModuleId: Name: Each property name in a type must be unique. Property
name 'ModuleId' is already defined.
CourseCourse: EntityType: EntitySet 'CourseCourse' is based on type
'CourseCourse' that has no keys defined.
ModuleModule: EntityType: EntitySet 'ModuleModule' is based on type
'ModuleModule' that has no keys defined.
I could not find an example of doing this which leads me to believe one of the following three are true:
There's a different way of accomplishing this that I don't see
I'm on the right track but overlooking something due to my lack of knowledge with EF
I'm the first one to try and EF doesn't support this (very unlikely)
First, does anyone know how I can set up this relationship, i.e., what do these errors mean (responding to #2)? For bonus points, is there another way of doing this that might be better or worse (kinda #1)? Thanks in advance.
Your mapping is nearly correct. But you have to understand that under the hood the Entity Framework wil create a so callled junction table that stores the many to many relationship.
This junction table wil just have two fields, containing the foreign keys which together make up the primary key. Obviously these foreign keys cannot have the same name.EF is smart enough to all figure it out by itself and no maping is necessary. Below a working example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
namespace ManyToManyUnderTheHoodSpike
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<CourseContext>());
using (CourseContext context=new CourseContext())
{
context.Courses.Add(new Course("Top of the bill")
{
PrerequisiteCourses = new List<Course>()
{
new Course("My two cents"),
new Course("Counting to two")
}
});
context.SaveChanges();
}
}
}
public class CourseContext : DbContext
{
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class Course
{
public Course() { }
public Course(string name)
{
Name = name;
}
public string Name {get;set;}
public int CourseId{get;set;}
public ICollection<Course> PrerequisiteCourses{get;set;}
public ICollection<Course> FollowUpCourses{get;set;}
}
}
If you run this code you get a database with two tables: Courses and CourseCourses with as the only fields Course_Id and Course_Id1.
But that is not very readable, so let's make the mapping to make it more readable:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Course>().HasMany(course => course.PrerequisiteCourses)
.WithMany(course => course.FollowUpCourses)
.Map(data => data.ToTable("Prerequisites")
.MapLeftKey("FollowUpId")
.MapRightKey("PrerequisiteId"));
}
Presto!
I would model like this. I know you wanted only 1 table. But Ef will create the many to many table if you dont. Not sure what you didnt get right without testing. So anyway, here is another option.
public class Course
{
public int CourseId { get; set; }
public string Name { get; set; }
public string InstitutionCode { get; set; }
public string Description { get; set; }
public bool IsElective { get; set; }
//nav elements
public virtual ICollection<Instructor> Instructors { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Module> Modules { get; set; }
public virtual ICollection<PreReqCourse> Prerequisites { get; set; }
// You can Find follow on courses, by accessing PreReqCourse table, but if you felt this navigation offered enough value, create a post req table too. Using same approach.
// public virtual ICollection<Course> Postrequisites { get; set; }
}
public class PreReqCourse
{
public virtual int Id {get; set;}
public virtual int CourseId { get; set; }
public virtual Course PreReqForCourse { get; set; } //Nav prop
}
modelBuilder.Entity<Course>()
.HasMany(e => e.Prerequisites)
.WithMany();
// Leave WithMany empty. You can define in PreReqCourse Table model, you dont need to model from both directions.
modelBuilder.Entity<PreReqCourse>()
.HasRequired(e => e.PreReqForCourse)
.HasForeignKey(f => f.CourseId)
.WithMany(p=>p.PreRequisites);
I need help creating the relationship in entity framework as everything I have tried gives me errors when trying to add the migration or if I get passed that then I try to update the database and get an error about indexes with the same name.
public class Profile
{
public Profile()
{
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string VersionCreated { get; set; }
public string DiskLocation { get; set; }
public string Name { get; set; }
public DateTime DateTime { get; set; }
public virtual Product Product { get; set; }
public virtual Instance OriginalInstance { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
public class Instance
{
public Instance()
{
TestResults = new HashSet<TestResult>();
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string UserFriendlyName { get; set; }
public virtual Product Product { get; set; }
public virtual Profile LastKnownProfile { get; set; }
public virtual Computer Computer { get; set; }
public virtual ICollection<TestResult> TestResults { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
The problem with the above classes is that the OrginalInstance property on the Profile class and the LastKnownProfile in the Instance class are supposed to just be foreign keys to those specific tables and they probably won't be the same very often. They can also both possibly be null.
I have tried:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile);
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance);
This gave me an Unable to determine the principal end of an association between the types 'EcuWeb.Data.Entities.Instance' and 'EcuWeb.Data.Entities.Profile'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. error.
and with:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile).WithOptional();
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance).WithOptional();
The database adds a foreign key reference back to itself.
...that the OrginalInstance property on the Profile class and the
LastKnownProfile in the Instance class are supposed to just be foreign
keys to those specific tables and they probably won't be the same very
often. They can also both possibly be null.
In this case you actually want two one-to-many relationships between Profile and Instance if I don't misunderstand your quote above. It would mean that many Profiles can have the same OriginalInstance and that many Instances can have the same LastKnownProfile. The correct mapping would look like this then:
modelBuilder.Entity<Profile>()
.HasOptional(p => p.OriginalInstance)
.WithMany()
.Map(m => m.MapKey("OriginalInstanceId"));
modelBuilder.Entity<Instance>()
.HasOptional(i => i.LastKnownProfile)
.WithMany()
.Map(m => m.MapKey("LastKnownProfileId"));
The lines with MapKey are optional. Without them EF will create a foreign key with a default name.
Also note that you must use HasOptional (instead of HasRequired) if "both can possibly be null".
This is my first day I've spent exploring ASP.NET MVC 4. Specifically I'm using the Web API and obviously this issue is actually an MS SQL issue. I'm running EF migrations PM> Update-Database to get this error, but have seen it when first creating the models. My models are:
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int MakeId { get; set; }
public virtual Make Make { get; set; }
public int ModelId { get; set; }
public virtual Model Model { get; set; }
}
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Model> Models { get; set; }
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public int MakeId { get; set; }
public virtual Make Make { get; set; }
}
The DB context is:
public class CarsContext : DbContext
{
public CarsContext() : base("name=CarsContext") { }
public DbSet<Car> Cars { get; set; }
public DbSet<Make> Makes { get; set; }
public DbSet<Model> Models { get; set; }
}
}
Would appreciate any help. My background is 5/6 solid of PHP and MySQL, so this is a steep learning curve.
Thanks.
Luke McGregor is correct. In addition to the way you fixed this you can override the default mapping that entity framework is giving you so that it doesn't cascade delete. In you CarsContext class you can override the OnModelCreating() method and specify your own mappings using fluent. This overrides what EF is trying to do by default. So you can do something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasOptional(x => x.Model)
.WithMany(y => y.Cars) //Add this property to model to make mapping work
.WillCascadeOnDelete(false);
}
This will still work with automatic migrations.
Hope that helps.
I'm trying to map the results of a stored procedure into a model that contains another model several times. I'm not sure if this is possible, but wanted to ask and see if this is a valid approach, or if it would be better to define everything out individually.
My model classes look like this:
public class ClientSummary {
public virtual int Id { get; set; }
public virtual int Client_ID { get; set; }
public virtual string Client_Name { get; set; }
public virtual string State { get; set; }
public virtual EDITotal Totals { get; set; }
public virtual EDITotal In_Totals { get; set; }
public virtual EDITotal Out_Totals { get; set; }
public virtual EDITotal Direct_Totals { get; set; }
public virtual EDITotal CPN_Totals { get; set; }
}
public class EDITotal {
public virtual int Count { get; set; }
public virtual double Charges { get; set; }
public virtual double StateSavings { get; set; }
public virtual double PPOSavings { get; set; }
public virtual float PPO_Pct_Bill { get; set; }
public virtual float PPO_Pct_State { get; set; }
public virtual int Unique_TaxIds { get; set; }
}
What I'm not sure of is what my class map would look like in Fluent NHibernate, or if something like this is possible. I'm mainly trying to keep things tidy, and reusable. I'm sure I'll have other reports that will use similar metrics. Am I going about this the right way, or do I really need to define a full model for each report I build? This is all taking place inside a MVC Web application.
Thanks in advance!
Yes this is indeed possible and right or wrong you can get it working with the way you have your classes setup. To do this, you can use a mapper like this:
public class ClientSummaryMappingOverride : IAutoMappingOverride<ClientSummary>
{
public void Override(AutoMapping<ClientSummary> mapping)
{
mapping.References(x => x.Totals, "Totals_id");
mapping.References(x => x.In_Totals, "In_Totals_id");
mapping.References(x => x.Out_Totals, "Out_Totals_id");
mapping.References(x => x.Direct_Totals, "Direct_Totals_id");
mapping.References(x => x.CPN_Totals, "CPN_Totals_id");
}
}
Once you have your map configured, you just need to make sure your Stored Procedure or SQL Query returns the ClientSummary records with the appropriate "Totals_id" type fields. NHibernate will pick up those ids and map them to the correct data (Lazy Load I believe, depending on your conventions or other mappings).