Entity Framework 6 seems to be ignoring HasDefaultSchema - c#

I am working on a site where we'd like schema to be the differentiator between projects and in doing so, securing things up between projects.
I have had some luck with this using the HasDefaultSchema method in OnModelCreating in my data context, but also having to make my DbContext implement IDbModelCacheKeyProvider and implementing the CacheKey property. It has worked before. Unfortunately, this solution seems to be inconsistent and I am currently having a problem where I am trying to update my model but getting the following error when running Update-Database:
The specified schema name "dbo" either does not exist or you do not have permission to use it.
The connection string has a user that only has access to my xma schema, so this error makes sense if I had changed the schema, but you can see in the following code, I haven't:
public class DataContext : DbContext, DbModelCacheKeyProvider
{
public DataContext()
//: base("name=DataContext")
: base("DataContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.HasDefaultSchema("xma");
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<Article> Articles { get; set; }
public virtual DbSet<Author> Authors { get; set; }
...
public string CacheKey
{
get { return Utility.SchemaPrefix ?? "xma"; }
}
}
This problem occurs with other team members too, so any help would be much appreciated.
Thanks
Edit: I forgot to mention, this appears to be a problem on a clean database.

I had removed the pending changes to the model, and was able to run update-database -script as advised by Steve, turns out that the initial migrations are based on the dbo schema so changed to a more privileged user and was able to continue with recreating the database. My mistake unfortunately.

Related

The string field is required. even thou there is no Required attribute in Asp.Net Core?

I am building a simple Asp.Net Core app in linux(pop os). I am using VueJs + Aps.Net Core 3.1.101
I am trying to do a POST call to my app and my model is like below:
public class AddConfigurationContextValueApiRequest
{
public int ContextId { get; set; }
[Required(ErrorMessage = "Value is required to continue")]
[StringLength(500, ErrorMessage = "Value can not be longer than 500 characters")]
public string Value { get; set; }
[StringLength(500, ErrorMessage = "Display name can not be longer than 500 characters")]
public string DisplayName { get; set; }
}
As you can see there is not Required attribute for the DisplayName field, but whenever I pass a null value from VueJS app for this field I get The DisplayName field is required..
I am trying to figure out why would AspNet Core complain for this, since there is no Required attribute for such field!
Does anybody know if this intentional ? I tried to remove the StringLength attribute and still it triggers required attribute.
My action is fairly simple:
[HttpPost(UrlPath + "addConfigurationContextValue")]
public async Task AddConfigurationContextValue([FromBody]AddConfigurationContextValueApiRequest request)
{
using var unitOfWork = _unitOfWorkProvider.GetOrCreate();
if (!ModelState.IsValid)
{
//Here it throws because ModelState is invalid
throw new BadRequestException(ModelState.GetErrors());
}
//do stuff
await unitOfWork.CommitAndCheckAsync();
}
I have seen the same issue where the .csproj Nullable setting caused a property that was not marked as [Required] to act as though it were. I took a different approach than changing the Nullable settings in the .csproj file.
In my case it came down to a property that is required by the database; but the model allows null during POST as this particular property is a secret from the user. So I had avoided changing string to string? initially.
Once again, the Fluent API has provided an alternative solution.
Original Property
[JsonIgnore]
[StringLength(15)]
public string MyProperty { get; set; }
Updated Property
[JsonIgnore]
public string? MyProperty { get; set; }
Fluent API Directives (in your DbContext file)
protected override void OnModelCreating(ModelBuilder builder) {
builder.Entity<MyClass>(c => {
c.Property(p => p.MyProperty)
.IsRequired()
.HasMaxLength(15)
.IsFixedLength();
});
}
Apparently .NET 6 Web APIs have the "Nullable" property added by default. I simply had to remove it.
.csproj file:
Edit: As Luke pointed out (without elaborating any further), the above behavior is working as intended and actually makes sense. If your JSON has a null value, your code might crash, if not handled. Throwing a compile time error is impossible, since the JSON is unknown. Forcing yourself to using nullable reference types makes the code more resilient.
I haven't checked yet, how swagger behaves, the default config surely doesn't care about a .NET 6 setting and will no longer flag mandatory fields, if you leave out the [Required] attribute and use only nullable reference types to point out what is mandatory and what isn't.
After #devNull's suggestion I found out that somehow while I was playing around with Rider IDE it seems it switched that feature on!
There is an option in rider that allows to change that configuration on project level:
If somebody has the same problem: right click on the project level, go to properties, Application and there you can see this configuration.
Thank you #devNull for the help :)

Migration from two WebApp with use MigrateDatabaseToLatestVersion

I have two WebApp.
Both refer to the same database, and the need to organize the migration of both App.
Both application setup to MigrateDatabaseToLatestVersion.
When you start with a blank database application first comes the creation of scheme, in table __MigrationHistory create record about this, and everything is OK.
But when you start the second application should make changes to existing database tables, but the application crashes with an error - "... This table already exists"
How to solve this problem?
Example of code second app
Global.asax
Database.SetInitializer(new SyncContextInitializer());
using (var context = new SyncDataContext())
{
context.Database.Initialize(force: true);
}
public class SyncContextInitializer : MigrateDatabaseToLatestVersion<SyncDataContext, SyncConfiguration>
{ }
public class SyncDataContext : DataContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new IdentityUserLoginMap());
.......
base.OnModelCreating(modelBuilder);
}
}
public sealed class SyncConfiguration : DbMigrationsConfiguration<SyncDataContext>
{
private readonly bool _pendingMigrations;
public SyncConfiguration()
{
AutomaticMigrationDataLossAllowed = true;
AutomaticMigrationsEnabled = true;
var migrator = new DbMigrator(this);
_pendingMigrations = migrator.GetDatabaseMigrations().Any();
}
......
.....
}
That's not going to work. When you modify the model in one app, it looks at the prior migration (in code) to compare. It will try to recreate the changes made in the other app or you will get a model mismatch error.
The best solution is to maintain the models in one application. Otherwise you would have to create a migration in the other application to keep it in sync:
// Application 1:
add-migration MyChanges
update-database
// Application 2:
add-migration SyncMyChanges -IgnoreChanges // Just update meta model
update-database
You would need to take care that there were no pending changes in App 2. See Entity Framework Under the Hood
If the models are not shared, you may just need to create an initial migration starting point in both apps:
add-migration Initial -IgnoreChanges

