Remove required attribute - EF Code first - entity-framework

I've got an abstract class where a property "CreatedBy" is required.
This is used for ALMOST every entities but one (UserProfile itself)
How can I remove the Required attribute from the UserProfile without removing it from the other entities inheriting from EntityBase?
public abstract class EntityBase: IEntityBase
{
[Key, Column(Order = 0)]
public Guid? Id { get; set; }
[Key, Column(Order = 1)]
public int Version { get; set; }
[Required]
public DateTime Created { get; set; }
[Required]
public UserProfile CreatedBy { get; set; }
public bool IsDeleted { get; set; }
}
public class UserProfile: EntityBase
{
[Required, Index("Username", 3, IsUnique = true), MaxLength(900)]
public string Username { get; set; }
[Required, Index("Email", 4, IsUnique = true), MaxLength(900)]
public string Email { get; set; }
}
I tried overriding my OnModelCreating, but that doesn't work... Anybody any ideas?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserProfile>().HasOptional(profile => profile.CreatedBy).WithMany();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
Strange thing is, in my database, the columns CreatedBy_Id and CreatedBy_Version can be null in the UserProfile table.
When I seed my UserProfile table, I get a validation error for each of them saying: "The CreatedBy field is required."

So you're actually designing wrong.
Your requirement clearly means you shouldn't inherit from the EntityBase. You shouldn't be trying to force this design onto your requirement.
Instead relax your EntityBase.
public abstract class EntityBase: IEntityBase
{
[Key, Column(Order = 0)]
public Guid? Id { get; set; }
[Key, Column(Order = 1)]
public int Version { get; set; }
[Required]
public DateTime Created { get; set; }
public UserProfile CreatedBy { get; set; }
public bool IsDeleted { get; set; }
}
Problem solved.
This is the correct way to do this. Entities can come from anywhere, they are not always created by a user at all, so it is quite wrong to put a requirement in your design which says "all entities must be made by a user".

The funny thing here is that the database generation logic is overridden by the fluent mapping, but the validation logic isn't. It's not the only area where there is tension between fluent mapping and data annotations.
The first thing that springs to my mind is: make one exception for the UserProfile class and don't let it inherit from EntityBase, and give it the properties it needs, among which the properties also found in EntityBase.
But I bet you have code elsewhere that relies on your classes inheriting the base class or implementing the interface.
The problem here is that RequiredAttribute has Inherited = true in its specification (from its base class, Attribute), so if you override CreatedBy in UserProfile it's still required.
Solution 1 - not good
To solve this problem you could create your own attribute, inheriting RequiredAttribute, and make it Inherited = false:
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false,
Inherited = false)]
public class RequiredInBaseClassAttribute : RequiredAttribute
{
}
Now if you put this attribute in the base class...
[RequiredInBaseClass]
public virtual UserProfile CreatedBy { get; set; }
and override it in UserProfile...
public override UserProfile CreatedBy { get; set; }
it's not required any more in UserProfile. Well, it is. EF seems to trace back the attribute on the base property.
Solution 2
Replace the Required attribute by a CustomValidation attribute that allows CreatedBy to be null when the validated type is a UserProfile:
public abstract class EntityBase: IEntityBase
{
...
[CustomValidation(typeof(EntityBase), "CreatedByIsValid")]
public UserProfile CreatedBy { get; set; }
public static ValidationResult CreatedByIsValid(UserProfile value, ValidationContext context)
{
return value != null || (context.ObjectInstance is UserProfile)
? ValidationResult.Success
: new ValidationResult("CreatedBy is required");
}
}

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...

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.

How to create a self referencing table using code first?

