ABP IO Code sample for run multiple databases for multi-tenancy - asp.net-core

Please notice that I am talking about ABP.io, not the Boilerplate framework.
The in-build free module Tenant-Management is developed to work with multiple tenants and a unique database. however, the documentation says that the framework has a built-in friendly way to use the multiple database approach, including:
new dbContext
database migration and seeding
Connection String service
I am new in ABP IO, and I want a sample that employs the framework elements to implement a single database for every tenant.
I get started by overriding the tenant create sync method of the tenant management module as follows.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITenantAppService), typeof(TenantAppService), typeof(ExtendedTenantManagementAppService))]
public class ExtendedTenantManagementAppService : TenantAppService
{
public ExtendedTenantManagementAppService(ITenantRepository tenantRepository,
ITenantManager tenantManager,
IDataSeeder dataSeeder) : base(tenantRepository, tenantManager, dataSeeder)
{
LocalizationResource = typeof(WorkspacesManagerResource);
ObjectMapperContext = typeof(WorkspacesManagerApplicationModule);
}
public override async Task<TenantDto> CreateAsync(TenantCreateDto input)
{
var tenant = await TenantManager.CreateAsync(input.Name);
input.MapExtraPropertiesTo(tenant);
await TenantRepository.InsertAsync(tenant);
await CurrentUnitOfWork.SaveChangesAsync();
using (CurrentTenant.Change(tenant.Id, tenant.Name))
{
//TODO: Handle database creation?
// create database
// migrate
// seed with essential data
await DataSeeder.SeedAsync(
new DataSeedContext(tenant.Id)
.WithProperty("AdminEmail", input.AdminEmailAddress)
.WithProperty("AdminPassword", input.AdminPassword)
);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
}
Any code sample?

Related

Multiple Unit Tests' Running With multiple Connections To Database

In the Asp.Net core project, there are several unit tests used services for connecting to the database and bring real data, so multiple concurrent connections are created. When these tests run, I received this error
A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
but I do not know how can I fix this error without using async ways.
In unit tests you should not use connection to a DB. You should use mockups and create your own data to test with.
Use the NuGet package moqto easily create mockup objects.
Example of using the mockup objects:
public void Test_Login()
{
Mock<IDatabase> mockDatabase = new Mock<IDatabase>();
mockDatabase.Setup(p => p.GetAccountAsync(It.IsAny<string>()))
.Returns((string givenEmail) => Task.FromResult(new Account(1, "test", givenEmail, "123", "$2b$10$pfsnDQ3IWuY/zER/uBQpedvRFntMNHGOGhOSpABKZ7bwS", false)));
Mock<IConfiguration> mockConfiguration = new Mock<IConfiguration>();
Mock<IHostingEnvironment> mockHostingEnvironment = new Mock<IHostingEnvironment>();
AccountService accountService = new AccountService(mockDatabase.Object, mockConfiguration.Object, mockHostingEnvironment.Object);
LoginViewModel loginViewModel = new LoginViewModel
{
EmailLogin = "test#test.com",
PasswordLogin = "s"
};
Task<Account> account = accountService.LoginAsync(loginViewModel);
Assert.NotNull(account.Result);
Assert.Equal(loginViewModel.EmailLogin, account.Result.Email);
}
In the example above I manually set the value of the mockup database that the service method will use to retrieve the account and compare the returned email with the given email.

Asp.net Boilerplate - Implement setting manager with database

I've been building an asp.net core website, using the asp.net boilerplate template. As of now, I've been storing all of the settings in the appsettings.json file. As the application gets bigger, I'm thinking I should start storing some settings via ABP's SettingProvider and ISettingStore.
My question is, does anyone have, or know of, a sample application that show's how to implement ISettingStore and storing the settings in the database?
The only post I could find so far is this, but the link hikalkan supplies is broken.
Thanks for any help,
Joe
ABP stores settings on memory with default values. When you insert a new setting value into database, then it reads from database and overrides the default value. So basically when database has no settings then it means all the settings are on default values. Setting values are stored in AbpSettings table.
To start using settings mechanism. Create your own SettingProvider inherited from SettingProvider. Initialize it in your module (eg:
ModuleZeroSampleProjectApplicationModule).
As SettingProvider is automatically registed to dependency injection; You can inject ISettingManager wherever you want.
public class MySettingProvider : SettingProvider
{
public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context)
{
return new[]
{
new SettingDefinition(
"SmtpServerAddress",
"127.0.0.1"
),
new SettingDefinition(
"PassiveUsersCanNotLogin",
"true",
scopes: SettingScopes.Application | SettingScopes.Tenant
),
new SettingDefinition(
"SiteColorPreference",
"red",
scopes: SettingScopes.User,
isVisibleToClients: true
)
};
}
}
In application services and controllers you don't need to inject ISettingManager
(because there's already property injected) and you can directly use SettingManager property. Forexample :
//Getting a boolean value (async call)
var value1 = await SettingManager.GetSettingValueAsync<bool>("PassiveUsersCanNotLogin");
And for the other classes (like Domain Services) can inject ISettingManager
public class UserEmailer : ITransientDependency
{
private readonly ISettingManager _settingManager;
public UserEmailer(ISettingManager settingManager)
{
_settingManager = settingManager;
}
[UnitOfWork]
public virtual async Task TestMethod()
{
var settingValue = _settingManager.GetSettingValueForUser("SmtpServerAddress", tenantAdmin.TenantId, tenantAdmin.Id);
}
}
Note: To modify a setting you can use these methods in SettingManager ChangeSettingForApplicationAsync, ChangeSettingForTenantAsync and ChangeSettingForUserAsync

Can i use System.Runtime.Caching.MemoryCache in cloud web role project?

I would like to use System.Runtime.Caching.MemoryCache in Web role project which contains WCF Service.
Can anybody please let me know that whether we can use System.Runtime.Caching.MemoryCache in Cloud web role project?
If yes please let me know Memory and other constraints.
Yes you can.
You should add the reference to System.Runtime.Caching to the Web Role project, then use something like the code below (it is doing almost nothing and is not a best practice, for sure).
Just tried it with ASP.NET MVC in the Cloud Web Role with the Azure Emulator and it works.
Regarding limits - there are two CacheMemoryLimit and PhysicalMemoryLimit properties you can use for retrieve the needed values. It shows the limit in bytes. I do not know if there are any limits beyond these in terms of in-memory cache in Azure Cloud Services.
private static object _lock = new Object();
private static MemoryCache _cache = new MemoryCache("ThisIsMyCache");
public static object GetItem(string key)
{
lock (_lock)
{
var item = _cache.Get(key);
if (item == null)
{
item = InitiaizeItem(key);
_cache.Set(key, item, new CacheItemPolicy());
}
return item;
}
}
private static object InitiaizeItem(string key)
{
return new { Value = key };
}

Best practices for prepopulated tables via OrmLite in Servicestack

I'm generating tables via OrmLite and I was wondering about best practices for prepopulating tables. Example tables - countries, states, cities, etc.
I can think of a few ways to pre-populate tables:
List item
Seed DB
API (when possible)
Static file
In code
Separate project
However, in some cases the data could get large as in the example of cities around the world so in code is not viable.
I could also consider generating tables that need to be pre-populated directly via another project where I can fetch data from a source and get it into the DB.
However, I was wondering about the scenario when you do generate it via an ORM (especially in production). How would you approach the problem?
This must be a common problem across all ORM's.
If it's only code tables like countries, states, etc, they're small enough to still have them as part of the project, normally I'd create a separate static class called SeedData with all the data in POCO's
1. Maintaining Code Tables in Host Project
public static class SeedData
{
public static List<Country> Countries
{
get { return new[] { new Country(...), ... }; }
}
}
Then in your AppHost populate add a flag on whether to re-create them on startup, e.g:
public void Configure(Container container)
{
var appSettings = new AppSettings(); //Read from Web.config <appSettings/>
if (appSettings.Get("RecreateTables", false))
{
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
Change AppSetting to recreate tables
This will then let you re-create the tables and re-populate the data when you change the RecreateTables appSetting to True, e.g:
<appSettings>
<add key="RecreateTables" value="True" />
</appSettings>
As the default behavior of ASP.NET will automatically restart the AppDomain, just saving a change to Web.config is enough to restart your ASP.NET application the next time any page gets refreshed.
2. Add to Test Project in adhoc Explicit Test
If the Data gets too big to fit in the working project I would first move it to a separate test project inside an [Explicit] text fixture (so it's never automatically run), that you can easily run manuallu, e.g:
[Explicit]
[TestFixture]
public class AdminTasks
{
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
3. Save data in external static text Files
Finally if the data is even too big to fit in C# classes, I would then save it out to a static file in the test that you can easily re-hydrate into POCO's that you can populate with OrmLite, e.g:
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
var countries = File.ReadAllText("~/countries.txt".MapAbsolutePath())
.FromJson<List<Country>>();
db.InsertAll(countries);
...
}
}

How to easily convert .sdf developed in Vs 2010 to SQL Server database

I don't have SQL Server installed my machine. Hence I decided to start working with a SQL Server Compact Edition (.sdf) in VS2010. After then I installed SQL Server at the moment, now I'd like to convert from .sdf to a "real" SQL Server database.
How can I do it ? Please advise.
What you should to is to change your connectionString that points to your Compact SQL to your new SQL Server instance.
After that in you Context file, inside of a static constructor which is called:
static YourDbContext()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<YourDbContext>());
}
This should create your database and tables based on your models. If you need to insert any data you should Enable-Migrations and in a configuration file that is createdc8 override Seed method.
One important thing DO NOT ASSUME that you have rights or privileges to CREATE or DROP DATABASE or to execute table modificiations.
I will assume that you used EF Code First approach. Your context file could look something like this:
public YourDbContext : DbContext
{
static YourDbContext()
{
// Database.SetInitializer<DbContext>(null); // Change this line to the next one
Database.SetInitializer(new CreateDatabaseIfNotExists<YourDbContext>());
}
// The rest of implementation
}
Inside of Visual Studio in Package Manager Console execute:
Enable-Migrations -ProjectName YourProjectName
(If you have more than one DbContext implementation you will need to follow the instructions from the error message that Enable-Migrations throws back at you.)
Once this is done you will notice a new folder Migrations with one file Configuration.cs. Open it and you will see method Seed.
protected override void Seed(YourDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
// Here you can call your context.DbSetImplementation.Add(new Something {...});
}
That's about it.