Using MartenDB for Eventsourcing with Azure Postgres database - c#

We're trying to configure Marten as our EventStore using a Postgres database in Azure. We've provisioned a Azure Database for PostgreSQL single server in Azure.
Ideally we would like Marten to create the database if it not exists. Our problem is that CreateDatabasesForTenants never gets called during services.AddMarten. This is our code:
services.AddMarten(options =>
{
var connection =
#"Server=databaseServerName.postgres.database.azure.com;Database=myDatabase;Port=5432;User Id=admin#databaseServerName;Password=apassword;Ssl Mode=VerifyCA;";
var maintenanceconnectionString = #"Server=databaseServerName.postgres.database.azure.com;Database=postgres;Port=5432;User Id=admin#databaseServerName;Password=apassword;Ssl Mode=VerifyCA;";
options.Connection(connection);
options.CreateDatabasesForTenants(options =>
{
options.MaintenanceDatabase(maintenanceconnectionString);
options.ForTenant()
.CheckAgainstPgDatabase()
.WithOwner("postgres")
.WithEncoding("UTF-8")
.ConnectionLimit(-1)
.OnDatabaseCreated(_ =>
{
});
});
});
Everytime we access the IDocumentStore we get the following message:
Npgsql.PostgresException (0x80004005): 3D000: database "myDatabase" does not exist
Obviously, it doesn't exist, but why is the CreateDatabaseForTenants never called? What am I missing here?

Related

Delete database shard mapping and shard databases in SQL Server to clean up tenant resources

We have a database for each tenant as shown in the screenshot below.
I am trying to delete the tenant DB and clean up and reclaim the resources of a tenant I have anciently created. I tried the following code, however, even though this method executes successfully, no changes take place in the database. The tenant database still remains and mappings are still there in [ShardsGlobal] table.
What is the proper way to fully delete tenant-specific databases and references from SQL Server?
Please note that we will be using Azure Elastic Pool in production.
string shardMapManagerConnectionString = configuration.GetShardMapMangerConnectionString();
ShardMapManager shardMapManager;
shardMapManager = ShardMapManagerFactory.GetSqlShardMapManager(
shardMapManagerConnectionString,
ShardMapManagerLoadPolicy.Lazy);
var shardMap = shardMapManager.GetListShardMap<T>(configuration.ShardMapName);
if (shardMap.TryGetMappingForKey(key, out PointMapping<T> mapping))
{
if (mapping.Status == MappingStatus.Online)
{
// `mapping =` on next line is needed
mapping = shardMap.MarkMappingOffline(mapping);
}
shardMap.DeleteMapping(mapping);
var shard = shardMap.GetShard(mapping.Shard.Location);
shardMap.DeleteShard(shard);
}
If you look at the docs for DeleteShard you'll notice this part:
These methods do not have any impact on the databases themselves, only on metadata in the shard map.
Have you tried the approach presented in the Azure Samples?
To delete both shard and DB, I would assume, based on the FAQ, you'd have to delete the shard first, then the DB.

How to recreate or refresh an access token for database connections in Azure functions?

I am trying to use an Entity Framework database context for use with managed identity credentials in an Azure function. I have been able to establish a database connection. The issue is that I am not able to keep the database connection established whenever the database context is used in the Azure function.
The following code is what I have tried from the following reference: https://github.com/dotnet/efcore/issues/11928#issuecomment-455312550
Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
s.AddDbContext<MyDbContext>(options => options.UseSqlServer("connectionstring"));
})
.Build();
host.Run();
MyDbContext.cs
public MyDbContext(DbContextOptions options) : base(options)
{
SqlConnection mySQLConnection = (SqlConnection) Database.GetDbConnection();
mySQLConnection.AccessToken = new DefaultAzureCredential().GetToken(new TokenRequestContext(new[]
{
"https://database.windows.net/.default"
})).Token;
}
This is the exception I receive:
Exception: System.InvalidOperationException: Not allowed to change the 'AccessToken' property. The connection's current state is open.
I have also tried to do the following:
Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
SqlConnection mySQLConnection = new SqlConnection("connectionstring");
mySQLConnection.AccessToken = new DefaultAzureCredential().GetToken(new TokenRequestContext(new[]
{
"https://database.windows.net/.default"
})).Token;
s.AddDbContext<MyDbContext>(options => options.UseSqlServer(mySQLConnection));
})
.Build();
host.Run();
The above does work without getting the exception mentioned before but I get the following error once the token has expired:
Login failed for user '<token-identified principal>'. Token is expired
I would like to know how a new token can be created or refreshed automatically as I thought Microsoft.Data.SqlClient or .GetToken() would be able to do this. Any help would be greatly appreciated.
Since you are using EF core, all you need to do is change your connection string to include Authentication=Active Directory Default;. This will internally use DefaultAzureCredential which will cache and refresh the token for you.
Also, add the package reference
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.0" />
The solution that you have implemented is useful for Entity Framework prior to core. You can modify your code to make a simple SQL connection with new connection string.
More details can be found at this Microsoft documentation

Why my identity database table is not migrated to Azure?