I have an entity that has a reference to itself in a parent - child relationship. I need to find out how to implement this using code first and fluent API. Below is my entity class.
public class MenuItem
{
public int Id { get; set; }
public string LinkText { get; set; }
public string ControllerName { get; set; }
public string ActionName { get; set; }
public MenuItem Parent { get; set; }
public int ParentId { get; set; }
private IList<Role> Roles;
private IList<MenuItem> ChildMenuItems;
public MenuItem()
{
Roles = new List<Role>();
ChildMenuItems = new List<MenuItem>();
}
}
I tried using the below code in my entity configuration.
HasOptional(m => m.Parent)
.WithMany(m => m.ChildMenuItems)
.HasForeignKey(m => m.ParentId)
.WillCascadeOnDelete(false);
but I got this error -
One or more validation errors were detected during model generation:
Vantage.Data.EF.MenuItem_Parent: : Multiplicity conflicts with the
referential constraint in Role 'MenuItem_Parent_Target' in
relationship 'MenuItem_Parent'. Because all of the properties in the
Dependent Role are non-nullable, multiplicity of the Principal Role
must be '1'.
All help appreciated.
Thank You.
ParentId field should be nullable. You are not able to create any record if parentid is required.
Just change public int ParentId { get; set; }
to public int? ParentId { get; set; }

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

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

Table Per Hierarchy & Inherited Relationships

I'm using Entity Framework 5, targeting .Net 4.5. For the life of me I can't figure out what I'm doing wrong that's causing the following error while trying to work with Table Per Hierarchy and Navigation columns:
Invalid column name 'Game_Category'.
Invalid column name 'Game_Value'.
Invalid column name 'Type_Category'.
Invalid column name 'Type_Value'.
Here's the abstract base class (note the composite PK on Category and Value):
[Table("Dictionary")]
public abstract class Lookup
{
[Key, Column(Order = 0)]
[StringLength(50)]
public string Category { get; set; }
[StringLength(100)]
public string ExtendedValue { get; set; }
[Required]
public bool IsActive { get; set; }
[Required]
[StringLength(50)]
public string Key { get; set; }
[Key, Column(Order = 1)]
public int Value { get; set; }
}
Followed by two subclasses that add no additional columns...
public class Game : Lookup {}
public class SetType : Lookup {}
Here's the class with Navigation properties to Game and SetType...
public class CardSet
{
[Required]
[StringLength(10)]
public string Abbreviation { get; set; }
public virtual Game Game { get; set; }
[Required]
public int GameId { get; set; }
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public DateTime ReleaseDate { get; set; }
public virtual Lookup Type { get; set; }
[Required]
public int TypeId { get; set; }
}
From my data context...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Lookup>()
.Map<Game>(l => l.Requires("LookupType").HasValue("Game"))
.Map<SetType>(l => l.Requires("LookupType").HasValue("Set Type"));
base.OnModelCreating(modelBuilder);
}
The lookup table has a discriminator column named LookupType. I've read through several tutorials on table/inheritance. The other two - TPT and TPC using similarly built objects were a cinch. While I understand the errors above - that it's looking for FK columns by convention, I don't understand what I'm doing wrong or missing that's causing it to look for those columns. I've tried placing ForeignKey attributes over the GameId and TypeId properties, but then I get errors about dependent/principal relationship constraints and I'm not sure how to specify the category as an additional foreign key.
I have yet to find a tutorial on table/inheritance that goes over navigation properties as I'm using them. Any help would be greatly appreciated, this has been driving me nuts for over an hour.
Update:
I believe the problem lies in the use of Category as part of the key. The CardSet doesn't have two properties for the category of "Game" for that lookup or the category for "Set Type" for that lookup. I tried creating these properties but that didn't work. Is it possible to set those using the Fluent API? I've made about a dozen attempts so far without any luck.
I think that EF does not "like" the construct modelBuilder.Entity<Lookup>() to map the two sub classes. This should help:
modelBuilder.Entity<Game>()
.Map(l => l.Requires("LookupType").HasValue("Game"));
modelBuilder.Entity<SetType>()
.Map(l => l.Requires("LookupType").HasValue("Set Type"));