Replacing DbContext in Microsoft MVC Individual User Accounts Template

I just created my first MVC Website.
To avoid having to program the controller myself, I am using the Individual User Accounts template from Microsoft.
I know, that this template uses the Entity Framework to create an express database to persist the user/account data.
Since I already have a database, which I want to use, I want to change the
template so it uses the DbContext for said database.
I was able to change the connectionString, so that the tables of the template got created in my database. But I don't want it to create it's own tables but use my already created tables.
Is there any easy way to achieve this?
Or should I just write the whole account/user controller from scratch myself?
// This is an example of DbContext class it implements DbContext
// If you do not use constructor(s) then the expectation by entity framework
// will be that your name of your connectionstring in web.config
// or app.config is name name as your class so e.g. "YourContext",
// otherwise "Name="YourConnectionString"
public class YourContext : DbContext
{
// constructor as you wish /want
public YourContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{ }
// critical mapping
public DbSet<someModel> someModel { get; set; }
// critical overide
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// critical key to NOT let your database get dropped or created etc...
Database.SetInitializer<YourContext>(null);
// This is an example of mapping model to table
// and also showing use of a schema ( dbo or another )
modelBuilder.Entity<someModel>().ToTable("someTable", schemaName: "dbo");
}
}

Moving Entity framework to another project from MVC causes re-migration

