I'm using EF5 Code First, I have entities named XXXEntity which is based on Entity class, in Entity class there is Id property.
Here is the problem, normaly EF will create table for each entity named XXXEntities and having Id field. What I want is the table should named XXX (without Entities) and the Id should be XXXId. How to do that at once by using convention. I know I can override the table name, and Id name one by one. But it is a bit boring and not reusable, is there any better way to do that using convention or something on EF 5?
UPDATE
i read about custom code first conventions but not sure is this an out dated page or a non implemented feature. because i couldn't found the Properties<T>() method on EF 5
No, you can't do that with EF 5, your link is a future feature for EF 6 see here
but you can do that with reflection easily, for reusability you can make it as an extension method of DbModelBuilder, a bit slow but it solve your case. here what you can do:
public static class MyCustomNamingConvention
{
public static void ToMyDatabaseNamingConvention(
this DbModelBuilder mb, IEnumerable<Type> entities)
{
foreach (var entity in entities)
{
var mi = typeof(MyCustomNamingConvention)
.GetMethod("MyNamingConvention")
.MakeGenericMethod(entity);
mi.Invoke(null, new object[] { mb });
}
}
public static void MyNamingConvention<T>
(DbModelBuilder mb) where T : Entity
{
var tableName = typeof(T).Name.Replace("Entity", "");
mb.Entity<T>().HasKey(x => x.Id);
mb.Entity<T>().Property(x => x.Id).HasColumnName(tableName + "Id");
mb.Entity<T>().ToTable(tableName);
}
}
Simply use it on your DBContext on OnModelCreating method
protected override void OnModelCreating(DbModelBuilder mb)
{
base.OnModelCreating(mb);
mb.ToMyDatabaseNamingConvention(GetEntities());
}
Note:
Remember to add appropriate namespace of the
MyCustomNamingConvention.
GetEntities() is a method to iterate your entity (remember to skip
the Entity base class)
Use this to remove table pluralizing:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
I cant seem to find a convention that you can disabled for the column Id name. Have a look at http://msdn.microsoft.com/en-us/library/gg696316(v=vs.103).aspx
Related
In my EF Core 5 project I use a polymorphic base class for many entities in different databases, thus I need to manually add foreign keys
public abstract class TrackedEntity
{
public DateTimeOffset LastUpdated { get; set; }
public Guid LastUpdatedBy { get; set; }
//...
}
Currently I do this for each entity as in
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DashboardTemplate>()
.HasOne<User>()
.WithMany()
.HasForeignKey(tracked => tracked.LastUpdatedBy);
I recently came across Jon P Smith's excellent little library EfCore.SoftDeleteServices [http://mng.bz/op7r] that gave me the idea that I could considerably tidy this up, by automating with something like
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(TrackedEntity).IsAssignableFrom(entityType.ClrType))
{
entityType.AddForeignKey(...);
}
}
Unfortunately I cannot find any documentation on how I would go about calling AddForeignKey()
AddForeignKey method expects 3 arguments (all in terms of EF Core metadata) - the property (or properties) of the dependent entity type defining the foreign key and the principal entity key and type.
In your case, first is the LastUpdatedBy property of the entity and next are the User entity primary key and type. So the usage would be something like this:
var principalEntityType = modelBuilder.Model.FindEntityType(typeof(User));
var principalKey = principalEntityType.FindPrimaryKey();
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(TrackedEntity).IsAssignableFrom(entityType.ClrType))
{
var property = entityType.FindProperty(nameof(TrackedEntity.LastUpdatedBy));
entityType.AddForeignKey(property, principalKey, principalEntityType);
}
}
we have regularly changing Database Data(every two weeks to once a month).
Usually the latest data has to be used, but in some special cases older data is necessary.
The current info which version has to be used atm is stored in another table.
The Database looks like this, versioned Schema-Names with the same tables beneath it.
YYYYMMDD+Revision
myshema_202001011
table1
myshema_202002011 and so on
table1
myshema_202003011 and so on
table1
I have build a Aspnet core (2.2) service with two DbContext classes,
one for the static schemas that gets the current version to use and one for the changing schemas that accesses those data.
The static DbContext works just fine.
The problem is, even when i use the changing contaxt with a using like,
using (var _context = new ChangingDbContext()){}
the constructors and OnConfiguring are executed each time but the OnModelCreating method is only executed once.
This leads to NOT updating to the current schemas.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity<my_table>(entity =>
{
entity.HasKey(e => e.key_adr);
entity.ToTable("mytable", $"myshema{mySchemaVersion}");
});
}
Has anyone a clue how to get a "really" new context where OnModelCreating is executed every time?
Or maybe another solution how to handle those changing Schemas?
To continue from my comment. The below db table design allows you or users add as many as new fields to an object as they want. And I think it gives most flexible structure.
Let's assume in a eCommerce system, we provide 3 fields (Name, Code, Price) for the product. But we also allow users want to add their custom fields to their products (e.g. Promotion1Price, Promotion2Price, Discount, ...)
PRODUCT (ProductId, Name, Code, Price)
CUSTOMEFIELD (FieldId, FieldName, FieldType)
PRODUCT_CUSTOMFIELD (ProductId, FieldId, FieldValue)
Let me know if this doesn't serve your purpose right.
Solved by this Answer https://stackoverflow.com/a/41985226/6692289
Quote from Example in case it gets deleted.
Derived DbContext that replaces it's ModelCacheKey (and factory) with
a Custom one.
class MyDbContext : DbContext
{
public MyDbContext(string schema)
{
Schema = schema;
}
public string Schema { get; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseSqlServer("...")
.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema(Schema);
// ...
}
}
The factory that creates the Context with a specific key.
class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create(DbContext context)
=> new MyModelCacheKey(context);
}
The custom ModelCacheKey per context.
class MyModelCacheKey : ModelCacheKey
{
string _schema;
public MyModelCacheKey(DbContext context)
: base(context)
{
_schema = (context as MyDbContext)?.Schema;
}
protected override bool Equals(ModelCacheKey other)
=> base.Equals(other)
&& (other as MyModelCacheKey)?._schema == _schema;
public override int GetHashCode()
{
var hashCode = base.GetHashCode() * 397;
if (_schema != null)
{
hashCode ^= _schema.GetHashCode();
}
return hashCode;
}
}
And using the Context like.
using (var _myContext = new MyDbContext(_schemaNameToUse)
{
}
I am using entity framework core in a normal .net project, version 4.7. I know I can do this. The problem is that I can't seem to map an entity to a table because the "ToTable" method doesn't exist. I can't edit the poco or entity classes because they are predefined and generated. So I can't use the attribute. I looked on the internet and everyone seems to use this method to map an entity to a table.
Here is my code:
public class FactsDbContext : DbContext
{
public DbSet<TblIncident> TblIncidents { get; set; }
public DbSet<TblAction> TblActions { get; set; }
public DbSet<TblAddressTypeAlias> TblAddressTypeAliases { get; set; }
public DbSet<TblCountry> TblCountries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//these methods don't exist in my case
modelBuilder.Entity<TblIncident>(entity => entity.ToTable("Incident"));
modelBuilder.Entity<TblIncident>().ToTable("Incident");
}
}
I also tried to use IEntityTypeConfiguration with a EntityTypeBuilder but it still don't have access to the map to table method:
public class IncidentConfig : IEntityTypeConfiguration<TblIncident>
{
public void Configure(EntityTypeBuilder<TblIncident> builder)
{
builder.ToTable("Incident");
}
}
I looked into the Entity Framework Core repository on GitHub and searched for the method "Totable" inside the repository. It turns out it is defined as an extension method but it is in separate nuget package and library called Microsoft.EntityFrameworkCore.SqlServer
After I downloaded the package I got the Totable method that I need. Still it doesn't make sense to add that method in a separate package for sql server when you already have the "Table" attribute that you can add on entities directly in the entity framework core package.
You can use the below approach. You have to use Table data annotation.
DBContext:
public virtual DbSet<Article> Article { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Article>(b =>
{
b.Property(e => e.Property1).HasDefaultValue(true);
... //Other properties
}
Model class:
[Table("Article")]
public class Article
{
You can also use to ToTable in DBContext, but you have to make sure that you have included using Microsoft.EntityFrameworkCore;.
Line modelBuilder.Entity<TblIncident>().ToTable("Incident"); looks correct according to the documentation.
https://learn.microsoft.com/en-us/ef/core/modeling/relational/tables#fluent-api
It's very old thread but I got the same issue and I solved it by placing base.OnModelCreating(builder) as a first line of OnModelCreating method.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Rest of the code
}
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 new with Entity Framework 5. Our team is using Code First workflow.
Before I'll start with my main question, let me first show you what I have tried (the ultimate comment of all time :D).
public class MyDBContext : CDBContext
{
public MyDBContext() : base(connString) { }
public MyDBContext(string connStr) : base(connStr) { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// removes some conventions
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
// ........
// model configurations which contains mappings
modelBuilder.Configurations.Add(new AccountConfiguration());
// ........
// calls base OnModelCreating
base.OnModelCreating(modelBuilder);
}
// list of all Entity
public DbSet<Account> Account { get; set; }
}
MyDBContext is the class I have created that inherits from CBDContext that contains override methods and which also inherits from DBContext. One of the problems I have encountered is that entity framework doesn't handle field uniqueness. I have already read the article on Configuring/Mapping Properties and Types with the Fluent API on their site and I can't find any configuration to set a property into unique.
So what I did in order to set the field unique is to manually run several ALTER sql statements during creation,
using (MyDBContext _context = new MyDBContext(connString))
{
if (_context.Database.CreateIfNotExists())
{
_context.Database.ExecuteSqlCommand("ALTER TABLE Account ADD CONSTRAINT UQ_Account_AccountNumber UNIQUE(AccountNumber)");
_context.Database.ExecuteSqlCommand("ALTER TABLE Account ADD CONSTRAINT UQ_Account_GUID UNIQUE(GUID)");
// .... more on this in the following lines ...
}
}
My Questions:
Am I right that entity framework don't have any configuration or data annotations to set the field unique?
Is there a way to detect or know during runtime if EF creates a database or not so I can move or hide this statement if (_context.Database.CreateIfNotExists()) somewhere to an available method that can be overriden?
What I really want is to remove if (_context.Database.CreateIfNotExists()) from the using statemnt and put it somewhere else or inside MyDBContext so my code will look like this,
using (MyDBContext _context = new MyDBContext(connString))
{
Account _acc = new Account()
// ...Account properties ...
_context.Account.Add(_acc);
_context.SaveChanges();
}
Thanks.
You should take a look at Code First Migrations, more specific at the Data Motion / Custom SQL and later sections - this is might the way to achieve your desired result. Your migration class can look like this:
public partial class AddUniqueConstrains : DbMigration
{
public override void Up()
{
Sql("ALTER TABLE Account ADD CONSTRAINT UQ_Account_AccountNumber UNIQUE(AccountNumber)");
Sql("ALTER TABLE Account ADD CONSTRAINT UQ_Account_GUID UNIQUE(GUID)");
}
public override void Down()
{
Sql("ALTER TABLE Account DROP CONSTRAINT UQ_Account_AccountNumber UNIQUE");
Sql("ALTER TABLE Account DROP CONSTRAINT UQ_Account_GUID");
}
}
You can also explore other options described in answers to this question: Unique Constraint in Entity Framework Code First
If you don't use (or cannot use) EF migrations you can use custom initializer as mentioned in this answer. The custom initializer will execute a Seed method after creating the database = only once when database doesn't exist. If you need to incrementally develop the database initializer itself will not help you (that is what migrations are for).