I'm new to .NET Core having been in standard ASP.NET for a long time.
I'm really struggling with creating a base class for all of my models as they all share a couple of fields.
Basically in each table I have the following columns:
Id
CreatedByUserID
CreationDateTime
UpdatedByUserID
UpdatedDateTime
DeletedByUserID
DeletionDateTime
IsDeleted
My base entity code:
public abstract class BaseEntity
{
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public Guid CreatedByUserId { get; set; }
public DateTime UpdateDate { get; set; }
public Guid UpdateByUserId { get; set; }
public bool IsDeleted { get; set; }
public DateTime DeletedDate { get; set; }
public Guid DeletedByUserId { get; set; }
public virtual User? CreatedByUser { get; set; }
public virtual User? DeletedByUser { get; set; }
public virtual User? UpdateByUser { get; set; }
public BaseEntity()
{
Id = Guid.NewGuid();
CreationDate = DateTime.Now;
CreatedByUserId = Guid.Empty;
UpdateDate = DateTime.Now;
UpdateByUserId = Guid.Empty;
DeletedDate = new DateTime(1, 1, 1);
DeletedByUserId = Guid.Empty;
IsDeleted = false;
}
}
Part of my user code:
public class User : IdentityUser<Guid>
{
public User()
{
BaseCreatedByUsers = new HashSet<BaseEntity>();
BaseDeletedByUsers = new HashSet<BaseEntity>();
BaseUpdateByUsers = new HashSet<BaseEntity>();
}
public String FirstName { get; set; }
public String LastName { get; set; }
public int IdentificationNumber { get; set; }
public string PassportNumber { get; set; }
public bool Active { get; set; }
public String DisplayName
{
get { return FirstName + " " + LastName; }
}
public DateTime CreationDate { get; set; }
public Guid CreatedByUserId { get; set; }
public DateTime UpdateDate { get; set; }
public Guid UpdateByUserId { get; set; }
public bool IsDeleted { get; set; }
public DateTime DeletedDate { get; set; }
public Guid DeletedByUserId { get; set; }
public virtual ICollection<BaseEntity> BaseCreatedByUsers { get; set; }
public virtual ICollection<BaseEntity> BaseDeletedByUsers { get; set; }
public virtual ICollection<BaseEntity> BaseUpdateByUsers { get; set; }
}
If I do not include base entity in the ApplicationDbContext file, then I get the error:
Unable to determine the relationship represented by navigation 'User.BaseCreatedByUsers' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
But if I do include it in ApplicationDbContext:
modelBuilder.Entity<BaseEntity>(entity =>
{
entity.HasOne(d => d.CreatedByUser)
.WithMany(p => p.BaseCreatedByUsers)
.HasForeignKey(d => d.CreatedByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User");
entity.HasOne(d => d.DeletedByUser)
.WithMany(p => p.BaseDeletedByUsers)
.HasForeignKey(d => d.DeletedByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User2");
entity.HasOne(d => d.UpdateByUser)
.WithMany(p => p.BaseUpdateByUsers)
.HasForeignKey(d => d.UpdateByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User1");
});
Then I get this error:
Both 'ReceivingSheetTemplateDetail' and 'BaseEntity' are mapped to the table 'BaseEntity'. All the entity types in a hierarchy that don't have a discriminator must be mapped to different tables.
In this case ReceivingSheetTemplateDetail is using the base entity class.
My goal in all of this is just to take out the hassle of adding these fields to every model and to make saving easier as I can overwrite on class instead of having to do it for each.
I know I'm missing something so I would appreciate any assistance :)
Related
I have two entities, Function and Department with a Many to Many table in between.
public class Department
{
public Department()
{
DepartmentFunctions = new HashSet<DepartmentFunction>();
PersonFunctions = new HashSet<PersonFunction>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<DepartmentFunction> DepartmentFunctions { get; set; }
public DateTime? DeletedAt { get; set; }
}
public class Function
{
public Function()
{
DepartmentFunctions = new HashSet<DepartmentFunction>();
FunctionTaskProfiles = new HashSet<FunctionTaskProfile>();
PersonFunctions = new HashSet<PersonFunction>();
}
public int Id { get; set; }
public string Name { get; set; }
public float? ItemOrder { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual ICollection<DepartmentFunction> DepartmentFunctions { get; set; }
public virtual ICollection<FunctionTaskProfile> FunctionTaskProfiles { get; set; }
public virtual ICollection<PersonFunction> PersonFunctions { get; set; }
public DateTime? DeletedAt { get; set; }
}
public class DepartmentFunction
{
public int FunctionId { set; get; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
public virtual Function Function { get; set; }
}
When the departmentfunction on either entity is loaded, either with eager loading or lazy loading, every unique DepartmentFunction is listed twice. This problem occured after updating from EF core 5 to EF core 7. I have not changed anything with that update.
I have tried explicitly configuring the relation like this:
modelBuilder.Entity<DepartmentFunction>(entity =>
{
entity.HasKey(e => new {FunctieId = e.FunctionId, AfdelingId = e.DepartmentId})
.HasName("PK__Afdeling__591131EC594F97A1");
entity.HasOne(d => d.Department)
.WithMany(p => p.DepartmentFunctions)
.HasForeignKey(d => d.DepartmentId)
.OnDelete(DeleteBehavior.ClientSetNull);
entity.HasOne(d => d.Function)
.WithMany(p => p.DepartmentFunctions)
.HasForeignKey(d => d.FunctionId)
.OnDelete(DeleteBehavior.ClientSetNull);
I have also seen this solution, however, implementing this one would involve changing hundreds of references.
I have a C# running on .NET 5.0 application. And I have the following tables
public class Attribute
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public List<AssessmentAttributeItem> ItemMappings { get; set; }
public List<AssessmentAttributeItem> AttributeMappings { get; set; }
public Attribute()
{
ItemMappings = new List<AssessmentAttributeItem>();
AttributeMappings = new List<AssessmentAttributeItem>();
}
}
public class AssessmentAttributeItem
{
[Key]
public int Id { get; set; }
[Required]
public int AttributeId { get; set; }
[ForeignKey("AttributeId")]
public virtual Attribute Attribute { get; set; }
public int? ItemId { get; set; }
[ForeignKey("ItemId")]
public virtual AssessmentItem Item { get; set; }
public int? ChildAttributeId { get; set; }
[ForeignKey("ChildAttributeId")]
public virtual Attribute ChildAttribute { get; set; }
}
And I am using Fluent API to specify the relationship like the following
modelBuilder.Entity<AssessmentAttributeItem>()
.HasOne(x => x.Item)
.WithMany().HasForeignKey(y => y.ItemId);
modelBuilder.Entity<AssessmentAttributeItem>()
.HasOne(x => x.Attribute)
.WithMany(y => y.ItemMappings);
modelBuilder.Entity<AssessmentAttributeItem>()
.HasOne(x => x.ChildAttribute)
.WithMany().HasForeignKey(y => y.ChildAttributeId);
Mmy question is: when I try to add migration it is adding an extra column called AttributeId1
migrationBuilder.AddColumn<int>(
name: "AttributeId1",
table: "AssessmentAttributeItems",
type: "int",
nullable: true);
public class AssessmentItem
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string Code { get; set; }
}
I want my table to look like this
ID
AttributeId
ItemId
ChildAttributeId
.
.
.
.
I want to list the data of the table sale, accessing the name of the user with the foreign key userId, what is the correct way to do this with the context?
using (var context = new AppDbContext())
{
var comic = context.sale.Select(d => d);
}
public class sale
{
public int saleId { get; set; }
public DateTime date { get; set; }
public int userId { get; set; }
}
public class user
{
public int userId { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string email { get; set; }
public string password { get; set; }
}
Context:
modelBuilder.Entity<sale>()
.HasOne<user>()
.WithMany()
.HasForeignKey(p => p.userId);
You need to alter you classes to add navigation properties:
public class sale
{
public int saleId { get; set; }
public DateTime date { get; set; }
public int userId { get; set; }
public user user { get; set; }
}
public class user
{
public int userId { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string email { get; set; }
public string password { get; set; }
public ICollection<sale> sales { get; set; } = new HashSet<sale>();
};
Then change your context:
modelBuilder.Entity<sale>()
.HasOne(a => a.user)
.WithMany(a => a.sales)
.HasForeignKey(p => p.userId);
You can then get the related data with:
var user = context.sale.Include(a => a.user)...
I am new to MVC and EF Core and have been trying to seed some data. All my tables seed correctly except for one table and I am tired of banging my head against a wall. I have even recreated the entire application with no luck. I am certain I am missing something obvious.
My Models
public class AgreementType
{
public int AgreementTypeID { get; set; }
[Required]
[StringLength(100)]
public string Description { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public Agreement Agreement { get; set; }
}
public class ContactType
{
public int ContactTypeID { get; set; }
[Required]
[StringLength(100)]
public string Description { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public Contact Contact { get; set; }
}
public class Branch
{
public int BranchID { get; set; }
public string Name { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public int IntermediaryID { get; set; }
public Intermediary Intermediary { get; set; }
public int RegionID { get; set; }
public virtual Region Region { get; set; }
}
My Context Class
public class BizDevHubContext : DbContext
{
public BizDevHubContext(DbContextOptions<BizDevHubContext> options) : base(options)
{
}
public DbSet<AgreementType> AgreementType { get; set; }
public DbSet<Branch> Branch { get; set; }
public DbSet<ContactType> ContactTypes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AgreementType>().ToTable("AgreementType");
modelBuilder.Entity<Branch>().ToTable("Branch");
modelBuilder.Entity<ContactType>().ToTable("ContactType");
// One to One
modelBuilder.Entity<Agreement>()
.HasOne(b => b.AgreementType)
.WithOne(i => i.Agreement)
.HasForeignKey<Agreement>(b => b.AgreementTypeID);
modelBuilder.Entity<Contact>()
.HasOne(b => b.ContactType)
.WithOne(i => i.Contact)
.HasForeignKey<Contact>(b => b.ContactTypeID);
//One to Many
modelBuilder.Entity<Region>()
.HasMany(c => c.Branch)
.WithOne(e => e.Region)
.HasForeignKey((p => p.BranchID));
// Set default dates
modelBuilder.Entity<AgreementType>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
modelBuilder.Entity<Branch>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
modelBuilder.Entity<ContactType>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
}
}
My Initializer Class
if (context.Branch.Any())
{
return;
}
var branches = new Branch[]
{
new Branch{Name="Bloemfontein",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Cape Town",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Durban",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Nelspruit",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Johannesburg",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Port Shepstone",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Pretoria",CreationDate=DateTime.Now,CreatedBy="System"}
};
foreach (Branch b in branches)
{
context.Branch.Add(b);
}
context.SaveChanges();
The other tables all seed except the Branch table.
Please help!
I want to create a database in PostgreSQL and API for my app. I've created Model and API in EF Core 1.0.0. Model exists in separate library.
public class Architect : IEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public ICollection<Project> Projects { get; set; }
}
public class Project : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTimeOffset CreatedAt { get; set; }
public ICollection<Company> Companies { get; set; }
public string City { get; set; }
public ICollection<ProjectState> ProjectStates { get; set; }
public Guid ArchitectId { get; set; }
public Architect Architect { get; set; }
}
public class Company : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
}
public class ProjectState : IEntity
{
public Guid Id { get; set; }
public ProjectPhase phase { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
}
I have created also DemoContext that inherits DbContext. I defined relashionships in OnModelCreating(ModelBuilder modelBuilder) method.
public class DemoContext : DbContext
{
public DbSet<Architect> Architects { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<ProjectState> ProjectStates { get; set; }
public DemoContext(DbContextOptions<DemoContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("PORT = 5432; HOST = localhost; TIMEOUT = 15; POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 20; COMMANDTIMEOUT = 20; DATABASE = ProgressTest; PASSWORD = 'postgres'; USER ID = postgres", b => b.MigrationsAssembly("WebAPI.API"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasPostgresExtension("uuid-ossp");
modelBuilder.Entity<Project>()
.HasOne(p => p.Architect)
.WithMany(a => a.Projects)
.HasForeignKey(p=>p.ArchitectId);
modelBuilder.Entity<ProjectState>()
.HasOne(p => p.Project)
.WithMany(p => p.ProjectStates)
.HasForeignKey(p => p.ProjectId);
modelBuilder.Entity<Company>()
.HasOne(c => c.Project)
.WithMany(p => p.Companies)
.HasForeignKey(c => c.ProjectId);
}
}
I want to migrate all classes and create new database. I've used
dotnet ef migrations add InitializeMigration to add new migration and after it I wanted to update my database, so I've used dotnet ef database update but Postgres throw an Exception:
Npgsql.PostgresException: 42P01: relationship "ProjectState.IX_ProjectState_ProjectId" doesn't exist
Can you help me with it?
In my case i just decorate my model class with "Table" attribute with actual table name (mind case sensativity) and it resolved my problem
[Table("city")]
public class CityModel