I have a following code:
public abstract class Entity
{
public virtual int Id { get; set; }
}
public class Category : Entity
{
public string Name { get; set; }
public virtual ICollection<Category> Children { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item : Entity
{
public string Name { get; set; }
public decimal Price { get; set; }
public DateTime DateCreated { get; set; }
public virtual Category Category { get; set; }
}
This syntax
modelBuilder.Entity<Category>().Property(x => x.Children).HasColumnName("CategoryID");
gives me Severity Code Description Project File Line
Error CS0453 The type 'ICollection' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'StructuralTypeConfiguration.Property(Expression>)'
Q: How I can change database column name via Fluent API?
The collection itself can't be mapped to one column. You should map this as a one-to-many association:
modelBuilder.Entity<Category>()
.HasMany(c => c.Children)
.WithOptional()
.Map(m => m.MapKey("CategoryID"));
Related
I have a POCO Entity named Employee.
And then I have a second POCO Entity named Case.
I want a navigation property that looks like instead this:
public class Case : BaseEntity
{
public long EmployeeId { get; set; }
public virtual Employee Employee{ get; set; }
like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
public virtual Employee InitialContact { get; set; }
I want to name my property InitialContact. Not Employee.
But I get this error when EF tries to create the Database:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 1:
I got it to work like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
The primary key is ID in my BaseEntity. Not EmployeeId.
But I have second part to my question.
Here is my Complete Employee POCO:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Hrsa.Core.Generic.Model.Framework.Concrete;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Hrsa.Core.Generic.Model.Lerd
{
public class Employee : BaseEntity
{
[BindNever]
public string Email { get; set; }
[BindNever]
public long OrganizationId { get; set; }
[BindNever]
public string Supervisor { get; set; }
[BindNever]
public string SupervisorEmail { get; set; }
[BindNever]
public string FirstName { get; set; }
[BindNever]
public string LastName { get; set; }
public string Notes { get; set; }
[BindNever]
public long BargainingUnitId { get; set; }
[BindNever]
public long PayPlanId { get; set; }
[BindNever]
public long GradeRankId { get; set; }
[BindNever]
public long PositionTitleId { get; set; }
[BindNever]
public long SeriesId { get; set; }
public bool IsUnionEmployee { get; set; }
public virtual Organization Organization { get; set; }
public virtual BargainingUnit BargainingUnit { get; set; }
public virtual PayPlan PayPlan { get; set; }
public virtual GradeRank GradeRank { get; set; }
public virtual PositionTitle PositionTitle { get; set; }
public virtual Series Series { get; set; }
public virtual ICollection<UnionHours> UnionHours { get; set; }
public virtual ICollection<Case> Cases { get; set; }
[NotMapped]
public string UnionEmployeeYesNo => (IsUnionEmployee) ? "Yes" : "No";
}
}
I want my Employee to have many Cases:
public virtual ICollection<Case> Cases { get; set; }
Here is my complete Cases POCO:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
So now my DB looks like this:
So I have my InitialContactId in Cases ok.
But now I need my Case to have many Employees.
So I add this in to my Case POCO:
public virtual ICollection<Employee> Employees { get; set; }
Now it looks like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Now when I run it, I get this error again:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 2:
I found this article for a Many-Many relationship in .Net Core 1:
http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
So now I have a bridge lookup entity:
public class EmployeeCase
{
[ForeignKey("Id")]
public long EmployeeId { get; set; }
public Employee Employee { get; set; }
[ForeignKey("Id")]
public long CaseId { get; set; }
public Case Case { get; set; }
}
Employee POCO:
Changed:
public virtual ICollection<Case> Cases { get; set; }
to:
// Mapping - Collection of Cases
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
Case POCO:
Changed:
public virtual ICollection<Employee> Employees { get; set; }
to:
// Mapping - Collection of Employees
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
In my AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
}
Now when I run I get this error:
An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code
Additional information: Introducing FOREIGN KEY constraint 'FK_EmployeeCase_Employees_EmployeeId' on table 'EmployeeCase' 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 or index. See previous errors.
Update 3:
Finally got my tables the way I want with this piece of code from:
Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Get rid of Cascading Circular error on ModelBuilding
foreach (var relationShip in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationShip.DeleteBehavior = DeleteBehavior.Restrict;
}
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
base.OnModelCreating(modelBuilder);
}
Update 4:
This did not work after all.
Remvoving the delete behavior for everything messes up my other relationships and I get errors.
How can I fix this?
This is disgusting.
So wishing I did not go Core.
Entity Framework uses conventions to guess how to map your C# model to database objects.
In your case you violate convention by custom name, so you should explain Entity Framework how to map this stuff.
There are two possible ways: attributes and fluent API. I'd suggest to use the latter one.
See section "Configuring a Foreign Key Name That Does Not Follow the Code First Convention" here: Entity Framework Fluent API - Relationships
I have made it a habit of explicitly defining my relationships as EF does not always get them the way I want. I like to create a Mapping folder that contains my entity maps. The fluent api works great for this and inherits from EntityTypeConfiguration.
Try this.
public class CaseMap : EntityTypeConfiguration<Case>
{
public CaseMap()
{
HasKey(m => m.Id)
HasRequired(m => m.InitialContact)
.WithMany(e => e.Cases)
.HasForeignKey(m => m.InitialContactId);
}
}
Almost forgot. You need to tell your DbContext where to find these mappings. Add this to your DbContexts OnModelCreating method.
modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
This is what worked finally for the Cascading Delete circular references on the many-to-many in EF Core:
// Get rid of Cascading Delete Circular references error.
var type = modelBuilder.Model.GetEntityTypes().Single(t => t.Name == "Hrsa.Core.Generic.Model.Lerd.EmployeeCase");
foreach (var relationship in type.GetForeignKeys())
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
You have to get the Entity representing the many to many lookup only.
And from there restrict the DeleteBehavior.
I have implemented following generic model as base model and two models derived from it and i need one to many relationship. I have used fluent api to create the relationships but i get the error mentioned in the title.
Would you please see if you find any thing wrong in my code.
public abstract class ModelBase<T> : BaseEntity, IModelBase<T>
{
public virtual T Id { get; set; }
}
[Table("VehicleModel", Schema = "Tracker")]
public class VehicleModelModel : ModelBase<int>
{
public string Company { get; set; }
public string ModelName { get; set; }
public byte LiPerKM { get; set; }
public virtual ICollection<VehicleModel> Vehicles { get; set; }
[Table("Vehicle", Schema = "Tracker")]
public class VehicleModel : ModelBase<Guid>
{
public string VehicleName { get; set; }
public string LicensePlate { get; set; }
public int VehicleModelId { get; set; }
public virtual VehicleModelModel Model { get; set; }
public virtual TrackerModel Tracker { get; set; }
[Required]
public virtual DriverModel Driver { get; set; }
modelBuilder.Entity<VehicleModel>()
.HasRequired<VehicleModelModel>(s => s.Model)
.WithMany(s => s.Vehicles)
.HasForeignKey(s => s.VehicleModelId);
Here I get this error:
entity types 'VehicleModelModel' and 'VehicleModel' cannot share table 'VehicleModels' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.
I'm new to MVC and EF code first. I'm in struggle to model a real-estate company DB model using EF code-first approach and I did some exercises as well as reading some online tutorials.
First thing I have a customers table that would be in relation with one or more properties he/she has registered as it's owner to sell or to rent, I was wondering if it is possible to have some sub classes inside a model class for registered properties as below:
public Property
{
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public class Apartment{
public int ApartmentID { get; set; }
public int AptSqureMeter { get; set; }
. . .
. . .
}
public class Villa{
public int VillaID { get; set; }
public int VillaSqureMeter { get; set; }
. . .
. . .
}
and also other sub-classes for other types of properties
}
If the answer is Yes, then how should I declare the relations using data annotation or Fluent API, and then please help me how to update both Customers table and Property table with the customer information and property info at the same time?
thanks for your answer in advance.
As #Esteban already provided you with a pretty detailed answer on how to design your POCOs and manage the relationship between them, I will only focus on that part of your question:
how should I declare the relations using data annotation or Fluent API
First of all, you should know that certain model configurations can only be done using the fluent API, here's a non exhaustive list:
The precision of a DateTime property
The precision and scale of numeric properties
A String or Binary property as fixed-length
A String property as non-unicode
The on-delete behavior of relationships
Advanced mapping strategies
That said, I'm not telling you to use Fluent API instead of Data Annotation :-)
As you seem to work on an MVC application, you should keep in mind that Data Annotation attributes will be understood and processed by both by Entity Framework and by MVC for validation purposes. But MVC won't understand the Fluent API configuration!
Both your Villa and Apartment classes have similar properties, if they are the same but as it's type, you could create an enum for that.
public enum PropertyType {
Apartment = 1,
Villa
}
public class Property {
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public PropertyType PropertyType { get; set; }
public int SquareMeter { get; set; }
}
This way of modelating objects is refered as plain old clr object or POCO for short.
Assume this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role {
public int RoleId { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
Creating relations with fluent api:
Mapping many to many
On your OnModelCreating method (you'll get this virtual method when deriving from DbContext):
protected override void OnModelCreating(DbModelBuilder builder) {
// Map models/table
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
// Map properties/columns
builder.Entity<User>().Property(q => q.UserId).HasColumnName("UserId");
builder.Entity<User>().Property(q => q.Username).HasColumnName("Username");
builder.Entity<Role>().Property(q => q.RoleId).HasColumnName("RoleId");
builder.Entity<Role>().Property(q => q.Name).HasColumnName("Name");
// Map primary keys
builder.Entity<User>().HasKey(q => q.UserId);
builder.Entity<Role>().HasKey(q => q.RoleId);
// Map foreign keys/navigation properties
// in this case is a many to many relationship
modelBuilder.Entity<User>()
.HasMany(q => q.Roles)
.WithMany(q => q.Users)
.Map(
q => {
q.ToTable("UserRoles");
q.MapLeftKey("UserId");
q.MapRightKey("RoleId");
});
Mapping different types of relationships with fluent api:
One to zero or one:
Given this model:
public class MenuItem {
public int MenuItemId { get; set; }
public string Name { get; set; }
public int? ParentMenuItemId { get; set; }
public MenuItem ParentMenuItem { get; set; }
}
And you want to express this relationship, you could do this inside your OnModelCreating method:
builder.Entity<MenuItem>()
.HasOptional(q => q.ParentMenuItem)
.WithMany()
.HasForeignKey(q => q.ParentMenuItemId);
One to many
Given this model:
public class Country {
public int CountryId { get; set; }
public string Name { get; set; }
public virtual List<Province> Provinces { get; set; }
}
public class Province {
public int ProvinceId { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
You now might want to express this almost obvious relationship. You could to as follows:
builder.Entity<Province>()
.HasRequired(q => q.Country)
.WithMany(q => q.Provinces)
.HasForeignKey(q => q.CountryId);
Here are two useful links from MSDN for further info:
Configuring Relationships with the Fluent API.
Code First Relationships Fluent API.
EDIT:
I forgot to mention how to create a many to many relationship with additional properties, in this case EF will NOT handle the creation of the join table.
Given this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
pubilc virtual List<UserEmail> UserEmails { get; set; }
}
pubilc class Email {
public int EmailId { get; set; }
public string Address { get; set; }
public List<UserEmail> UserEmails { get; set; }
}
public class UserEmail {
public int UserId { get; set; }
public int EmailId { get; set; }
public bool IsPrimary { get; set; }
public User User { get; set; }
public Email Email { get; set; }
}
Now that we've added a new property into our join table ef will not handle this new table.
We can achieve this using the fluent api in this case:
builder.Entity<UserEmail>()
.HasKey( q => new {
q.UserId, q.EmailId
});
builder.Entity<UserEmail>()
.HasRequired(q => q.User)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.EmailId);
builder.Entity<UserEmail>()
.HasRequired(q => q.Email)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.UserId);
I have these two entities:
public partial class Suscriptores
{
public Suscriptores()
{
this.Publicacion = new HashSet<Publicacion>();
}
[Key]
public int IdSuscriptor { get; set; }
public string LogoSuscriptor { get; set; }
public string Identificacion { get; set; }
public string Nombre { get; set; }
public string Direccion { get; set; }
public string Telefono { get; set; }
public string Email { get; set; }
public string Fax { get; set; }
public string Home { get; set; }
public virtual ICollection<Publicacion> Publicacion { get; set; }
}
public partial class Publicacion
{
public Publicacion()
{
this.Edictos = new HashSet<Edictos>();
}
[Key]
public decimal IdPublicacion { get; set; }
public System.DateTime FechaPublicacion { get; set; }
public string IdUsuario { get; set; }
public System.DateTime FechaPublicacionHasta { get; set; }
public System.DateTime FechaArchivoHasta { get; set; }
public int IdSuscriptor { get; set; }
public decimal IdTipoPublicacion { get; set; }
[ForeignKey("IdSuscriptor")]
public virtual Suscriptores Suscriptores { get; set; }
}
When I try to run this query:
public ActionResult DetailsVSTO(string Identificacion)
{
var SusQ = from s in db.Suscriptores
where s.Identificacion == Identificacion
select s;
return Json(SusQ.First(), JsonRequestBehavior.AllowGet);
}
It throw this message:
System.Data.SqlClient.SqlException: Invalid column name 'Suscriptores_IdSuscriptor1'
Trying to solve this problem, I added this fluent API code at DBContext:
modelBuilder.Entity<Suscriptores>()
.HasMany(x => x.Publicacion)
.WithRequired(x => x.Suscriptores)
.Map(a => a.MapKey("IdSuscriptor"));
But the problem persists. How can I solve this?
Try add a many-to-one mapping as well. Please use pure Fluent API, and you should remove the [ForeignKey] annotations.
modelBuilder.Entity<Publicacion>()
.HasRequired(x => x.Suscriptores)
.WithMany(x => x.Publicacion);
I received this error in relation to a non-foreign key column and wasted far too much time trying to figure out the error. It was in my code, not in EF or the database. I had simply thought that I had typed
this.Property(t => t.Revision).HasColumnName("Revision");
this.Property(t => t.DistributionClass).HasColumnName("DistributionClass");
But what I had typed was
this.Property(t => t.Revision).HasColumnName("Revision");
this.Property(t => t.Revision).HasColumnName("DistributionClass");
I suppose I was looking at the line above and put t.Revision instead of t.DistributionClass. And no matter how long I looked at it I could not see my own mistake. With any luck this will save some other poor soul some time.
I had this issue in my Item table on a property (column) I had just added, and how frustrating!
Turns out I had a List property in the data model for Order, and because I did not Ignore it in that configuration it cause this same issue in the Item table. This would not have happened except that both tables had a property of the same name, so I had to do this... which I should have done anyways.
public OrderConfiguration() {
Ignore(p => p.Items);
}
Hello Guys In my case I had a legacy code with
two classes with different names of the same foreign key.
Adding the Annotation doing reference to the correct column and the name of attribute with the same name in other classes.then the annotation ForeignKey doing match between the both columns.
[Table("LFile")]
public partial class LFile
{
[Key]
public int FileId { get; set; }
[StringLength(500)]
public string FileName { get; set; }
public int? LRRFileDetailId { get; set; }
public byte[] FileData { get; set; }
public FileType Type { get; set; }
[Column("LUpload_Id")] //Foreign Key of the class
public int? LUploadId { get; set; }
[ForeignKey("LUploadId")] //Foreign key inherited
public virtual LParserProcess LParserProcess { get; set; }
}
I was getting the same error SqlException message where the number 1 was being appended to my field names.
I was able to solve it once I realized that I had incorrectly assumed the [ForeignKey] annotations refer to the field name as it is in the database. Instead, they should match the property name as defined in the model.
So for example:
[Column("Organisation_Account_Manager")]
[Display(Name = "Organisation_Account_Manager_ID")]
[DisplayFormat(NullDisplayText = "Unknown")]
public int? Organisation_Account_Manager_ID { get; set; }
[Display(Name = "Account Manager")]
[ForeignKey("Organisation_Account_Manager_ID")]
public Contact Account_Manager { get; set; }
In this example it will work, because [ForeignKey("Organisation_Account_Manager_ID")] is an exact match to public int? Organisation_Account_Manager_ID. Previously my [ForeignKey] annotation was using Organisation_Account_Manager, which is the field name in the database -- but this was incorrect.
I have following entity class called Code. It stores categories of different kinds - the data for which I would have otherwise needed to create many small tables e.g. User Categories, Expense Categories, Address types, User Types, file formats etc.
public class Code
{
public int Id { get; set; }
public string CodeType { get; set; }
public string CodeDescription { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
:
: // many more
}
The class Expense looks like this:
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public double Amount { get; set; }
public DateTime ExpenseDate { get; set; }
}
With the above class definitions, I have established 1:many relation between Code and Expense using the CategoryId mapping.
My problem is, I want to map the SourceId field in Expense to the Code object. Which means, Expense object would contain
public Code Source { get; set; }
If I use this, at runtime I get an error about cyclic dependencies.
Can someone please help?
You will need to disable cascading delete on at least one of the two relationships (or both). EF enables cascading delete by convention for both relationships because both are required since the foreign key properties are not nullable. But SQL Server doesn't accept multiple cascading delete paths onto the same table that are introduced by the two relationships. That's the reason for your exception.
You must override the convention with Fluent API:
public class Code
{
public int Id { get; set; }
//...
public virtual ICollection<Expense> Expenses { get; set; }
//...
}
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public virtual Code Source { get; set; }
//...
}
Mapping with Fluent API;
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Category)
.WithMany(c => c.Expenses)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Source)
.WithMany()
.HasForeignKey(e => e.SourceId)
.WillCascadeOnDelete(false);