[DevExpress Support Team: CLONED FROM K18098: How to create a custom XPO connection provider and then use it in an XAF application]
I have implemented this solution for a Postgres connection provider but I get an exception when the application starts and the database is not existing because the CreateProviderFromString autoCreateOption parameter is set to SchemaAlreadyExists.
Forcing it to DatabaseAndSchema solves the problem but I would like to be able to change it from outside, i.e. the caller should pass a different parameter.
C#using DevExpress.Xpo.DB;
using Npgsql;
using System;
using System.Data;
namespace PrgDig.PostGIS
{
/// <summary>
/// <see cref="https://supportcenter.devexpress.com/ticket/details/k18098/how-to-create-a-custom-xpo-connection-provider-and-then-use-it-in-an-xaf-application"/>
/// </summary>
public class PostGISConnectionProvider : PostgreSqlConnectionProvider
{
public PostGISConnectionProvider(IDbConnection connection, AutoCreateOption autoCreateOption)
: base(connection, autoCreateOption)
{
}
public new static IDataStore CreateProviderFromString(string connectionString, AutoCreateOption autoCreateOption, out IDisposable[] objectsToDisposeOnDisconnect)
{
IDbConnection connection = new NpgsqlConnection(connectionString);
objectsToDisposeOnDisconnect = new IDisposable[] { connection };
return CreateProviderFromConnection(connection, autoCreateOption);
}
public new static IDataStore CreateProviderFromConnection(IDbConnection connection, AutoCreateOption autoCreateOption)
{
return new PostGISConnectionProvider(connection, autoCreateOption);
}
public new static string GetConnectionString(string server, int port, string userId, string password, string database)
{
return $"{XpoProviderTypeParameterName}={XpoProviderTypeString};Server={server};Port={port};Database={database};User ID={userId};Password={password}";
}
public new static string GetConnectionString(string server, string userId, string password, string database)
{
return $"{XpoProviderTypeParameterName}={XpoProviderTypeString};Server={server};Database={database};User ID={userId};Password={password}";
}
public new static void Register()
{
RegisterDataStoreProvider(XpoProviderTypeString, CreateProviderFromString);
RegisterDataStoreProvider(typeof(PostgreSqlConnectionProvider).FullName, CreateProviderFromConnection);
}
public new const string XpoProviderTypeString = "PostGIS";
/// <summary>
/// <see cref=">https://supportcenter.devexpress.com/ticket/details/t439479/add-support-for-geometry-in-x"/>
/// <see cref="https://community.devexpress.com/blogs/oliver/archive/2017/01/16/xpo-sql-server-and-spatial-data-revisited.aspx"/>
/// </summary>
/// <param name="value"></param>
/// <param name="args"></param>
/// <returns></returns>
protected override object ReformatReadValue(object value, ReformatReadValueArgs args)
{
if (value != null && value is NetTopologySuite.Geometries.Geometry)
{
return value;
}
else
{
return base.ReformatReadValue(value, args);
}
}
}
}
Hello,
I reviewed your code, and it looks like the issue is not related to your custom provider. You may experience the same behavior with a default provider. To get rid of the exception, use the solutions described here: T367835 - How to avoid compatibility errors during the database update or application startup.
If the default provider works smoothly, please send us a simple example that illustrates how you use your custom provider and the issue that you faced in action.
Thanks,
Andrey
Well, I've introduced the custom connection provider along with changing checkCompatibilityType to ModuleInfo and the problem as you stated is not there.
this.CheckCompatibilityType = DevExpress.ExpressApp.CheckCompatibilityType.ModuleInfo;
In global.asax I have the following
#if DEBUG if(System.Diagnostics.Debugger.IsAttached && WebApplication.Instance.CheckCompatibilityType == CheckCompatibilityType.DatabaseSchema) { WebApplication.Instance.DatabaseUpdateMode = DatabaseUpdateMode.UpdateDatabaseAlways; } #endif
Is it possible to use ModuleInfo Check Compatibility Type and create the DB if it doesn't exist ?
Hello Massimo,
When you start an XAF application from Visual Studio, it should create the missing database and tables with any CheckCompatibilityType. If CheckCompatibilityType is ModuleInfo, it will create the database in the following cases:
Please refer to the following help topic for additional information: Update Application and Database Versions using the ModuleInfo Table.
If this does not happen in your application, please share a small sample project that demonstrates the issue.
I also would like to note that you do not need to change CheckCompatibilityType to create the database automatically. If this property is set to DatabaseSchema, XAF will create the missing database and tables when you launch your application from Visual Studio. You do not need to change the autoCreateOption parameter in your code. XAF will pass an appropriate value to the CreateProviderFromString method when it detects schema changes. Note that an application will throw SchemaCorrectionNeededExceptions in this case, but they are handled in our code. If you see them in the debugger, just skip them with F5. Please let me know if this works for you.
I'm sorry but it was not so clear for me that I could skip the exception. Problem solved.
Anyway the exception is an User-Unhandled exception:
DevExpress.Xpo.DB.Exceptions.UnableToOpenDatabaseException: 'Unable to open database. Connection string: 'Server=127.0.0.1;User ID=postgres;Password=***REMOVED***;Database=AnotherEmptySolution;'; Error: 'Npgsql.PostgresException (0x80004005): 3D000: database "AnotherEmptySolution" does not exist at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage) at Npgsql.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken) at Npgsql.ConnectorPool.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken) at Npgsql.ConnectorPool.<>c__DisplayClass38_0.<<Rent>g__RentAsync|0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Npgsql.NpgsqlConnection.<>c__DisplayClass41_0.<<Open>g__OpenAsync|0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Npgsql.NpgsqlConnection.Open() at DevExpress.Xpo.DB.PostgreSqlConnectionProvider.CreateDataBase() Exception data: Severity: FATAL SqlState: 3D000 MessageText: database "AnotherEmptySolution" does not exist File: postinit.c Line: 848 Routine: InitPostgres''
Hello Massimo,
From your comment it is not quite clear whether any issue remains after you skip startup exceptions. These exceptions are expected. XAF creates a connection provider with AutoCreateOption = DatabaseAndSchema only when it detects that a schema update is required. During this detection, handled UnableToOpenDatabaseException and SchemaCorrectionNeededExceptions may occur. You won't see them with the default Connection Provider if the Debugging -> Options -> Enable Just My Code option is enabled in your Visual Studio. With a custom Connection Provider, these exceptions occur in your code, and the debugger shows them.
I confirm that skipping the exception solves definitively the problem, but the exception I get is not the one you posted in your first post but is
UnableToOpenDatabaseException.
Thank you for support
Hello Massimo,
Thank you for the confirmation.
Just for your information, XPO throws SchemaCorrectionNeededException when it cannot find a column or a table and UnableToOpenDatabaseException when it cannot find a database.