mirror of https://github.com/abpframework/abp.git
50 changed files with 580 additions and 227 deletions
@ -0,0 +1,90 @@ |
|||
# Switch to another DBMS for Entity Framework Core |
|||
|
|||
**[The application startup template](Startup-Templates/Application.md)** comes with SQL Server provider pre-configured for the Entity Framework Core. Entity Framework Core supports [many other DBMSs](https://docs.microsoft.com/en-us/ef/core/providers/) and you can use any of them with your ABP based applications. |
|||
|
|||
ABP framework provides integration packages for some common DBMSs to make the configuration a bit easier (see the [entity framework core document](Entity-Framework-Core.md) for a list of available integration packages). However, you can configure your DBMS provider without these integration packages. |
|||
|
|||
While using the integration package is always recommended (it also makes standard for the depended version across different modules), you can do it manually if there is no integration package for your DBMS provider. |
|||
|
|||
This document explains how to switch to MySQL without using [the MySQL integration package](Entity-Framework-Core-MySQL.md). |
|||
|
|||
## Replace the SQL Server Dependency |
|||
|
|||
* Remove the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package dependency from the `.EntityFrameworkCore` project. |
|||
* Add the [Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql/) NuGet package dependency to your `.EntityFrameworkCore` project. |
|||
|
|||
## Remove the Module Dependency |
|||
|
|||
Remove the `AbpEntityFrameworkCoreSqlServerModule` from the dependency list of your ***YourProjectName*EntityFrameworkCoreModule** class. |
|||
|
|||
## Change the UseSqlServer() Calls |
|||
|
|||
Find the following code part inside the *YourProjectName*EntityFrameworkCoreModule class: |
|||
|
|||
````csharp |
|||
Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.UseSqlServer(); |
|||
}); |
|||
```` |
|||
|
|||
Replace it with the following code part: |
|||
|
|||
````csharp |
|||
Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.Configure(ctx => |
|||
{ |
|||
if (ctx.ExistingConnection != null) |
|||
{ |
|||
ctx.DbContextOptions.UseMySql(ctx.ExistingConnection); |
|||
} |
|||
else |
|||
{ |
|||
ctx.DbContextOptions.UseMySql(ctx.ConnectionString); |
|||
} |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
* `UseMySql` calls in this code is defined by the Pomelo.EntityFrameworkCore.MySql package and you can use its additional options if you need. |
|||
* This code first checks if there is an existing (active) connection to the same database in the current request and reuses it if possible. This allows to share a single transaction among different DbContext types. ABP handles the rest of the things. |
|||
* It uses `ctx.ConnectionString` and passes to the `UseMySql` if there is no active connection (which will cause to create a new database connection). Using the `ctx.ConnectionString` is important here. Don't pass a static connection string (or a connection string from a configuration). Because ABP [dynamically determines the correct connection string](Connection-Strings.md) in a multi-database or [multi-tenant](Multi-Tenancy.md) environment. |
|||
|
|||
## Change the Connection Strings |
|||
|
|||
MySQL connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/mysql/ ) for details of MySQL connection string options. |
|||
|
|||
You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. |
|||
|
|||
## Change the Migrations DbContext |
|||
|
|||
MySQL DBMS has some slight differences than the SQL Server. Some module database mapping configuration (especially the field lengths) causes problems with MySQL. For example, some of the the [IdentityServer module](Modules/IdentityServer.md) tables has such problems and it provides an option to configure the fields based on your DBMS. |
|||
|
|||
The startup template contains a *YourProjectName*MigrationsDbContext which is responsible to maintain and migrate the database schema. This DbContext basically calls extension methods of the depended modules to configure their database tables. |
|||
|
|||
Open the *YourProjectName*MigrationsDbContext and change the `builder.ConfigureIdentityServer();` line as shown below: |
|||
|
|||
````csharp |
|||
builder.ConfigureIdentityServer(options => |
|||
{ |
|||
options.DatabaseProvider = EfCoreDatabaseProvider.MySql; |
|||
}); |
|||
```` |
|||
|
|||
Then `ConfigureIdentityServer()` method will set the field lengths to not exceed the MySQL limits. Refer to related module documentation if you have any problem while creating or executing the database migrations. |
|||
|
|||
## Re-Generate the Migrations |
|||
|
|||
The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core Migrations depend on the selected DBMS provider. So, changing the DBMS provider will cause the migration fails. |
|||
|
|||
* Delete the Migrations folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. |
|||
* Run `Add-Migration "Initial"` on the Package Manager Console (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). |
|||
|
|||
This will create a database migration with all database objects (tables) configured. |
|||
|
|||
Run the `.DbMigrator` project to create the database and seed the initial data. |
|||
|
|||
## Run the Application |
|||
|
|||
It is ready. Just run the application and enjoy coding. |
|||
@ -1,4 +1,118 @@ |
|||
# Options |
|||
|
|||
TODO! |
|||
Microsoft has introduced [the options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) that is used to configure a group of settings used by the framework services. This pattern is implemented by the [Microsoft.Extensions.Options](https://www.nuget.org/packages/Microsoft.Extensions.Options) NuGet package, so it is usable by any type of applications in addition to ASP.NET Core based applications. |
|||
|
|||
ABP framework follows this option pattern and defines options classes to configure the framework and the modules (they are explained in the documents of the related feature). |
|||
|
|||
Since [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) explains the pattern in detail, no reason to repeat all. However, ABP adds a few more features and they will be explained here. |
|||
|
|||
## Configure Options |
|||
|
|||
You typically configure options in the `ConfigureServices` of the `Startup` class. However, since ABP framework provides a modular infrastructure, you configure options in the `ConfigureServices` of your [module](Module-Development-Basics.md). Example: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.IsEnabled = false; |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
* `AbpAuditingOptions` is a simple class defines some properties like `IsEnabled` used here. |
|||
* `AbpModule` base class defines `Configure` method to make the code simpler. So, instead of `context.Services.Configure<...>`, you can directly use the `Configure<...>` shortcut method. |
|||
|
|||
If you are developing a reusable module, you may need to define an options class to allow developers to configure your module. In this case, define a plain options class as shown below: |
|||
|
|||
````csharp |
|||
public class MyOptions |
|||
{ |
|||
public int Value1 { get; set; } |
|||
public bool Value2 { get; set; } |
|||
} |
|||
```` |
|||
|
|||
Then developers can configure your options just like the `AbpAuditingOptions` example above: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<MyOptions>(options => |
|||
{ |
|||
options.Value1 = 42; |
|||
options.Value2 = true; |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
* In this example, used the shortcut `Configure<...>` method. |
|||
|
|||
### Get the Option Value |
|||
|
|||
Whenever you need to get the value of an option, [inject](Dependency-Injection.md) the `IOptions<TOption>` service into your class and use its `.Value` property. Example: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly MyOptions _options; |
|||
|
|||
public MyService(IOptions<MyOptions> options) |
|||
{ |
|||
_options = options.Value; //Notice the options.Value usage! |
|||
} |
|||
|
|||
public void DoIt() |
|||
{ |
|||
var v1 = _options.Value1; |
|||
var v2 = _options.Value2; |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Read [the Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options) for all details of the options pattern. |
|||
|
|||
## Pre Configure |
|||
|
|||
One restriction of the options pattern is that you can only resolve (inject) the `IOptions<MyOptions>` and get the option values when the dependency injection configuration completes (that means the `ConfigureServices` methods of all modules complete). |
|||
|
|||
If you are developing a module, you may need to allow developers to set some options and use these options in the dependency injection registration phase. You may need to configure other services or change the dependency injection registration code based on these option values. |
|||
|
|||
For such cases, ABP introduces the `PreConfigure<TOptions>` and the `ExecutePreConfiguredActions<TOptions>` extension methods for the `IServiceCollection`. The pattern works as explained below. |
|||
|
|||
1. Define a plan option class in your module. Example: |
|||
|
|||
````csharp |
|||
public class MyPreOptions |
|||
{ |
|||
public bool MyValue { get; set; } |
|||
} |
|||
```` |
|||
|
|||
Then any [module class](Module-Development-Basics.md) depends on your module can use the `PreConfigure<TOptions>` method in its `PreConfigureServices` method. Example: |
|||
|
|||
````csharp |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<MyPreOptions>(options => |
|||
{ |
|||
options.MyValue = true; |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
> Multiple modules can pre-configure the options and override the option values based on their dependency order. |
|||
|
|||
Finally, your module can execute the `ExecutePreConfiguredActions` method in its `ConfigureServices` method to get the configured option values. Example: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var options = context.Services.ExecutePreConfiguredActions<MyPreOptions>(); |
|||
if (options.MyValue) |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
|
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Microsoft.EntityFrameworkCore.ChangeTracking; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.ValueComparers |
|||
{ |
|||
public class AbpDictionaryValueComparer : ValueComparer<Dictionary<string, object>> |
|||
{ |
|||
public AbpDictionaryValueComparer() |
|||
: base( |
|||
(d1, d2) => d1.SequenceEqual(d2), |
|||
d => d.Aggregate(0, (k, v) => HashCode.Combine(k, v.GetHashCode())), |
|||
d => d.ToDictionary(k => k.Key, v => v.Value)) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.ValueConverters |
|||
{ |
|||
public class AbpJsonValueConverter<TPropertyType> : ValueConverter<TPropertyType, string> |
|||
{ |
|||
public AbpJsonValueConverter() |
|||
: base( |
|||
d => JsonConvert.SerializeObject(d, Formatting.None), |
|||
s => JsonConvert.DeserializeObject<TPropertyType>(s)) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.AspNetCore.Mvc.Authentication; |
|||
|
|||
namespace MyCompanyName.MyProjectName.Controllers |
|||
{ |
|||
public class AccountController : ChallengeAccountController |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using MyCompanyName.MyProjectName.Localization; |
|||
using Volo.Abp.UI.Navigation; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace MyCompanyName.MyProjectName |
|||
{ |
|||
public class MyProjectNameWebHostMenuContributor : IMenuContributor |
|||
{ |
|||
private readonly IConfiguration _configuration; |
|||
|
|||
public MyProjectNameWebHostMenuContributor(IConfiguration configuration) |
|||
{ |
|||
_configuration = configuration; |
|||
} |
|||
|
|||
public Task ConfigureMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
if (context.Menu.Name == StandardMenus.User) |
|||
{ |
|||
AddLogoutItemToMenu(context); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
private void AddLogoutItemToMenu(MenuConfigurationContext context) |
|||
{ |
|||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>(); |
|||
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<MyProjectNameResource>>(); |
|||
|
|||
if (currentUser.IsAuthenticated) |
|||
{ |
|||
context.Menu.Items.Add(new ApplicationMenuItem( |
|||
"Account.Manage", |
|||
l["ManageYourProfile"], |
|||
$"{_configuration["AuthServer:Authority"].EnsureEndsWith('/')}Account/Manage", |
|||
icon: "fa fa-cog", |
|||
order: int.MaxValue - 1001, |
|||
null, |
|||
"_blank") |
|||
); |
|||
|
|||
|
|||
context.Menu.Items.Add(new ApplicationMenuItem( |
|||
"Account.Logout", |
|||
l["Logout"], |
|||
"/Account/Logout", |
|||
"fas fa-power-off", |
|||
order: int.MaxValue - 1000 |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
|
|||
"ManageYourProfile": "Manage your profile" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "tr", |
|||
"texts": { |
|||
"ManageYourProfile": "Profil yönetimi" |
|||
} |
|||
} |
|||
Loading…
Reference in new issue