In my DbContext class I have the following:
void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(c => { c.HasMaxLength(250); });
}
but also inside the EntityTypeConfiguration class for a specific model I have:
{
Property(p => p.Name).HasMaxLength(500);
}
The problem is that setting the property to 500 inside the Configuration class is not taking effect and EF is still validating the max to be 250.
How can I have a general setting of 250 but override by fluent api as needed inside each class?
#Lutti Coelho: My mistake
Add code below in OnModelCreating method
modelBuilder.Entity<Student>()
.Property(p => p.StudentName)
.MaxLength(500);
If you want to give a string field 500 characters.
You can do this by using the "MaxLength" attribute.
I am creating a sample table like the following.
My Context Configuration.
void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(c => { c.HasMaxLength(250); });
}
I am creating a table named FooEntity.
using System;
using System.ComponentModel.DataAnnotations;
namespace Foo.Model
{
public class FooEntity
{
[MaxLength(500)]
public string FooName { get; set; }
}
}
After adding this attribute, the migration output is as follows.
Even if a 250 character limit is given, it will be passed as 500 characters in SQL.
CreateTable(
"dbo.FooEntity",
c => new
{
FooName = c.String(maxLength: 500)
});
Related
I have created class to configure the corresponding table name in the database with proper name "MyTableInfo" by Table attribute:
using System.ComponentModel.DataAnnotations.Schema;
[Table("MyTableInfo" , Schema = "dbo")]
public class MyTableInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
}
and I use Fluent API in model class for add some constraints:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Answers>()
.HasMany(e => e.MyTableInfo)
.WithRequired(e => e.Answers)
.HasForeignKey(e => e.AnswersId)
.WillCascadeOnDelete(false);
}
But when I try get data from the table I have an exception:
System.Data.SqlClient.SqlException: Invalid object name 'dbo.MyTableInfoes'
however I don't have even any string in my source files with text MyTableInfoes !!!
How to fix this ?
The reason is EF is Generating pluralize name for the tables there is an option to remove that configuration.
In your DB context class, override the following method
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
to have it not pluralize the generated table names.
Reason - using 2 different methods for configure EF - Data Annotations Attributes and Fluent API.
Just delete Table attribute and add the same in Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyTableInfo>().ToTable("MyTableInfo");
As per the default conventions, EF creates a table name matching with property name> + 's' (or 'es') - this is reason why I can't find string MyTableInfoes in my source files ...
I have a nullable varchar(max) column in SQL Server that I'm mapping to a Guid? in EF code-first. However, this property is actually in a base class that many other entities derive from.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>().Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
}
The above line is repeated many times for each table. Is there a way to tell EF that this is a base class property so the mapping can be declared only once?
Sure it is possible. With the lack of custom conventions, it is achieved with the "typical" modelBuilder.Model.GetEntityTypes() loop. Something like this (just change the base class and the property names):
var entityTypes = modelBuilder.Model.GetEntityTypes()
.Where(t => t.ClrType.IsSubclassOf(typeof(BaseClass)));
var valueConverter = new ValueConverter<Guid, string>(
v => v.ToString(), v => (Guid?)Guid.Parse(v));
foreach (var entityType in entityTypes)
entityType.FindProperty(nameof(BaseClass.Property1)).SetValueConverter(valueConverter);
You may also consider using the EF Core provided out of the box Guid to String converter:
var valueConverter = new GuidToStringConverter();
Better to make next calculation property:
[Column("Property1")]
public string Property1Raw { get; set; }
[IgnoreDataMember]
public Guid? Property1
{
get => Guid.TryParse(Property1AsString, out Guid result) ? result : (Guid?)null;
set => Property1Raw = value?.ToString();
}
Another way to do is it to have a matching base class IEntityTypeConfiguration:
internal class EntityConfiguration<T> : IEntityTypeConfiguration<T> where T : Entity
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
// ... Other base-specific config here
}
}
(Assuming here your base class is called Entity - change as needed).
This works better when you use the pattern of factoring out your entity configurations, so yours might be like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new Model1EntityConfiguration());
modelBuilder.ApplyConfiguration(new Model2EntityConfiguration());
// ...
}
...
internal sealed class Model1EntityConfiguration : EntityConfiguration<Model1>
{
public override void Configure(EntityTypeBuilder<Model1> builder)
{
base.Configure(builder); // <-- here's the key bit
// ...; e.g.
builder.Property(c => c.Name).HasMaxLength(80).IsRequired();
}
}
I have the following code first scenario:
public class crmContext : DbContext
{
public crmContext() : base("crmContext")
{
} // end crmContext()
public DbSet<Pool> Pools { get; set; }
public DbSet<Center> Centers { get; set; }
// Po DbSet PoolAssignments?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Pool>()
.HasMany(c => c.Centers)
.WithMany(p => p.Pools)
.Map(
m =>
{
m.ToTable("PoolAssignments");
m.MapLeftKey("poolid");
m.MapRightKey("centerid");
});
} // end OnModelCreating()
} // class crmContext
The PoolAssignments table is created by modelBuilder and I would like to access it the same way I access Pools and Centers. For example:
crmContext db = new crmContext();
Pool pool db.Pools.Find(id);
PoolAssignment pa = db.PoolAssignments.Find(id);
The problem with this approach is that I have not defined PoolAssignment as a class and there is no DbSet PoolAssignments in crmContext. I think I am not understanding this part of Entity Framework very well.
If I define a PoolAssignment class (together with its navigation properties) and a PoolAssignments DbSet, then the modelBuilder code becomes unnecessary. Code first will generate the table for me.
I am simply trying to understand the logic behind using modelBuilder in such a scenario. How can I access the table PoolAssignments and how can I access data with the Entity Framework if I do not define classes? I have searched for an answer but I cannot find any. I have read many articles but none seems to cover this scenario.
I am trying to implement a history table for an entity in EF6, code first.
I figured there would be a way to do this with inheritance. The history table, which is a derived type of the actual table entity, just containing straight copies of all the properties. Along with an edit to the key.
My code first table entity config for Booking.
public class BookingEntityConfiguration
: EntityTypeConfiguration<Booking>
{
public BookingEntityConfiguration()
{
Property(b => b.BookingId).HasColumnOrder(0);
HasKey(b => new { b.BookingId });
HasOptional(b => b.BookingType)
.WithMany()
.HasForeignKey(c => c.BookingTypeId);
}
}
My code first table entity config for BookingHistory.
public class BookingHistoryTypeEntityConfiguration
: EntityTypeConfiguration<BookingHistory>
{
public BookingHistoryTypeEntityConfiguration()
{
Property(b => b.BookingId).HasColumnOrder(0);
Property(b => b.BookingVersion).HasColumnOrder(0);
HasKey(b => new { b.BookingId, b.BookingVersion });
}
}
Where
public class BookingHistory : Booking { }
My BookingHistory table never gets generated in the contexts associated database, which includes these references to the table entities:
public DbSet<Booking> Bookings { get; set; }
public DbSet<BookingHistory> BookingHistories { get; set; }
Is there any simple way to achieve what I want? Which is the derived entity (history table) generates a table that contains the same column fields as the base class entity, but with a change of key.
I appreciate my code above is pretty naive, but I can't seem to find a blog post of similar to help.
The best way is to have a base type from which both the entity and its history entity inherit:
public class BookingsContext : DbContext
{
public DbSet<Booking> Bookings { get; set; }
public DbSet<BookingHistory> BookingHistories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BookingBase>()
.HasKey(p => p.BookingId)
.Property(p => p.BookingId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Booking>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Booking");
});
modelBuilder.Entity<BookingHistory>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BookingHistory");
});
}
}
By ToTable you specify that both entities should be mapped to different tables. On top of that, MapInheritedProperties tells EF to mapp all properties from the base type to this table as well. the result is two completely independent tables that can be addressed by two separate DbSet properties.
I have a problem mapping a relationship
while reverse engineering in visual studio .net
I use inheritance:
public class Quiz : Component
{
public QuizQuestion rootQuestion { get; set; }
public override String getType() { return "quiz"; }
}
the property rootQuestion is not specified in the motherclass, it only exists in the subclass
Quiz doesn't exist as a table in my sqlserver database(only the QuizQuestions and Component table exists in the database, (my teacher told me to do it like this for
the java part of this project).
But I want the subclass Quiz to have a property rootQuestion that refers to quizRootQuestion in my database. So here's what I did:
public class QuizMapper : EntityTypeConfiguration<Quiz>
{
public QuizMapper()
{
this.HasKey(t => t.ComponentID);
this.HasOptional(c => c.rootQuestion)
.WithMany().Map(m => m.MapKey("quizRootQuestionID"));
}
}
public class QuizQuestionMap : EntityTypeConfiguration<QuizQuestion>
{
public QuizQuestionMap()
{
// Properties
// Table & Column Mappings
this.HasKey(t => t.QuestionID);
this.ToTable("QuizQuestions");
this.Property(t => t.QuestionID).HasColumnName("questionID");
}
}
I get this error when i'm trying to run in my browser:
Invalid column name 'rootQuestion_QuestionID'.
Tables in my database:
Component:
componentId
quizRootQuestionID
TypeId(=discriminator)
QuizQuestions
questionID
question
Can someone please tell me what I am doing wrong?
You would get exactly this exception if you forgot to add the QuizMapper to the model builder configuration in OnModelCreating of your derived context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new QuizMapper());
modelBuilder.Configurations.Add(new QuizQuestionMap());
}