I am still starting with Entity Framework Code First. I want to be able to select a Resource from list when creating a new resource. How do I reference a Resource with a Resource model.
public class Resource
{
public int ResourceId { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[EmailAddress]
public string EmailAddress { get; set; }
[Required]
public string Password { get; set; }
public string FullName { get; set; }
public int TimeManagerId { get; set; }
public int TravelManagerId { get; set; }
public int OvertimeManagerId { get; set; }
public int AbsenceManagerId { get; set; }
public virtual Resource TimeManager { get; set; }
public virtual Resource TravelManager { get; set; }
public virtual Resource OvertimeManager { get; set; }
public virtual Resource AbsenceManager { get; set; }
}
I think you're pretty close! If you want to do this by convention, you can change the foreign keys in your model to the form of [navigation property name][principal primary key property name]. Specifically, change Id to ResourceId so it matches the primary of the table you're referencing (which happens to be itself)...
public int TimeManagerResourceId { get; set; }
public int TravelManagerResourceId { get; set; }
public int OvertimeManagerResourceId { get; set; }
public int AbsenceManagerResourceId { get; set; }
Since you're just starting with EF code first, I'd recommend you install the Entity Framework Power Tools. You'll be able to right-click on the .cs file containing your DbContext, and it'll generate a read-only diagram of the mappings.
Try it out with your current model... right-click on entity in the diagram and view Table Mappings. You'll see EF wasn't able to figure out your foreign keys and created 4 more for you. Once you make the changes above, generate the diagram again and see the difference.
Edit: Docs on code first conventions... http://msdn.microsoft.com/en-us/data/jj679962.aspx
Related
I am rewriting an old .NET Core/EF Core 2 web app in .NET / EF Core 7.
I have the following two entities which, as far as I remember, worked fine in the old app but now I get an error
Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_ReleaseDates_Sections_SectionID' on table 'ReleaseDates' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
I am not sure if I have always been doing something wrong in the entities and fixed it somewhere else I can't see it or if something has changed over the years and I am now doing it completely wrong.
public class Section
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Title { get; set; }
[Column(TypeName = "varchar(15)")]
public string Abbreviation { get; set; }
public int TypeID { get; set; }
public int? LogoFileID { get; set; }
public string Synopsis { get; set; }
public ICollection<ReleaseDate> ReleaseDates { get; set; }
[ForeignKey("LogoFileID")]
public SiteFile LogoFile { get; set; }
[ForeignKey("TypeID")]
public SectionType Type { get; set; }
}
public class SectionType
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
}
public class ReleaseDate
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public int VersionNumber { get; set; }
public int TypeID { get; set; }
public DateTime? Released { get; set; }
public string Platform { get; set; }
public string Region { get; set; }
public string Note { get; set; }
public int SectionID { get; set; }
[ForeignKey("SectionID")]
public Section Section { get; set; }
[ForeignKey("TypeID")]
public SectionType Type { get; set; }
}
Am I supposed to be fixing this via FluentAPI's Cascade command or is there something I am screwing up in the entities that I should be doing?
Note : This model don't work in .NET Core 2.1 and EF Core 2.1 with SQL Server. I tested and reproduced the same error.
The model has multiple cascade path :
Section -> ReleaseDate
Section -> SectionType -> ReleaseDate
And SQL Sever don't allow this. To understand, see this data :
SectionA -> ReleaseDateA
-> SectionTypeA -> ReleaseDateA
When the SectionA is removed, by cascading this remove also ReleaseDateA and SectionTypeA . And remove SectionTypeA, by cascading this remove again ReleaseDateA... ReleaseDateA is removed two times.
But this seems legit, just need to ReleaseDateA one time. Other DBMS like MySQL manage this. Then why not SQL Server?
I don't know and found no official information about this. Just found this :
SQL Server - Why can't we have multiple cascade paths?
...so instead of fixing it, the implementation avoids it by preventing the definition of duplicate cascade paths. It's clearly a short-cut...
SQL Server has this constraint and we need to live with. A solution is to avoid multiple cascade path. In your case, you can :
public class ReleaseDate
{
...
public int? TypeID { get; set; }
[ForeignKey("TypeID")]
public SectionType? Type { get; set; }
}
I've revisited my web site recently and had to upgrade from ASP.net MVC (DBF) core 2.0 to 2.1.
Since doing so I'm getting the following error...
SqlException: Invalid column name 'MovieTitleId'. Invalid column name 'MovieTitleId'.
Yet there is no such field 'MovieTitleId' in any part of my code or db.
The error occurs only when the site is accessing the 'many table' Scenes
(there is a one-to-many relationship set up in the db with FKs.. Movie > Scenes)
This is the Scene class..
public partial class Scene
{
[Key]
public int SceneId { get; set; }
[ForeignKey("TitleId")]
public int? TitleId { get; set; } // foreign key from Movie
[ForeignKey("LocationSiteId")]
public int? LocationSiteId { get; set; }
[ForeignKey("LocationAliasId")]
public int? LocationAliasId { get; set; }
public string Notes { get; set; }
public int? SceneOrder { get; set; }
public string TitleList { get; set; }
public LocationAlias LocationAlias { get; set; }
public LocationSite LocationSite { get; set; }
public Movie Movie { get; set; }
}
And this is the Movie class which on the 'one side' and call Scenes on a typical 'Master/Detail' type web page...
public partial class Movie
{
public Movie()
{
Scenes = new HashSet<Scene>();
}
[Key]
public int TitleId { get; set; }
[Required]
public string Title { get; set; }
[DisplayName("Title")]
public string ParsedTitle { get; set; }
[DisplayName("Year")]
public int? TitleYear { get; set; }
public string ImdbUrl { get; set; }
public string Summary { get; set; }
public bool? ExcludeTitle { get; set; }
public bool? Widescreen { get; set; }
[DisplayName("Title")]
public override string ToString()
{
return Title + " (" + TitleYear + ")";
}
public ICollection<Scene> Scenes { get; set; }
}
The error occurs in the MoviesController.cs...
Movie movie = _context.Movies.Find(id);
ViewBag.Scenes = _context.Scenes
.Where(s => s.TitleId == id)
.Include(s => s.LocationSite)
.Include(s => s.LocationSite.LocationPlace)
.OrderBy(s => s.SceneOrder).ToList();
Everything used to work fine until i upgraded to core 2.1.
I can't even recall there ever being a field called 'MovietitleId' which is actually 'TitleId'.
Is the error msg concatenating the model 'Movie' and column 'TitleId' somehow?
Try adding virtual keyword for your foreign key. Also the ForeignKey Data Annotation should be on that property where you have declared your virtual property just like below. So it should be something like this:
Scene.cs
public int TitleId { get; set; }
[ForeignKey("TitleId")
public virtual Movie Movie { get; set; }
Why virtual?
If you declare your property virtual your virtual property (by default) won't be loaded right away when querying the main object. It will be retrieved from the database ONLY if you try to access it. This is called lazy loading.
If you want to know why to use virtual in detail, you may visit this link: Why are foreign keys in EF Code First marked as virtual?
Hope this helps.
I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.
I have an 'User'class contains two 'Address' properties reference to 'Address' entity, and there is another class - 'Shipment' also associate with 'Address'.
How i can use fluent api on ef core to build a correct relation between entities.
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
...
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual Address DefaultAddress { get; set; }
public virtual Address BillingAddress { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public virtual Address DeliveryAddress { get; set; }
}
There is not need to declare explicit configuration, the EF will do everything without any help.
I prepared a working example with and without fluent configuration, you can check it out here. Just switch between commits to see the difference.
As you can notice, there is no differences in generated migration.
i was trying to run the Update-Database command in Nugget Package Manager console but wasnt successful as i kept getting the error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' 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..
I want to set up relationship in which there is a class called Team.cs that contains the below properties
public class Team
{
public int TeamId { get; set; }
public string TeamName { get; set; }
public Decimal MonthlyTarget { get; set; }
public ICollection<SalesAgent> Agents { get; set; }
}
which means a team has many Agents and there is another class called SalesAgent.cs which contain info about agents
public class SalesAgent
{
[Key]
public int AgentId { get; set; }
public string AgentFirstName { get; set; }
public string AgentLastName { get; set; }
public string HomeAddress { get; set; }
public bool IsActive { get; set; }
public string AgentPhone { get; set; }
public Decimal MonthlyTarget { get; set; }
public int TeamId { get; set; }
public virtual Team Team { get; set; }
}
Now i want a class which i would be able add the relationship between a team and an agent i.e in essence i want to be able to assign a team leader to each team so i set up the class below
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
public int AgentId { get; set; }
public virtual Team Team { get; set; }
[ForeignKey("AgentId")]
public virtual SalesAgent Agent { get; set; }
}
Upon running "Update-Database Command" I get an error that The ForeignKeyAttribute on property 'AgentId' on type 'SalesForce.Models.TeamToLeader' is not valid. The navigation property 'SalesAgent' was not found on the dependent type 'SalesForce.Models.TeamToLeader'. The Name value should be a valid navigation property name.
So i changed the model to
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
and that resulted in this error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' 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.
Help please.
You should diasble OneToManyCascadeDeleteConvention to force EF not to use cascade delete. In DbContext add:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
...
Or you can make foreign keys nullable:
public class TeamToLeader
{
[Key]
public int? TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int? TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
Depends which behavior you prefer.
You can also use fluent API:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TeamToLeader>().HasRequired(i => i.Agent).WithMany().WillCascadeOnDelete(false);
}
...
Note that your model Team has many SalesAgent and many TeamToLeader.
There should be TeamToLeaders collection in your Team and SalesAgent model :
...
public virtual ICollection<TeamToLeader> TeamToLeaders { get; set; }
...
I'm not sure if you need Team to many SalesAgent relation anymore.
As this link, and this link saids...
It is theoretically correct but SQL server (not Entity framework) doesn't like it because your model allows single employee to be a member of both First and Second team. If the Team is deleted this will cause multiple delete paths to the same Employee entity.
SQL server doesn't allow multiple delete paths to the same entity.
This link said that it can be solved by disabling OneToManyCascadeDeleteConvention and ManyToManyCascadeDeleteConvention, but those deleting operations SHOULD BE done by codes manually.