When publishing my first ASP.NET Core 2.1 web app in Azure from Visual Studio 2017, I am setting up EF migrations like in the picture below:
After running Publish, the identity table is not created. In Configure -> Settings I have info, that No databases found in the project.
Also I am using console command:
dotnet ef database update --context AppIdentityDbContext
What gives me same result. Connection strings I am taking from Azure Show database connection strings, updateting them with username and password.
I am not sure what I am doing wrong here. Everything works perfectly on my computer. I will be thankful for any suggestions to check.
Right now, after trying to populate Identity table:
public static class IdentitySeedData
{
private const string adminUser = "****";
private const string adminPassword = "****";
public static async Task EnsurePopulated(UserManager<IdentityUser> userManager)
{
IdentityUser user = await userManager.FindByIdAsync(adminUser);
if (user == null)
{
user = new IdentityUser("Admin");
await userManager.CreateAsync(user, adminPassword);
}
}
}
I am getting error:
AggregateException: One or more errors occurred. (Invalid object name 'AspNetUsers'.)
System.Threading.Tasks.Task.ThrowIfExceptional(bool includeTaskCanceledExceptions)
SqlException: Invalid object name 'AspNetUsers'.
System.Data.SqlClient.SqlCommand+<>c.<ExecuteDbDataReaderAsync>b__108_0(Task<SqlDataReader> result)
UPDATE: I found out, that my problem occurs only identity database.
As I did not managed to migrate my Identity database to Azure, I found alternative solution here: Invalid object name 'dbo.AspNetUsers' in Asp.NET MVC 5 Entity Framework

Error connecting to Aurora during Entity Framework Core DB update

I am trying to run the dotnet ef database update CLI command against my AWS Aurora MySql RDS cluster. Just for testing purposes, I've hard-coded the connection string to remove configuration loading being an issue.
When I run the update command I'm given the following error:
An error occurred using the connection to database '' on server 'some-rds-cluster.abcdefg.us-east-2.rds.amazonaws.com'.
MySql.Data.MySqlClient.MySqlException (0x80004005): Access denied for user 'bobby'#'555.555.555.555' (using password: YES)
at MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in C:\projects\mysqlconnector\src\MySqlConnector\Core\ServerSession.cs:line 333
My ConfigureServices method looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddDbContextPool<AccountContext>(options =>
{
options.UseMySql("server=a-random-cluster.foobar.us-east-2.rds.amazonaws.com;database=SomeDbHere;user=abcdefg;password=abcdefg", contextOptionsBuilder =>
{
contextOptionsBuilder.ServerVersion(new Version(5, 7, 12), ServerType.MySql);
});
});
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
I am able to connect to the cluster using MySql Workbench on my local machine; the update CLI command from the same machine though fails. The Security Group attached to the RDS allows traffic from my IP address to connect (hence why MySql Workbench connects). Is there something specific with EF Core and Pomelo.EntityFrameworkCore.MySql I need to do in order for the CLI to update the tables? Does Aurora support what EF Core needs to do in order to update the data model, such as using a different port number for some reason?
I noticed the error said "using the connection to database' '` and I can't tell if it intentionally left the database name blank or if that is part of the problem. The DB is specified in the migration and connection string though, so I'm would assume they leave the db name out since in MySql it's not a Db but rather a "schema".
I'm running aspnetcore 2.1.0 and EF core 2.1.0
I generated the migrations from my entities and contexts which is below.
public partial class InitialMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "SomeDbHere");
migrationBuilder.CreateTable(
name: "Users",
schema: "SomeDbHere",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation(
"MySql:ValueGenerationStrategy",
MySqlValueGenerationStrategy.IdentityColumn),
Username = table.Column<string>(nullable: true),
CognitoId = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Users",
schema: "SomeDbHere");
}
}
It seems like a connection issue. I was able to connect and run migrations in a new project. Take a look at this project https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql and read the code in test/EFCore.MySql.IntegrationTests.
I had the same problem and it was because the DB password contained a '$' char in it.
Thus when I did export MY_DB_ENV="Server=localhost;Database=my_db;Uid=root;Pwd=pass_with_$abc" the $abc was stripped from the password and couldn't connect.
You can check with echo $MY_DB_ENV if that's the case.

Exception while mapping composite type using Npgsql in C#

We map composite type in our C# application using npgsql (open source postgres ado.net provider for C#).
NpgsqlConnection.MapCompositeGlobally<SomeCompositeType>("some_type", new NpgsqlSnakeCaseNameTranslator());
We map it every time globally before executing postgres query or opening connection. It works fine mostly but sometimes we get error.
Exception we are getting
System.NotSupportedException: The CLR type Entities.CompositeTypes.SomeCompositeType isn't supported by Npgsql or your PostgreSQL. If you wish to map it to a PostgreSQL composite type you need to register it before usage, please refer to the documentation.
Also we do not make any changes at postgres db level for this composite type but still get this error sometimes.
Here is the pseudo code we are using for this.
public void CreateData(int value)
{
NpgsqlConnection.MapCompositeGlobally<SomeCompositeType>("some_type", new NpgsqlSnakeCaseNameTranslator());
var parameters = new NpgsqlParameter[]
{
new NpgsqlParameter{ParameterName = "p_value", NpgsqlDbType = NpgsqlDbType.Composite, Value = value,SpecificType=typeof(SomeCompositeType[])}
}
}
excuteQuery(query, parameters); // inside it we open connection & execute query
Current Npgsql version we are using is Npgsql 3.2.7

Categories