How to create Mapping rules for custom entities in EF6? - entity-framework

I am new to Entity framework.I got an opportunity to work EF6 using code first approach .When through some concepts in google for creating mapping rules for custom columns i found one way OnModelCreating()
Is there any other way other than this so that we can create tables in db from code first approach.
If there is way..which is better in what context?

Yes, there is other way to map your classes and it better option. At least, I thonk so. You can create mapper for your model which inherits generic EntityTypeConfiguration and add this mapper OnModelCreating. This way your code will stay clean and its much more easier to manage mappings if you have a lot of models.
Model class:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public int Age { get; set; }
}
Mapper class:
internal class PersonMap
: EntityTypeConfiguration<Person>
{
public PersonMap()
{
// Primary key
this.HasKey(m => m.Id);
// Properties
this.Property(m => m.FullName)
.HasMaxLength(50);
// Table & column mappings
this.ToTable("TABLE_NAME", "SCHEMA_NAME")
this.Property(m => m.Id).HasColumnName("ID");
this.Property(m => m.FullName).HasColumnName("FULL_NAME");
this.Property(m => m.Age).HasColumnName("AGE");
// Relationship mappings
// Map your naviagion properties here if you have any.
}
}
Then you add mapper at OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PersonMap());
base.OnModelCreating(modelBuilder);
}

Related

can you have many to many and one to many without Collection properties on the other end in Entity Framework

this is my current working solution:
public class Person
{
public ICollection<Foo> Foos { get; set; }
public Foo BonusFoo { get; set; }
}
public class Foo
{
public ICollection<Person> Persons { get; set; }
public ICollection<Person> Persons2 { get; set; }
}
public class MemContext : DbContext
{
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(b => b.Foos)
.WithMany(o => o.Persons);
modelBuilder.Entity<Person>()
.HasOne(o => o.BonusFoo)
.WithMany(o => o.Persons2);
base.OnModelCreating(modelBuilder);
}
}
I will never actually need to access in code Foo.Persons or Foo.Persons2, is it possible to have the same configuration but without these 2 properties on the Foo ?
The answer for one-to-many is positive (yes). Remove Persons2 property from Foo and use WithMany w/o passing navigation property (since there isn't):
modelBuilder.Entity<Person>()
.HasOne(o => o.BonusFoo)
.WithMany(); // <--
The answer for many-to-many is negative(no) currently (EF Core 5, 6) as explained at the beginning of the Many-to-many documentation:
Many-to-many relationships require a collection navigation property on both sides.
It will be possible though in the upcoming EF Core 7 - Support unidirectional many-to-many relationships through shadow navigations #3864.
Until then, keep the Person.Foos and Foo.Persons collection properties in the model (even if you "don't need" the later) and the existing fluent configuration.

Entity Framework Core : invalid column name 'UserId1'

I am trying to use Entity Framework Core / .NET 5 to interact with my databases.
When I try to query DbContent.UserClaims I get the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'UserId1'.
I am not sure where UserId1 us coming from. I have a property called UserId which is the foreign key. Here are the relation mapping
Here is what I tried to do in the DbContext class
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>(user =>
{
user.HasKey(r => r.Id);
user.HasMany(x => x.UserRoles).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserTokens).WithOne().HasForeignKey(x => x.UserId);
});
builder.Entity<UserClaim>(userClaim =>
{
userClaim.HasKey(r => r.Id);
userClaim.HasOne(r => r.User).WithOne().HasForeignKey<UserClaim>(x => x.UserId);
});
}
Here is the UserClaim class which is derived from IdentityUserClaim
public class UserClaim : IdentityUserClaim<string>
{
public virtual User User { get; set; }
}
Here is the User class which is derived from IdentityUser
public class User : IdentityUser<string>
{
public virtual ICollection<UserToken> UserTokens { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
public virtual ICollection<UserClaim> UserClaims { get; set; }
}
Here is the query that EF5 is generating
SELECT [u].[Id], [u].[ClaimType], [u].[ClaimValue],[u].[UserId], [u].[UserId1]
FROM [UserClaims] AS [u]
How can I fix this issue in Entity Framework Core?
You're using shadow properties here, and on top of that, trying to add UserId foreign key to the User itself. Since UserId is an already defined property in that class, it's adding a suffix to the property name every time you're trying to add a foreign key in the user table by the same name.
It should be something like this:
modelBuilder.Entity<UserClaim>()
.Property<int>("UserForeignKey");
modelBuilder.Entity<UserClaim>()
.HasOne(a => a.User)
.WithMany(b => b.UserClaims)
.HasForeignKey("UserForeignKey")
Read the documentation on how to configure Fluent API for shadow properties, and some other ways to use the Fluent API.

EF Core Fluent API, set IsRequired on all entities to generate a non-null db column

I'm working on a Razor pages web app which works directly with a db context...yes this is not ideal but is what I'm stuck with for the time being.
In the data model, each object inherits from a base entity class containing audit data, e.g.:
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
...etc.
public class Table1 : BaseEntity
{
public string TestItemName { get; set; }
}
In the database, I want CreatedBy to be required (not null), but I don't want to use the [Required] attribute since this will trigger the UI to validate the CreatedBy column. I don't want to expose this column in the UI and instead have service code which updates all of the audit properties based on Add/Insert.
What I'm looking for is a way via Fluent API which will give me the column type in the db that I need, e.g. NVARCHAR(MAX) NOT NULL.
I can accomplish this in the OnModelCreating method in the dbcontext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Table1>()
.Property(o => o.CreatedBy)
.IsRequired();
However this would require me to create a similar entry for every table in the model.
Is there code I can use in OnModelCreating which could accomplish this for all entities? Something like this (this is just pseudo-code, but looking to give an idea):
var entityTypes = modelBuilder.Model.GetEntityTypes().Select(o => o.GetType()).ToList();
entityTypes.ForEach(e =>
{
e.Property("CreatedBy").IsRequired();
});
Implement your entity configurations in discrete classes that implement IEntityTypeConfiguration. Your implementations should inherit from a base implementation that configures BaseEntity and the Configure method should be virtual with overriding implementations calling the base class' method:
public abstract class BaseEntityConfiguration<TEntity>
: IEntityTypeConfiguration<TEntity>
where TEntity : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.Property(be => be.CreatedBy)
.IsRequired();
// etc
}
}
public class SomeEntityConfiguration : BaseEntityConfiguration<SomeEntity>
{
public override void Configure(EntityTypeBuilder<SomeEntity> builder)
{
// call base class method to configure BaseEntity properties
base.Configure(builder);
// configure remaining SomeEntity-specific properties/etc
builder.TestItemName.IsRequired();
}
}
You'll need to inform the model builder to use your configuration classes. For example, if your config classes are in the same assembly as your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(YourDbContext).Assembly);
}