I currently have an asp.net MVC 4 application which contains Entity framework 6 Code First models, DbContext and Migrations. In an attempt to separate this from my web application so I can re-use these database classes in another project I have moved all related Entity Framework classes to their own project.
However now when I run the solution it thinks my model has changed and attempts to run all my migrations once more. The problem appears to be in my use of SetInitializer as if I comment out this line I can run the web application as per normal.
public static class DatabaseConfig
{
public static void Initialize()
{
System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<G5DataContext, Configuration>());
// make sure the database is created before SimpleMembership is initialised
using (var db = new G5DataContext())
db.Database.Initialize(true);
}
}
This wasn't a problem until I've tried to move all the Entity Framework classes. Is this not possible, or have I done something fundamentally wrong?
At startup, EF6 queries exiting migrations in your database, as stored in the __MigrationHistory table. Part of this table is a context key, which includes the namespace of the entities.
If you move everything to a new namespace, EF6 doesn't recognize any of the previously run migrations, and tries to rebuild the database.
A quick solution is to run a script to rename the context key in the __MigrationHistory table to your new namespace. From http://jameschambers.com/2014/02/changing-the-namespace-with-entity-framework-6-0-code-first-databases/ :
UPDATE [dbo].[__MigrationHistory]
SET [ContextKey] = 'New_Namespace.Migrations.Configuration'
WHERE [ContextKey] = 'Old_Namespace.Migrations.Configuration'
Would also like to add that you should remember to change the ContextKey property in your Configuration class. I did the above but it was still trying to create a new database. Here's an example:
Before:
internal sealed class Configuration : DbMigrationsConfiguration<PricedNotesContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "Synapse.DAL.PricedNotesContext";
}
protected override void Seed(PricedNotesContext context)
{
}
}
After:
internal sealed class Configuration : DbMigrationsConfiguration<PricedNotesContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "SynapseDomain.DAL.PricedNotesContext";
}
protected override void Seed(PricedNotesContext context)
{
}
}
Hope this helps anyone who is stuck on this. It's a shame that it shouldn't be easier...

Entity Framework using IdentityDbContext with Code First Automatic Migrations table location and schema?

I am trying to setup automatic migration updates using the IdentityDbContext class and propagating changes to the actual DbContext for the entire database.
Before I get into the code, on my implementation of the IdentityDbContext with automatic migrations I get this error:
Automatic migrations that affect the location of the migrations history system table (such as default schema changes) are not
supported. Please use code-based migrations for operations that affect
the location of the migrations history system table.
I am not going to post the models that are associated with the migrations and context code unless someone finds them of use.
Implemenation of the IdentityDbContext:
public class SecurityContext: IdentityDbContext<User>
{
public SecurityContext() : base("MyActualContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//removing this line I do not get an error, but everything gets placed into the dbo schema. Keeping this line, i get the above error.
modelBuilder.HasDefaultSchema("ft");
}
}
So I tried adding this class to place the migrations history into the correct schema. This, in fact, does move the migrations history into the correct schema, but everything else remains in the dbo schema.
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("ft");
}
}
public class SecurityContextMigrations : DbMigrationsConfiguration<SecurityContext>
{
public SecurityContextMigrations()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
//When the migrations get set, I use the new class created to move the migrations to the correct schema.
SetHistoryContextFactory("System.Data.SqlClient", (c, s) => new MyHistoryContext(c, s));
}
protected override void Seed(SecurityContext context)
{
...
}
}
Ideally, I'd like everything to be in the ft schema. I don't think the migrations are that complex that I need to manually setup the migrations. I was hoping for simplicity sake, I could use automatic migrations for this. I am wondering if this approach is impossible and what I need to do to make this happen and any changes made to the models do get propagated.
I have a similar issue with Oracle 12c and EF6: I cannot get automatic migrations to work. I found, however, the following partial success factors: - I needed to set
modelBuilder.HasDefaultSchema("")
on my DbContext in order to get the runtime see the tables in the logon schema of the particular user - For the update-database it was necessary to set the MyHistoryContext parameters like that:
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("L2SRVRIZ");
}
}
NOTE: You need to hard-code the schema name there. In this way, update-database does not try to use dbo as schema (but still no automatic migrations are possible, they will drop your MigrationHistory table and mess up everything). This is in my opinion a nasty bug inside either EF6 or the Oracle custom class. As I have no maintenance contract with them, I cannot file a ticket.
For your case, I think its not possible somehow by design to avoid the error message with automatic migrations. EF6 thinks, for some reason, that if you use a custom schema name, that you actually moving the __MigrationHistory table from the default dbo schema, which is of course not true.
Or, did you find a solution for that?
BR Florian

Categories

Resources