How to make optional required for differents attributes on differents inherited classes on EF6 , (businnes required rules) - entity-framework

How to make optional REQUIRED for the same attribute on different inherited classes on EF6.
Why the 'required attribute' from one child is required for other child?
Why does entity framework merge all data anotations to base classe 'Person' if the base class is non required attributes?
I've used the same classes on MVC to create required fields on cshtml, and it works. The MVC understands only required field from one child and not make any 'wrong merge' with those two child classes.
For example:
//EF Codefirst Class
public class Person
{
[Key]
public int key{get;set;}
[StringLength(500)]
public virtual string name { get; set; }
[StringLength(500)]
public virtual string email{ get; set; }
[StringLength(500)]
public virtual string phone{ get; set; }
[StringLength(500)]
public virtual string address{ get; set; }
[StringLength(500)]
public virtual string manager{ get; set; }
[StringLength(500)]
public virtual string Discriminator{ get; set; }
}
//My Inherited classes
public class Employee : Person
{
[Required]
public override string name{ get; set; }
[Required]
public override string phone{ get; set; }
[Required]
public override string manager{ get; set; }
}
public class Manager: Person
{
[Required]
public override string name{ get; set; }
[Required]
public override string email{ get; set; }
}
//And my sample function 'Add PersonManager'
private void InsertPerson()
{
using (var ctx = new MyDataContext())
{
try
{
var m = new Manager() ;
m.name = "my name" ;
m.email = "my#email.com";
m.address =" something";
ctx.Person.Add(m);
ctx.SaveChanges();
}
catch (Exception ex)
{
// Why, if I try to Add my Person 'Manager', the attribute : phone and manager is REQUIRED?
}
}
}

It happens because you are using TPH strategy. All entities will be merged in one table, EF handles what must be null or not null.
If you use TPT strategy, EF will create different tables for each entity. To learn more about inheritance strategies take a look at this link http://blogs.msdn.com/b/alexj/archive/2009/04/15/tip-12-choosing-an-inheritance-strategy.aspx
To use TPT instead of TPH, you must define a "key" in your child class, like this:
public class Employee : Person
{
[Key]
public int employeeId;
[Required]
public override string name;
[Required]
public override string phone;
[Required]
public override string manager;
}
Another way to do this is using Fluent API. Like this:
modelBuilder.Entity<Person>()
.HasKey(c => c.key);
modelBuilder.Entity<Employee>()
.ToTable("Employees");
modelBuilder.Entity<Manager>()
.ToTable("Managers");
To see more about this, take a look at this link https://msdn.microsoft.com/en-us/data/jj591617.aspx#2.5

Related

The entity type required a primary key to be defined - but there is

I probably have a fairly trivial problem with EF configuring 1 table. This is how my class looks like:
public class Task
{
[Key]
public int Id { get; set; }
[Required]
public string Description { get; set; }
[Display(Name = "Modification Date")]
public DateTime ModificationDate { get; set; }
[Required]
public bool IsDone { get; set; }
}
This is how dbContext looks like:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options) : base (options) { }
public DbSet<Task> Tasks { get; set; }
}
And while creating migration I get this error:
The entity type 'Task' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating' [...]
But as you can see I have an attribute [Key], the property is public and has a setter, what could be the problem?
Ok, that was the dumbest mistake in a long time. It turned out the context was using the Task system class instead of my model class...

EF core 2.0 one to many share the same table

