How do I handle situations in which I need pre-existing data before the app is started or right after the database is generated. For example, I have a list of countries in which I'd like to load into the database after code-first generates it. How do I do this?
App is structured as follows:
Repository > Service > WebMVC
The xml is in the WebMVC project.
You create custom initializer, which inherits from DropCreateDatabaseIfModelChanges or DropCreateDatabaseAlways interface. Like:
public class EntitiesContextInitializer : DropCreateDatabaseIfModelChanges<-YourDbContext->
And then you overwrite Seed method like:
protected override void Seed(YourDbContext context)
Whole example might look like:
public class EntitiesContextInitializer : DropCreateDatabaseIfModelChanges<EntitiesContext>
{
protected override void Seed(EntitiesContext context)
{
List<Role> roles = new List<Role>
{
new Role {Id=1, Title="Admin"},
new Role {Id=2, Title="ProjectManager"},
new Role {Id=3, Title="Developer"}
};
// add data into context and save to db
foreach (Role r in roles)
{
context.Roles.Add(r);
}
context.SaveChanges();
}
}
Edit: After setting this up, you have to set up Initializer too, as Ladislav Mrnka mentioned.
Database.SetInitializer(new EntitiesContextInitializer());
ie.: in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Database.SetInitializer(new EntitiesContextInitializer());
}
Don't forget to add using System.Data.Entity;
.....
You must create custom database initializer derived for example from DropCreateDatabaseIfModelChanges and fill data in overriden Seed method. Then you must use Database.SetInitializer to set your new initializer when application starts. Here is example (from CTP5) used to create custom index in the database.
For an example see the new MVC / Entity Framework tutorial series at
http://www.asp.net/entity-framework/tutorials#Using%20MVC
Both #1 and #4 show initializer classes.
Related
I am using separate DB per Tenant. I ran into a scenario where I need to load the settings definitions from the DB. The reason for it is each Tenant can have its own default values. So I want to copy the default values over to the new tenants created by cloning the parent tenant DB and override the default values. However, I also want to update the other fields or add new columns ex: IsVisibleToClients can change per tenant and I cannot have this value in application code but instead want it in the DB
Is this currently supported or a way to handle this
Able to override the settings but looking to save all default values to DB. I believe this can be done by creating seeding scripts.
And new tenants can be creating by cloning this DB and then override the default values. But how can I manage the fields like IsVisibleToClients per tenant and add other custom columns
Creating Custom Setting Value Providers is explained in Abp's own documentation, you can use it.(https://docs.abp.io/en/abp/latest/Settings#custom-setting-value-providers)
public class CustomSettingValueProvider : SettingValueProvider
{
public override string Name => "Custom";
public CustomSettingValueProvider(ISettingStore settingStore)
: base(settingStore)
{
}
public override Task<string> GetOrNullAsync(SettingDefinition setting)
{
/* Return the setting value or null
Use the SettingStore or another data source */
}
}
Configure<AbpSettingOptions>(options =>
{
options.ValueProviders.Add<CustomSettingValueProvider>();
});
I see that EF Core 2 has EF.Functions property EF Core 2.0 Announcement which can be used by EF Core or providers to define methods that map to database functions or operators so that those can be invoked in LINQ queries. It included LIKE method that gets sent to the database.
But I need a different method, SOUNDEX() that is not included. How do I write such a method that passes the function to the database the way DbFunction attribute did in EF6? Or I need to wait for MS to implement it? Essentially, I need to generate something like
SELECT * FROM Customer WHERE SOUNDEX(lastname) = SOUNDEX(#param)
Adding new scalar method to EF.Functions is easy - you simply define extension method on DbFunctions class. However providing SQL translation is hard and requires digging into EFC internals.
However EFC 2.0 also introduces a much simpler approach, explained in Database scalar function mapping section of the New features in EF Core 2.0 documentation topic.
According to that, the easiest would be to add a static method to your DbContext derived class and mark it with DbFunction attribute. E.g.
public class MyDbContext : DbContext
{
// ...
[DbFunction("SOUNDEX")]
public static string Soundex(string s) => throw new Exception();
}
and use something like this:
string param = ...;
MyDbContext db = ...;
var query = db.Customers
.Where(e => MyDbContext.Soundex(e.LastName) == MyDbContext.Soundex(param));
You can declare such static methods in a different class, but then you need to manually register them using HasDbFunction fluent API.
EFC 3.0 has changed this process a little, as per https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#udf-empty-string
Example of adding CHARINDEX in a partial context class:
public partial class MyDbContext
{
[DbFunction("CHARINDEX")]
public static int? CharIndex(string toSearch, string target) => throw new Exception();
partial void OnModelCreatingPartial(
ModelBuilder modelBuilder)
{
modelBuilder
.HasDbFunction(typeof(MyDbContext).GetMethod(nameof(CharIndex)))
.HasTranslation(
args =>
SqlFunctionExpression.Create("CHARINDEX", args, typeof(int?), null));
}
}
I have started an EF6 project to store measurement results from analytical instruments. Each instrument has a built-in PC with and it's own results database.
Initially, the database initializer CreateDatabaseIfNotExists was used. On database creation, it creates an entry in the __MigrationHistory table with a non-unique MigrationId entry (timestamp differs from instrument to instrument, e.g. 201706011336597_InitialCreate), the ContextKey if the fully qualified type of my derived DbContext.
After a while, it was decided to add more result data to the database... Furtunately, only three new tables are required. There are no changes in the existing tables.
For that, I wanted to use the MigrateDatabaseToLatestVersion initializer. But I have to support the following two scenarios:
Existing database with the non-unique MigrationId, that has to be migrated to the extended version with the three new tables.
No database, create the database with the the MigrateDatabaseToLatestVersion initializer.
How can I do this?
I have created an initial migration using the add-migration PM console command from the initial DbContext. That works well with scenario 2 (no database exists). From that starting point I can update my DbContext and create a new migration with the three new tables.
But how to support scenario 1? The Up() method of the initial migration contains the table creation code, that is not nessessary, because the tables already exist. Is an empty migration (add-migration -IgnoreChanges) helpful, maybe with a later timestamp than the initial migration?
Note: I have no access from the PM console to the target database(s), only on my developer machine to a test database.
Thanks and best regards
Karsten
Update:
I have modified the created initial migration with the static flag TablesAlreadyCreated.
public partial class InitialMigraCreate : DbMigration
{
/// <summary>
/// Set this field to true, if the tables are already created by the
/// CreateDatabaseIfNotExists database initializer. Then, the Up()
/// and Down() methods do nothing, but the
/// migration is added to the __MigrationHistory table.
/// </summary>
public static bool TablesAlreadyCreated = false;
public override void Up()
{
if (TablesAlreadyCreated)
return;
// several CreateTable calls here
}
/// <inheritdoc/>
public override void Down()
{
if (TablesAlreadyCreated)
return;
// several Drop... calls here
}
}
I have also implemented a new database initializer class as follows:
public class MigrateDatabaseToLatestVersionEx<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
...
/// <inheritdoc />
public virtual void InitializeDatabase(TContext context)
{
if (context == null)
throw new ArgumentNullException("context");
// check whether a first migration exists from the CreateDatabaseIfNotExists database initializer
var firstConfig = new ConfigurationAutoCreatedDatabase();
firstConfig.TargetDatabase = _config.TargetDatabase;
var firstMigrator = new DbMigrator(firstConfig);
var firstDbMigrations = firstMigrator.GetDatabaseMigrations();
// create the default migrator with the current configuration
var migrator = new DbMigrator(_config);
if (1 == firstDbMigrations.Count(migra => migra.EndsWith("_InitialCreate", StringComparison.InvariantCultureIgnoreCase)))
{ // This database was created using the CreateDatabaseIfNotExists database initializer.
// That's an indication whether it's an old database
// Do the custom migration here!
InitialMigraCreate.TablesAlreadyCreated = true;
migrator.Update();
}
else
{ // do the default migration the database was created with this MigrateDatabaseToLatestVersionEx initializer
InitialMigraCreate.TablesAlreadyCreated = false;
migrator.Update();
}
}
}
It checks, whether the initial migration entry is from the CreateDatabaseIfNotExists initializer and disables the table creation/drop calls in the Up()/Down() methods in that case. ConfigurationAutoCreatedDatabase is a manually created derived DbMigrationsConfiguration class:
internal sealed class ConfigurationAutoCreatedDatabase : DbMigrationsConfiguration<MyNamespace.MyDbContext>
{
/// <summary>
/// Creates a <c>ConfigurationAutoCreated</c> object (default constructor).
/// </summary>
public ConfigurationAutoCreatedDatabase()
{
this.AutomaticMigrationsEnabled = false;
this.AutomaticMigrationDataLossAllowed = false;
this.ContextKey = "MyNamespace.MyDbContext";
}
}
So, it works for both scenarios. I hope that helps other guys with a similar problem. It would be interesting, if there is an out-of-the-box EF workflow for that task.
This is a very common scenario that EF handles nicely. The non-migration initializers (CreateDatabaseIfNotExists, etc) should be used in very early development (when you don't care about the data except for stuff that is seeded).
Once you are switching to migrations you should generate a baseline migration that takes a snapshot of your current model as you indicate (add-migration MyStartPoint -IgnoreChanges). This adds a migration with no Up() code and stores the current state of your code first model so that when you change the model only those changes are reflected. You could accomplish the same thing by commenting out the items that exist from the Up() code.
Now when you run against an existing database, it will check __MigrationHistory to see which migrations have been applied. If the database does not exist, it will be created. See here and here for more info.
Not sure what you are talking about with the MigrationId. EF handles that automatically unless you change your namespace (there is a workaround for that as well).
I am working a project in gui builder..As my project is growing bigger and bigger, i find it hard to search a particular forms and methods all in a statemachine class. so i wanted to create a separate class for each form. but since the gui builder create the methods automatically in statemachine which extends statemachineBase class. how can i use separate class for separate gui forms so that they automatically create methods in the designated class. for instance when i click before show event of form named "NextPage", the gui builder automatically create beforeNextPage method in NextPage class instead of statemachine. I did the followings but lost in the process..
NextPage.class
public class NextPage extends StateMachine {
private ArrayList<Map<String, Object>> mData;
private ArrayList<Map<String, Object>> moreData;
public NextPage(String resFile) {
super(resFile);
}
#Override
public void beforeNextPage(Form f) {
//.........
}
}
Forms generated by GUI Builder cannot be separated from StateMachineBase into different Classes.
What I do personally is create a form in GUI to get the right Look and Feel and then create a replica of that form in code, then delete the one on GUI Builder once I'm satisfied with the code version. It makes my projects well organized and easy to debug.
BeforeShow() would be handle while the form class is loading and to do anything in PostShow(), just do this:
this.addShowListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
removeShowListener(this);
//Your postShow() codes here.
revalidate();
}
});
Forms created in code are light-weight and more customizable than GUI forms.
I am new to ASP.Net MVC . Any help is greatly appreciated in resolving my problem.
I am using a LINQToSQL db in my MVC application. For one of the auto generated partial class (Example MyClass assume for table MyClass) , I created another Partial class as MyClass and added DataAnnotations Like following...
namespcae NP
{
[MetadaType(typeof(myData))]
[Serializable()]
public partial class MyClass
{
}
public myData
{
[Required]
public string ID { get ; set ;}
// Other properties are listed here
}
}
In my controller class example MyHomeController
I have a code as follows:
List<MyClass> list = new List<MyClass>();
list = dbContext.StoredProcedure(null).ToList<MyClass>()
session["data"] = list.
above code works fine if I use inProc session state. But if I use SQLServer mode then I get error as
"Unable to serialize the session state. In 'StateServer' and
'SQLServer' mode, ASP.NET will serialize the session state objects,
and as a result non-serializable objects or MarshalByRef objects are
not permitted. The same restriction applies if similar serialization
is done by the custom session state store in 'Custom' mode. "
Can anyone tell me what I am doing wrong here..?. I can see the data is getting populated in ASPState database tables. By application throws error as follows.
Just mark as Serializable all classes whose instances you want to store in Session.
Finally I was able to resolve the issue.
Solution:
Add the below statement before querying the database. In my case I was calling LinqToSQl context( dbContext).
dbContext.ObjectTrackingEnabled = false;
Sample Code:
List empList = new List();
dbContext.ObjectTrackingEnabled = false;
empList = dbContext.SomeStoredProcedure().ToList()
Session["employee"] = empList.