model builder add one column more? - asp.net

I use EF code first.
It's my modelbuilder
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Market>()
.HasRequired(s => s.State)
.WithMany()
.HasForeignKey(s => s.StateId)
.WillCascadeOnDelete(false);
}
and State Class :
public class State
{
public State()
{
Markets = new HashSet<Market>();
}
[Key]
public int StateId { get; set; }
public string StateName { get; set; }
// navigation property
public virtual ICollection<Market> Markets { get; set; }
}
and Market class :
public class Market
{
[Key]
public int MarketId { get; set; }
public string MarketName { get; set; }
public int StateId { get; set; }
// navigation property
public virtual State State { get; set; }
}
Of course I remove extra code.
Problem is when I use this code , an State_StateId column add to my Market table in database, and when I do not use modelbuilder an error occurred with message loop code and ... (I say that I remove extra code), so how can I use code first without this "State_StateId" extra column.
excuse me for bad english writing.

If you want to remove State_StateId column set the configuration completely like the code below and don't let WithMany empty:
modelBuilder.Entity<Market>()
.HasRequired(s => s.State)
.WithMany(p => p.Markets)
.HasForeignKey(s => s.StateId)
.WillCascadeOnDelete(false);
Or you can just remove the Fluent API configuration and let EF use the default configuration convention and will set all tables, primary keys, foreign keys and column name for you.

Related

Error having two columns in primarykey when using computed colum (EF Migrations)

I'm trying to add migrations for a model which has multiple versions of my model in time (ModificationDate).
Creating a PK based on Id alone creates the migrations without incident (also tried ModificationDate as PK also no problems). But when I use Id + ModificationDate to create a PK I'm getting the following error:
The property 'Id' cannot be configured as 'ValueGeneratedOnUpdate' or
'ValueGeneratedOnAddOrUpdate' because the key value cannot be changed
after the entity has been added to the store.
public class Model
{
public int Id { get; set; }
public System.DateTimeOffset ModificationDate { get; set; }
public string Json { get; set; }
}
protected override CreateModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model>()
.ToTable("model")
.HasKey(e => new { e.Id, e.ModificationDate });
modelBuilder.Entity<Model>()
.Property(t => t.Id)
.HasComputedColumnSql("CAST(JSON_Value(Json, '$.Id') as int) PERSISTED");
}
I'm using EF Core Tools version 3.1.13.
Update
No fix but I fill the Id manually instead taking it from the json value.
public class Model
{
public int Id { get; set; }
public System.DateTimeOffset ModificationDate { get; set; }
public string Json { get; set; }
}
protected override CreateModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model>()
.ToTable("model")
.HasKey(e => new { e.Id, e.ModificationDate });
}

Two Foreign Keys in Entity Framework Core

I am using Code First approach while creating Database using Entity Framework Core. I would like to create two foreign keys pointing the same table.
My example shows User table which will hold userID and Message table which will hold both Receiver ID and Sender ID (what means both values have to point the same table).
Code for User:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string UserName { get; set; }
[Required]
[MaxLength(50)]
public string Password { get; set; }
public virtual ICollection<Message> MessagesSent { get; set; }
public virtual ICollection<Message> MessagesReceived { get; set; }
}
For Message:
public class Message
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public User Sender { get; set; }
public User Receiver { get; set; }
public int senderId { get; set; }
public int receiverId { get; set; }
[MaxLength(500)]
public string message { get; set; }
}
I am using ASP.NET Core 2 and I am a newbie.
I was trying to use this solution, but unfortunately, I couldn't manage to override OnModelCreating method. It shows that it doesn't exist.
PS. don't mind password field, it is only for testing purpose.
Thank you!
I managed to make it works using Fluent API.
Code in my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Message>()
.HasOne(p => p.Receiver)
.WithMany(t => t.MessagesReceived)
.HasForeignKey(m => m.ReceiverId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Message>()
.HasOne(p => p.Sender)
.WithMany(t => t.MessagesSent)
.HasForeignKey(m => m.SenderId)
.OnDelete(DeleteBehavior.Restrict);
}
What is more, I've discovered a problem with not set User deletion behaviour.
There are two options to solve it.
First is keeping Messages if User was deleted:
.OnDelete(DeleteBehavior.Restrict);
Or second which will remove Messages:
.OnDelete(DeleteBehavior.Cascade);

