Convention for DatabaseGeneratedOption.None - entity-framework

I would like to manually insert id for all my entities.
Is it possible to create some kind of convention or I have to set HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) (or add DatabaseGenerated attribute) for every entity ?
EDIT
There is a simpler way to do this then using accepted answer:
modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();

It's possible to create custom conventions in EntityFramework 6 as follows:
Create a convention class
public class ManualIdentityConvention : Convention
{
public ManualIdentityConvention()
{
this.Properties<int>()
.Where(p => p.Name.EndsWith("Id"))
.Configure(p => p.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
}
}
Add the convention to your DbContext
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new ManualIdentityConvention());
}
}
EntityFramework 5
As for EntityFramework 5, I believe something similar can be achieved, but not via a convention class:
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(x => x.Name.EndsWith("Id"))
.Configure(x => x.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
}
}
In either case, neither approach is particularly surgical, but one could conceivably tighten up the convention by being more specific in one's where clause.
Does this help?

Related

ASP.Net.Core 2.2 Identity change ID from string to int

I'm trying to change the ID of the Users table from a string (GUID) to an int and really struggling. I have looked at lots of examples but they seem to be for earlier versions of Identity or vs and for numerous reasons they don't work.
I either get a compiler error
'Microsoft.AspNetCore.Identity.IdentityUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore 6[TUser,TContext,TKey,TUserClaim,TUserLogin,TUserToken]' violates the constraint of type 'TUser'.
Or when I create the migration I still get a string column for the ID not an int as I was expecting.
I'm using vs2019. Asp.Net.Core 2.2 and Microsoft.AspNetCore.Identity 2.2
Can anyone help me out, please? Thanks!
First extend the IdenityUser class class as follows so that you can add custom properties:
public class ApplicationUser : IdentityUser<int>
{
}
Then extend the IdentityRole class follows if you use Role in application too. You can keep this safely even you don't want to use it:
public class ApplicationRole : IdentityRole<int>
{
}
Now your ApplicationDbContext should be as follows:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Now in your ConfigureServices method of the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
}
Job done! Now run a brand new migration and apply it.
#TanvirArjel solution also worked with .NET Core 3.1 in my project with modifications. I had added Identity through scaffolding.
Instead of editing ConfigureServices(), I edited IdentityHostingStartup.cs instead and mofified Configure() inside IdentityHostingStartup.cs:
builder.ConfigureServices((context, services) =>
{
services.AddDbContext(options => options.UseSqlServer(context.Configuration.GetConnectionString("IdentityDbContextConnection")));
services.AddIdentity()
.AddEntityFrameworkStores()
.AddDefaultTokenProviders()
.AddDefaultUI(); //add default razor pages for identity (login, register, etc)
});

Entity Types cannot share table | Not actually the same table though

I have two databases that so happen to have the same table name. Entity is giving me an InvalidOperationException stating "The entity types 'PmsUser' and 'AcctUser' cannot share table 'NGUSERS'...
I created these using a code first approach.
My models for both look something like
[Table("NGUSERS")]
public partial class AcctUser
{
...
and
[Table("NGUSERS")]
public partial class PmsUser
{
...
They each have their own respective connection string to differing databases. What do I need to do to get it to allow the same table name (modifying the Database isn't an option)?
Use two ModelBuilder instead of attributes. Then pass the created DbModel to the DbConnection. Or set the database name in the DbContext. See Entity Framework Connections and Models.
This base class helps to register the model builders with the DbContext:
public abstract class ModelBuilderBase {
protected readonly DbModelBuilder ModelBuilder;
protected ModelBuilderBase(DbModelBuilder modelBuilder) {
ModelBuilder = modelBuilder;
}
public abstract void BuildModel();
}
ModelBuilder implementation:
public class ModelBuilder1 : ModelBuilderBase {
public ModelBuilder1 (DbModelBuilder modelBuilder) : base(modelBuilder)
{ }
public override void BuildModel() {
ModelBuilder.Entity<AcctUser>().ToTable("NGUSERS");
}
}
DbContext:
public class DbContext1 : DbContext {
public DbContext1() : base("Database1") /* set DB name */ {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var modelBuilders = new List<ModelBuilderBase> {
new ModelBuilder1(modelBuilder)
};
modelBuilders.ToList().ForEach(x => x.BuildModel());
}
}
Create ModelBuilder2 and DbContext2 for the second DB/Table.

Adding new models to an existing context at runtime in entity framework

Is it posible to have a dbContext in a dll, import it in another project and add new models/entities/tables programatically so you can make joins with previously existing tables?
I'll try to explain why I want such a thing (may be there is a better way).
I want to make a modular app, so there will be a Core.dll with a dbContext. And I want others making modules, to be able to extend the Core dbContext.
Thanks.
Sounds like you basically want to do what EF already does, if I'm understanding correctly. There's at least a couple ways to do this using code first. I like using the EntityTypeConfiguration approach (rather than fluent API) to build up my object model. In your case, that might look something like this:
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using CoreNamespace;
namespace CoreNamespace
{
public class MyDbContext:DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
modelBuilder.Configurations.Add(new CoreEntityConfiguration());
}
}
public class CoreEntity
{
//list properties, methods, etc
}
public class CoreEntityConfiguration : EntityTypeConfiguration<CoreEntity>
{
public CoreEntityConfiguration()
{
//configure entity and map to db
}
}
namespace ExtendingProjectNamespace
{
public class MyExtendedDbContext : MyDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new SubEntityConfiguration());
}
}
public class SubEntity
{
//list properties, methods, etc
}
public class SubEntityConfiguration : EntityTypeConfiguration<SubEntity>
{
public SubEntityConfiguration()
{
//configure entity and map to db
}
}
}
The namespaces here are intended to refer to separate projects/dlls

