Dynamic Schema and Connection String Changes with Entity Framework in NET Framework 4.8

Gabriel OrtizGabriel Ortiz
4 min read

Source Code: GitHub

In this article, we will explore how to create a connection that can be modified at runtime with Entity Framework in a desktop application based on .NET Framework 4.8.

Database Structure

The previous image provides an overview of the database structure that we will use as a starting point for our example.

Creating the Context and Entities

Important: CodeFirst was used for creating the context in this example.

Before delving into the implementation of dynamic connections, it is essential to understand how the DbContext works in Entity Framework. This class has multiple constructors that allow us to configure our context, one of which accepts a connection string of type string that the context will use.

Therefore, we will add a constructor that allows us to pass the connection string as a parameter to our context.

public class MinimalDbContext : DbContext
{
    public MinimalDbContext (string connectionString)
        : base(connectionString)
    {
    }

    // More code
}

To set the value of the schema, we can store it in a private variable as follows:

public partial class MinimalDbContext : DbContext
{
    private readonly string _schema;

    public MinimalDbContext(string connectionString, string schema)
        : base(connectionString) => _schema = schema;

    // More code
}

The schema is defined in the OnModelCreating function that overrides our context.

public partial class MinimalDbContext : DbContext
{
    private readonly string _schema;

    public MinimalDbContext(string connectionString, string schema)
        : base(connectionString) => _schema = schema;

    // DbSet definitions

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema(_schema);

        // More code
    }
}

Funcionamiento del método OnModelCreating

How the OnModelCreating Method Works

The OnModelCreating method is used to initialize our context and is where we set our default schema for each of the entities.

Typically, this method (OnModelCreating) is called only once when the first instance of a derived context is created. The model for that context is then cached and is used for all further instances of the context in the app domain. — Microsoft

Since the OnModelCreating method is called only once and is cached, the schema value will not change once it has been initialized the first time (even if another instance of the context is created). Therefore, we need to establish a change in the cache according to our schema name. For this, we will use the interface called IDbModelCacheKeyProvider, which allows us to provide a key to the cache that should be the value of our schema so that the function is called when the schema changes.

public partial class MinimalDbContext
    : DbContext, IDbModelCacheKeyProvider
{
    private readonly string _schema;

    string IDbModelCacheKeyProvider.CacheKey
    {
        get => _schema;
    }

    public MinimalDbContext(string connectionString, string schema)
        : base(connectionString) => _schema = schema;

    // DbSet Definitions

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema(_schema);

        // More code
    }
}

Modifying the Entities

Entities created with CodeFirst usually have a reference to the schema to which the table belongs. These could be:

[Table("Product", Schema = "dbo")]
public partial class Product
{
    // Attributes
}
[Table("dbo.Product")]
public partial class Product
{
    // Attributes
}

It is necessary to remove these references so that they only refer to the table (by default, it will direct to dbo, but when setting the default schema, it will change in the entities that do not have a schema defined). The result should be:

[Table("Product")]
public partial class Product
{
    // Attributes
}

Implementation in Forms

To check the functionality of our code, a form was created with the basic fields of a connection string:

  • Data Source

  • Initial Catalog

  • User Id

  • Password

In addition, a field to set the schema.

public partial class FrmLogin : Form
{
    private readonly SqlConnectionStringBuilder _sqlConnectionStringBuilder;

    public FrmLogin()
    {
        InitializeComponent();
        _sqlConnectionStringBuilder = new SqlConnectionStringBuilder();
    }

    private void BtnConnect_Click(object sender, EventArgs e)
    {
        _sqlConnectionStringBuilder.DataSource = txtServer.Text;
        _sqlConnectionStringBuilder.InitialCatalog = txtDatabase.Text;
        _sqlConnectionStringBuilder.UserID = txtUserId.Text;
        _sqlConnectionStringBuilder.Password = txtPassword.Text;

        var connectionString = _sqlConnectionStringBuilder.ToString();
        var schema = txtSchema.Text;
        using (var context = new MinimalDbContext(connectionString, schema))
        {
            try
            {
                var count = context.Product.Count();

                MessageBox.Show($"Products: {count}", "Success");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex?.InnerException?.Message ?? ex.Message, "Error");
            }
        }
    }
}

Results

Results of Product Count Requests with Schema_1 in the Database

Results of Product Count Requests with Schema_2 in the Database


Thank you for reading, and I hope this article was helpful. I wouldn’t have taken the time to write it if it weren’t for the lack of documentation out there, lol.

Source Code

Repository: GitHub

References

0
Subscribe to my newsletter

Read articles from Gabriel Ortiz directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gabriel Ortiz
Gabriel Ortiz

A passionate software developer with a strong background in computer engineering. I love learning about new technologies and best practices to write clean and scalable code. My experience in programming has allowed me to delve deeply into the evolution of code and adopt a unique approach to the development of my projects