EF core , Code First Table rename not detected for migration definition, Scafolder is empty

I get empty migration builder when I change a class name in EF core.
In older EFs , it usually auto generate the code for renaming tables.
But not working in EF core
public class EventComment : Comment
{
[Key]
public int CommentID { get; set; }
public int? ParentID { get; set; }
public virtual EventComment Parent { get; set; }
public int EventID { get; set; }
public virtual EventMaster EventMaster { get; set; }
}
public class Comment
{
public string CommentTitle { get; set; }
public string CommentDetails { get; set; }
public int UpVoteCount { get; set; }
public int DownVoteCount { get; set; }
public int CommentEmotion { get; set; }
public string CommentedByID { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
now changing EventComment to CommentMaster. The migration is empty.
I use fluent API for configuring
builder.Entity<EventComment>()
.HasOne(e => e.EventMaster)
.WithMany()
.HasForeignKey(m => m.EventID);
builder.Entity<EventComment>()
.HasOne(e => e.ApplicationUser)
.WithMany()
.HasForeignKey(m => m.CommentedByID);
builder.Entity<EventComment>()
.HasOne(e => e.Parent)
.WithMany()
.HasForeignKey(x => x.ParentID);
This is because of the EF Core default table mapping convention (highlights are mine):
By convention, each entity will be setup to map to a table with the same name as the DbSet<TEntity> property that exposes the entity on the derived context. If no DbSet<TEntity> is included for the given entity, the class name is used.
I guess this is different from the previous EF. The essential part is that although you renamed the entity class, if you keep the old DbSet property name, the table name will not change.

Can I split a SQL table into three classes using EF 6.1 and SQL Server 2012?

I have the following class which I simplified for this example. The class has an id
and some properties related to House and some related to room. All the data is stored
in one row of a SQL table.
namespace ClassLibrary1.Models
{
public partial class AspNetUser
{
public string Id { get; set; }
public Nullable<int> AdminHouseNumber { get; set; }
public string AdminHouseCity { get; set; }
public Nullable<int> AdminRoomNumber { get; set; }
public string AdminRoomFloor { get; set; }
}
}
SQL:
CREATE TABLE [dbo].[AspNetUsers] (
[Id] NVARCHAR (128) NOT NULL,
[AdminHouseNumber] INT NULL,
[AdminHouseCity] NCHAR (10) NULL,
[AdminRoomNumber] INT NULL,
[AdminRoomFloor] NCHAR (10) NULL,
CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
);
This maps to my database like this:
namespace ClassLibrary1.Models.Mapping
{
public class AspNetUserMap : EntityTypeConfiguration<AspNetUser>
{
public AspNetUserMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Id)
.IsRequired()
.HasMaxLength(128);
// Table & Column Mappings
this.ToTable("AspNetUsers");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.AdminHouseNumber).HasColumnName("AdminHouseNumber");
this.Property(t => t.AdminHouseCity).HasColumnName("AdminHouseCity");
this.Property(t => t.AdminRoomNumber).HasColumnName("AdminRoomNumber");
this.Property(t => t.AdminRoomFloor).HasColumnName("AdminRoomFloor");
}
}
}
For performance reasons I would like to keep all my data in the one table. However is
it possible for me to create two additional classes. One for house and one for room
and have all three classes link to the same table?
namespace ClassLibrary1.Models
{
public partial class AspNetUser
{
public string Id { get; set; }
public string RoomId { get; set; }
public string HouseId { get; set; }
public virtual House { get; set; }
public virtual Room { get; set; }
public class House
public string HouseId { get; set; }
public Nullable<int> AdminHouseNumber { get; set; }
public string AdminHouseCity { get; set; }
}
Public class Room
public string RoomId { get; set; }
public Nullable<int> AdminRoomNumber { get; set; }
public string AdminRoomFloor { get; set; }
}
}
}
It seems what you're looking for is called ComplexTypes support, some googling showed this basic article
I understand the question as that you have three types of AspNetUser; AspNetUser, House and Room. If I'm wrong, then this answer will not make sense - please let me know.
You may want to look at using Table per Hierarchy Inheritance in your modelling, but that will require a further "flag column" in your table to work (for example containing 0 for AspNetUser, 1 for House, 2 for Room). I've found that performance is slightly slower when using TPH, but essentially you do this.
public class AspNetUserBase
{
//Define columns shared between all here
}
public class AspNetUser: AspNetUserBase
{
// Define columns only for AspNetUser here
}
public class House: AspNetUserBase
{
// Define columns only for House here
}
// Repeat for Room
// Now define the "split" in OnModelCreating
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Any code you need etc.
// This part maps all three to the table
modelBuilder.Entity<AspNetUserBase>()
.Map<AspNetUser>(m => m.Requires("Your Flag Column").HasValue(0)) // 0 is example
.Map<House>(m => m.Requires("Your Flag Column").HasValue(1)) // 1 is example
.Map<Room>(m => m.Requires("Your Flag Column").HasValue(2)) // 2 is example
}
You'll then be able to directly perform CRUD's on AspNetUser, Home or Room (without having to reference the base class).
However, I've got to question the need for them all to be on one table for performance reasons as my experience has almost always proved otherwise. It's hard to be specific as of course I don't know your entire model, but I urge at some point you re-evaluate.