Cannot find plural form of table

For some reason code-first EF7 (vNext) will not use/find the plural form of my table. I have tried adding the table attribute to the model but it does not solve the problem.
[Table("Units")]
public class Unit
If I name the table Unit then no problem. If I name the table Units then it's not found.
What am I doing wrong or missing?
Thank you.
This is how I resolved:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Unit>().ToTable("Units");
}
For Entity Framework 7 beta1, I solved this issue by this way:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Unit>().ForRelational(rb =>
{
rb.Table("Units");
});
}
Entity Framework 7 is being configured using the Fluent API. I created an extension method which maps the table names to their plural form, with the intention to reproduce the EF 6 behavior and to be able to use my existing database while working with EF7.
public static class ModelBuilderExtensions
{
public static void PluralizeNames(this ModelBuilder modelBuilder)
{
var types = modelBuilder.Model.EntityTypes;
foreach (var type in types.Where(type => type.ClrType != null))
{
modelBuilder.Entity(type.ClrType)
.ForRelational()
.Table(type.ClrType.Name.Split('`')[0].Pluralize());
};
}
}
Please note the .Pluralize() extension method. This may be Humanizer you're using or any other extension method which pluralizes your string. (I shamelessly copied https://github.com/srkirkland/Inflector/blob/master/Inflector/Inflector.cs in my project to be able to compile my project with the DNX Core.)
The .Split() part is to deal with the type.ClrType.Name which can output stuff like IdentityUserRole`1.
You can use it like this in your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.PluralizeNames();
}
Ps; this works for me
Now the ToTable and ForRelational both are missing in beta5 of EF7. So I used the below code.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Role>().ForSqlServer().Table("Role");
}
You need to add "Microsoft.EntityFrameworkCore.Relational" in your project.json and restore your package.
.NET core is broken down in to small pieces for less memory foot print. So you need to explicitly tell what you want.

How do I singularize my tables in EF Code First?

I prefer using singular nouns when naming my database tables. In EF code first however, the generated tables always are plural. My DbSets are pluralized which I believe is where EF is generating the names but I don't want to singularize these names as I believe it is more pratical to have them plural in code. I also tried overriding the setting but to no avail.
Any ideas? Here is my code and thanks.
MyObjectContext.cs
public class MyObjectContext : DbContext, IDbContext
{
public MyObjectContext(string connString) : base(connString)
{
}
public DbSet<Product> Products {get;set;}
public DbSet<Category> Categories {get;set;}
//etc.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>();
}
}
You've removed the wrong convention (PluralizingEntitySetNameConvention) for this purpose. Just replace your OnModelCreating method with the below and you will be good to go.
using System.Data.Entity.ModelConfiguration.Conventions.Edm.Db;
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
With Entity Framework 6, on your file that inherit from DbContext:
using System.Data.Entity.ModelConfiguration.Conventions;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
You can also change the property value:
On the Tools menu, click Options.
In the Options dialog box, expand Database Tools.
Click O/R Designer.
Set Pluralization of names to Enabled = False to set the O/R Designer so that it does not change class names.
Set Pluralization of names to Enabled = True to apply pluralization rules to the class names of objects added to the O/R Designer.
The location of the definition of PluralizingTableNameConvention has moved to:
using System.Data.Entity.ModelConfiguration.Conventions;