How to resolve "Navigation properties can only participate in a single relationship." error on below case?
1 company has many Milestone and MissionValueStory, where Milestone and MissionValueStory share same table with different typeId, and each of those has many translation where link up with companyInfoId only
Or BETTER break the relationship between companyInfo and company, and just another query to fetch companyInfo is much easy?
public class Company
{
[key]
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CompanyInfo> Milestone { get; set; } //multi
public virtual ICollection<CompanyInfo> MissionValueStory { get; set; } //multi
}
public class CompanyInfo
{
[key]
public long Id { get; set; }
public long typeId { get; set; }
[Required]
public long CompanyId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
public ICollection<Translation> Translation { get; set; }
}
public class Translation
{
[key]
public long Id { get; set; }
public string Title { get; set; }
[Required]
public long CompanyInfoId { get; set; }
public string Language { get; set; }
[ForeignKey("CompanyInfoId")]
public virtual CompanyInfo CompanyInfo { get; set; }
}
modelBuilder.Entity<Company>()
.HasMany(e => e.Milestone)
.WithOne(t => t.Company)
.HasForeignKey(m => m.CompanyId).IsRequired()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Company>()
.HasMany(e => e.MissionValueStory)
.WithOne(t => t.Company)
.HasForeignKey(m => m.CompanyId).IsRequired()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<CompanyInfo>()
.HasMany(e => e.Translation)
.WithOne(t => t.CompanyInfo).IsRequired();
What you're trying to do is legitimately not supported. At least in the way you're going about this. Fortunately there's a fairly painless solution for you. Use Table Per Hierarchy.
Change the class CompanyInfo to be an abstract class called CompanyInfoBase, and let it be an abstract type. Make typeId abstract on CompanyInfoBase.
Create two new classes that implement CompanyInfoBase:
public class MilestoneCompanyInfo : CompanyInfoBase
{
public override long typeId { get; set; } = MILESTONE_TYPE_ID;
}
public class MissionValueStoryCompanyInfo : CompanyInfoBase
{
public override long typeId { get; set; } = MISSION_VALUE_STORY_TYPE_ID;
}
where MILESTONE_TYPE_ID and MISSION_VALUE_STORY_TYPE_ID are some sort of predefined constants.
Then, in your DbContext's OnModelCreating, use typeId as your discriminator.
It'll look something like this:
modelBuilder.Entity<CompanyInfoBase>()
.HasDiscriminator<long>(nameof(CompanyInfoBase.typeId))
.HasValue<MilestoneCompanyInfo>(MILESTONE_TYPE_ID)
.HasValue<MissionValueStoryCompanyInfo>(MISSION_VALUE_STORY_TYPE_ID);
Since you're changing the name of the entity, it's worth setting the table name to accommodate your existing db. Something like:
modelBuilder.Entity<CompanyInfoBase>().ToTable("CompanyInfos");
Note to other readers: It's only required to define the discriminator like this due to his decision to use a long. If he had just left it undefined then EF Core automagically handles this (by creating a column named discriminator that contains the concrete class names).
Here's a link to the inheritance reference page: https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/inheritance

Multiple Common Fields CreatedOn and CreatedBy in every table of a database. How it can be without repeating for every table