entity framework multiple relationships

I'm new to Entity Framework and I have probably a simple question.
I have simplified the structure at a maximum to be clear (I hope I am).
Imagine that I just need to create a simple "Enterprise" class, with only a Name.
Then another class named "Worker" with also just a Name for the worker.
A worker should belong to an Enterprise.
An Enterprise must have a manager (who is a Worker).
So here is how I imagine these simple classes :
public class Worker
{
public int WorkerId { get; set; }
public String Name { get; set; }
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
public class Enterprise
{
public int EnterpriseId { get; set; }
public String Name { get; set; }
public Worker Manager { get; set; }
public List<Worker> Workers { get; set; }
}
I'd like these classes to result in the following DB structure :
Table Worker
WorkerId (PK, int, not null)
Name (varchar(128), not null)
EnterpriseId (FK, int)
Table Enterprise
EnterpriseId (PK, int, not null)
Name (varchar(128), not null)
Manager (FK, int)
I tried many things with modelBuilder, but I never obtain what I want.
Is there a solution with Fluent API to do what I want to do?
Thank you very much for your help.
This will not get you what you want (in Db) - but is what I recommend...
public ICollection<Worker> Workers { get; set; } // instead of List<>
// ...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalPrincipal() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
You can use it like:
var enterprise = new Enterprise { Manager = new Worker { Name = "Manager", }, };
enterprise.Workers = new[]
{
enterprise.Manager,
new Worker{ Name = "Worker1", },
new Worker{ Name = "Worker2", },
new Worker{ Name = "Worker3", },
new Worker{ Name = "Worker4", },
new Worker{ Name = "Worker5", },
};
db.Enterprises.Add(enterprise);
db.SaveChanges();
var enterprises = db.Enterprises.ToList();
This is exactly what you want...
modelBuilder.Entity<Worker>()
.HasRequired(x => x.Enterprise)
.WithMany(x => x.Workers)
.HasForeignKey(x => x.EnterpriseId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Enterprise>()
.HasKey(x => x.EnterpriseId);
modelBuilder.Entity<Enterprise>()
.HasOptional(x => x.Manager)
.WithOptionalDependent() // x => x.DefaultForEntity)
.WillCascadeOnDelete(false);
...but will not work - due to cyclical references (EF error).
Here is a pretty detailed example for a similar / identical solution...
Entity Framework One-to-Many with Default
I don't know how you intend to get the manager object, but my guess is you need to use Inheritance to make your design optimal. Try this:
public abstract class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmployeeId{ get; set; }
public String Name { get; set; }
[ForeignKey("Enterprise"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int EnterpriseId { get; set; } // ForeignKey for Enterprise
public Enterprise Enterprise { get; set; }
}
[Table("Workers")] // Table per Type (TPT), This will be your Table name in your database
public class Worker : Employee
{
//Add properties only related to workers
}
[Table("Managers")] // Table per Type (TPT). This will be your Table name in your database
public class Manager : Employee
{
//Add properties only related to Managers
}
public class Enterprise
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EnterpriseId { get; set; }
public String Name { get; set; }
public virtual ICollection<Employee> Employees{ get; set; }
}
Note: Sorry this is done using Property Mapping
Link: Here is a link to simple Fluent Mapping example
Link : Read about Table per Type (TPT) Inheritance here

Resources