Equivalent for .HasOptional in Entity Framework Core 1 (EF7)

Consider two classes.
public class File
{
[Key]
public string Id { get; set; }
public string Message_Id { get; set; }
internal Message Message { get; set; }
}
public class Message
{
[Key]
public string Id { get; set; }
}
In EF6, for N : 1..0 relation there was this fluent API.
modelBuilder.Entity<File>()
.HasOptional(e => e.Message ).WithMany().HasForeignKey(e => e.Message_Id);
What is equivalent in Entiity Framework Core 1?
Thank you
You will not find an equivalent method in EF 7. By convention, a property whose CLR type can contain null will be configured as optional. So what decide if the relationship is optional or not is if the FK property is nullable or not respectively.
In summary, due to your Message_Id FK property is string, it already accepts null value, so if you use the following Fluent Api configuration:
modelBuilder.Entity<File>()
.HasOne(s => s.Message)
.WithMany()
.HasForeignKey(e => e.Message_Id)
EF will configure your relationship as optional (or N : 0..1 as requested).
In case of your FK property is value type like int, you should declare it as nullable (int?).
Also I noticed now you have a navigation property with internal access modifier. You should always declare your entity properties as public.
In EF Core you can use two ways for relating two tables:
Inside OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<File>()
.HasOne(c => c.Message)
.WithOne()
.HasForeignKey(c => c.MessageId)
}
Create new class FileConfiguration and calling it inside OnModelCreating:
public class FileConfiguration : IEntityTypeConfiguration<File>
{
public void Configure(EntityTypeBuilder<File> builder)
{
builder.ToTable("File");
// Id
builder.HasKey(c => c.Id);
builder.Property(c => c.Id)
.ValueGeneratedOnAdd();
// Message
builder.HasOne(c => c.Message)
.WithOne(c => c.File)
.HasForeignKey<Message>(c => c.MessageId)
.OnDelete(DeleteBehavior.Restrict);
}
}
and inside OnModelCreating put below codes:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new FileConfiguration());
}
To add to the accepted answer, if your property can't be configured as nullable (for example, if you are dealing with a split-table scenario, where the property is the primary key), adding IsRequired(false), will make the join optional (LEFT).
Like this:
modelBuilder.Entity<File>()
.HasOne(s => s.Message)
.WithMany()
.HasForeignKey(e => e.Message_Id)
.IsRequired(false)