Scenerio:
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class TestItem
{
public int TestItemId { get; set; }
public string TestItemName { get; set; }
public Department Department { get; set; }
public int DepartmentId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Patient
{
public int PatientId { get; set; }
public string PatientName { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
the problem is that, every time I create a table I have to add those two columns repeatedly.
But I want like this-
public class EntryLog
{
public int EntryLogId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public EntryLog EntryLog { get; set; }
public int EntryLogId { get; set; }
}
and so on...
class A { .. }
class B { .. }
But its creating problem [showing conflicts error with other table's foreign key] while creating a row for a Department or a Patient.
In EF core, there is Table Per Hierarchy (TPH) but in that case every table will be merged into a single table. But that doesn't give me any solution.
looking forward to expert's suggestion...
The bottom line is: use EntryLog as a base type and don't tell EF about it. It's easy enough to keep EF-core oblivious of the base type: only register the derived types. Doing so, EF-core will map your subtypes to their own tables, just as if they didn't have a common type.
Now EntryLog will no longer need an Id, and it should be abstract:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
public string CreatedBy { get; set; }
}
Whether this is enough depends on your specific requirements. There are several possibilities.
1. No additional configuration
If you're happy with the default conventions EF will apply to the common properties, your done. CreatedOnUtc will be mapped to a DateTime2 column (in Sql Server) and CreatedBy to an nvarchar(max) column in each table for an EntryLog entity.
However, if you do need custom configurations --for example if you want to map CreatedBy to an nvarchar(50) column-- additional mapping instructions should be applied. And of course you still want to do the mapping of the common properties only once --which would also happen if you did map the base type in a TPH scheme. How to do that?
2. Data annotations in the base type
The easiest option is to add data annotations:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
[MaxLength(50)]
public string CreatedBy { get; set; }
}
And that's all.
But there are dev teams that don't want to use data annotations for mapping instructions. Also, EF's fluent mappings offer more options than data annotations do. If data annotations don't fit the bill for whatever reason, fluent configurations must be applied. But still, you only want to configure the common properties only once. A viable way to achieve that is to use IEntityTypeConfigurations for each EntryLog and let each concrete configuration derive from a base class. This offers two more options.
3. The base class contains regular properties
Option 4 will make clear why I talk about "regular properties" here. This is what it looks like:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
// Just an example of how to configure a base property.
builder.Property(e => e.CreatedBy).HasMaxLength(50);
}
}
class DepartmentConfiguration : EntryLogConfiguration,
IEntityTypeConfiguration<Department>
{
public void Configure(EntityTypeBuilder<Department> builder)
{
builder.Property(p => p.DepartmentName).HasMaxLength(100);
ConfigureBase(builder);
}
}
And in the context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new DepartmentConfiguration());
}
4. Using shadow properties
Shadow properties is a new feature of EF-core.
Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model. The value and state of these properties is maintained purely in the Change Tracker.
Let's suppose you want to have CreatedBy as a class property (because you want to show it in a UI) but only need CreatedOnUtc as a property that's set in the background and that shouldn't be exposed. Now EntryLog will look like this:
public abstract class EntryLog
{
public string CreatedBy { get; set; }
}
So the property CreatedOnUtc is gone. It has been moved to the base configuration as shadow property:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
builder.Property(e => e.CreatedBy).HasMaxLength(50);
builder.Property<DateTime>("CreatedOnUtc");
}
}
Now you can't set CreatedOnUtc directly, only through EF's change tracker. The best place to do that is in an override of SaveChanges in the context:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<EntryLog>())
{
entry.Property<DateTime>("UpdatedOnUtc").CurrentValue = DateTime.UtcNow;
}
return base.SaveChanges();
}
Of course, if UpdatedOnUtc was a regular property, this override would also come in handy, but you could just do
entry.Entity.CreatedOnUtc = DateTime.UtcNow;
I hope this will give you enough food for thought to figure out which option suits you best.

EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType

I've just started a new project in Web Forms because I thought it'd be quicker than learning MVC, but how wrong was I! I'm struggling with the Identity aspect of my project. I've followed this link here: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
And it all compiles ok and works (my ID column is now an integer).
So I've created a new class for a 'Property' context:
using System.Collections.Generic;
using System.Data.Entity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Web.Security;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace MyApp.Models
{
public class PropertyContext : DbContext
{
public PropertyContext()
: base("DefaultConnection")
{
}
public DbSet<Property> Property { get; set; }
}
public class Property
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int PropertyID { get; set; }
//[Required, StringLength(128)]
//public string OwnerID { get; set; }
// [ForeignKey("OwnerID")]
//public virtual ApplicationUser ApplicationUser { get; set; }
//[ForeignKey("OwnerID")]
//public int OwnerID { get; set; }
// public virtual ApplicationUser ApplicationUser { get; set; }
public int OwnerID { get; set; }
[ForeignKey("OwnerID")]
public virtual ApplicationUser ID { get; set; }
[Required, StringLength(255), Display(Name = "Address 1")]
public string Address1 { get; set; }
[Required, StringLength(255), Display(Name = "Address 2")]
public string Address2 { get; set; }
[Required, StringLength(255), Display(Name = "Address 3")]
public string Address3 { get; set; }
[Required, StringLength(255), Display(Name = "Town/City")]
public string Settlement { get; set; }
[Required, StringLength(255), Display(Name = "County")]
public string County { get; set; }
[Required, StringLength(255), Display(Name = "Post Code")]
public string PostCode { get; set; }
[Required, StringLength(255), Display(Name = "Country")]
public string Country { get; set; }
}
}
Whereby I'm trying to make a new int column called 'OwnerID' a foreign key into the 'AspNetUser' table's ID column. When i build my project, it builds fine. However when I try to add a migration I get this:
MyApp.Models.CustomUserLogin: : EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType.
MyApp.Models.CustomUserRole: : EntityType 'CustomUserRole' has no key defined. Define the key for this EntityType.
CustomUserLogins: EntityType: EntitySet 'CustomUserLogins' is based on type 'CustomUserLogin' that has no keys defined.
CustomUserRoles: EntityType: EntitySet 'CustomUserRoles' is based on type 'CustomUserRole' that has no keys defined.
So in my ApplicationDbContext I've added this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("Users").HasKey<int>(u => u.Id);
modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles").HasKey(ur => new { ur.RoleId, ur.UserId });
modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins").HasKey<int>(ul => ul.UserId);
modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims").HasKey<int>(uc => uc.Id);
modelBuilder.Entity<CustomRole>().ToTable("Roles").HasKey<int>(r => r.Id);
}
and even this
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CustomUserLogin>().HasKey(cul => cul.UserId);
modelBuilder.Entity<CustomUserRole>().HasKey(cur => cur.UserId);
But it still doesn't work. And to be quite honest I'm burning up a whole weekend by stabbing in the dark. I can't believe something as simple as creating a foreign key is so difficult in this Entity Framework environment.
Can anybody give me a pointer? Thanks.
I did exactly as you did.
Added Identity to my MVC Web Application
Changed the primary key to be an INT rather a GUID
Added an EmployeeProfile and a CustomerProfile to the ApplicationUser
I did not have to override OnModelCreating as I made my own changes to the database tables to support what I needed.
Added two new properties to the ApplicationUser class in IdentityModels.cs
public virtual CustomerProfile CustomerProfile { get; set; }
public virtual EmployeeProfile EmployeeProfile { get; set; }
I created the CustomerProfile and EmployeeProfile classes. Note the Data Annotations on the Id property of each class. The Id property is the ForeignKey property to guide the virtual User property back to the ApplicationUser.
public class EmployeeProfile
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public DateTime HireDate { get; set; }
public DateTime BirthDate{ get; set; }
public virtual ApplicationUser User { get; set; }
}
public class CustomerProfile
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public CustomerType CustomerType { get; set; }
public string Company { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string WorkPhone { get; set; }
public string CustomerId { get; set; }
public string AccountType { get; set; }
public virtual ApplicationUser User { get; set; }
}
In my ApplicationDbContext class, I added the following lines of code:
public DbSet<CustomerProfile> CustomerProfile { get; set; }
public DbSet<EmployeeProfile> EmployeeProfile { get; set; }
Everything was working as expected. I logged in and saw the Customer and Employee info in my views.
I started seeing the error you received "EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType" when I added a many to many relation ship in the EmployeeProfile class to a Departments class. In my scenario an Employee can belong to 1 or many departments and a department can have many employees. My database changes were good and even the classes compiled just like you experienced, however once I ran the project, the website would error out.
Backing out my changes resolved the issue. Something in my classes was throwing off the validation of the model, so I'll need to approach the problem differently. So just like in my case, your poco classes may have an issue.
Remove your 'Property' class and references. Make the project work with the base essentials. Once you have it working, add the Property class back in with just the Id property.
Let me know if you still need help.
Place the base.OnModelCreating at the bottom:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ApplicationUser>().ToTable("Users").HasKey<int>(u => u.Id);
modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles").HasKey(ur => new { ur.RoleId, ur.UserId });
modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins").HasKey<int>(ul => ul.UserId);
modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims").HasKey<int>(uc => uc.Id);
modelBuilder.Entity<CustomRole>().ToTable("Roles").HasKey<int>(r => r.Id);
base.OnModelCreating(modelBuilder);
}

How do i create One-to-One to relationship without having navigation property in dependent entity

I understand the following code creates "One-to-One relationship" between a principal and a dependent entity.
However, I would like to ask:
Is it possible to create one-to-one relationship without including navigation property in the dependent entity?
If yes, than how should I re-write the following code?
public class Student
{
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public StudentReport StudentReport { get; set; }
}
public class StudentReport
{
[Key, ForeignKey("Student")]
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
To create a one-to-one relationship without a navigation property on the dependent side, you'll need to use the fluent API. For example, in your DbContext class, you can override OnModelCreating and use this to define the relationship:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// I'm assuming the report is optional
modelBuilder.Entity<Student>()
.HasOptional(t => t.StudentReport)
.WithRequired();
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
}
See documentation for WithRequired() here