Table-per-Hierarchy and Many to Many relationship

this may seem very easy to solve, but the complication I'm having is that I have a table per hierarchy to store all of the entities and I'm not able to create the relationships over the same table. Here is what I have in the DB and classes:
I have just one table named BaseObject with an ID, name and type. I will create two classes for those entities stored in there. Master and Component. The type column is the discriminator. I have another table to store the relationships between both: A master can have many components and a component also can have many other components.
This is the code I have for the classes:
public partial class BaseObject
{
public BaseObject()
{
}
public System.Guid ID { get; set; }
public string Name { get; set; }
public Nullable<int> Type { get; set; }
}
public class MasterObject : BaseObject
{
public virtual ICollection<ComponentObject> Components { get; set; }
public MasterObject()
{
this.Components = new List<ComponentObject>();
}
}
public class ComponentObject : BaseObject
{
public virtual ICollection<MasterObject> MasterObjects { get; set; }
public ComponentObject()
{
this.MasterObjects = new List<MasterObject>();
}
}
And these are the mappings:
public class BaseObjectMap : EntityTypeConfiguration<BaseObject>
{
public BaseObjectMap()
{
// Primary Key
this.HasKey(t => t.ID);
// Table & Column Mappings
this.ToTable("BaseObject");
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.Name).HasColumnName("Name");
//configure the inheritance in here
this.Map<MasterObject>(m => m.Requires("Type").HasValue(1));
this.Map<ComponentObject>(m => m.Requires("Type").HasValue(2));
}
}
public class MasterObjectMap : EntityTypeConfiguration<MasterObject>
{
public MasterObjectMap()
{
this.HasMany<ComponentObject>(t => t.Components)
.WithMany(t => t.MasterObjects)
.Map(c =>
{
c.ToTable("ObjectComponents");
c.MapLeftKey("ComponentObjectID");
c.MapRightKey("BaseObjectID");
});
}
}
public class ComponentObjectMap : EntityTypeConfiguration<ComponentObject>
{
public ComponentObjectMap()
{
this.HasMany<MasterObject>(t => t.MasterObjects)
.WithMany(t => t.Components)
.Map(m =>
{
m.ToTable("ObjectComponents");
m.MapLeftKey("BaseObjectID");
m.MapRightKey("ComponentObjectID");
});
}
}
The thing is that when quering the DB, I can get a Master by accessing the DBSet Masters, but the Component collection always gives a non-sense exception saying that "Problem in mapping fragments starting at line 6:Condition member 'BaseObject.Type' with a condition other than 'IsNull=False' is mapped. Either remove the condition on BaseObject.Type or remove it from the mapping."
I don't understand what's happening. Of course if the classes where pointing each to a table, this would be very easy, but I suspect that is the root of my problem.
Also, I'm just starting with EF. I wanted to create the classes based on my existing DB that I wouldn't like to modify at all. Unless it's really needed. Please guide me if what I'm trying to do is right or wrong, or what should I do first to fully implement EF on this project that is currently using NHibernate.
Any help here? Thanks
Finally I found the issue thanks to this answer: EF4.1 Exception creating Database with table-per-hierarchy inheritance
The problem is in the mapping of the Base class. When using a base class and a discriminator to get the children classes, the discriminator MUST NOT BE a property in neither instance. Just removed the discriminator as property and it worked fine. In my example, column 'Type' is the discriminator.
Hope this helps to someone else.