Mendes
Mendes

Reputation: 18561

c# Entity Framework EF 4.1 Change Schema and Database name at runtime

I´ve searched for some topics, but did not find a concrete solution to my problem.

My app is a c# commercial app. I´m using EF 4.1 database first. I generate the model from a development database connection and that create a Model.edmx file and all EF stuff pretty fine.

My goal is to deliver an appplication to the customer and let him freely create the database and database user at his own. Do do it, at runtime I would get the username, password, database connection and schema name parameters to connect to customer database. In that way, to deploy the application all I need is to ask the customer to create a database and add the database parameters to the app config file.

So, myy goal is to change the connection string and schema parameter at runtime, without changing att all the auto generated edmx file, not touching the VS generated code.

I have looked around and found:

For EF earlier versions:

Changing schema name on runtime - Entity Framework

http://efmodeladapter.codeplex.com

All other posts are going around that. I tryed even to use the first post code with no success.

But I´ve seen that EF 4.1 comes with better support tools to do it, but I could not find references or examples to it. It´s important not to change the auto generated code from VS.

I´m pretty new to EF, so I would like to ask for help on accomplish the following tasks: a) Change connection string at runtime adding my username, password and database server/port parameters b) Change database schema

I´m using Oracle as a database server (that makes things worst as Oracle mixes schema and users together).

Upvotes: 1

Views: 4184

Answers (1)

Erti-Chris Eelmaa
Erti-Chris Eelmaa

Reputation: 26318

Actually, I needed solution for this too. I quickly whipped up solution, which works nicely. I didn't find much information about this in Internet, so I am not sure about the "EF 4.1 comes with better support tools to do it".

The concrete example "Changing schema name on runtime - Entity framework" didn't entirely work for me, however with some minor modifications I got it working.

Here is a DatabaseUtils class that can do it:

internal static class DatabaseUtils
{
    /// <summary>
    /// Builds the connection string for Entity framework.
    /// </summary>
    /// <returns></returns>
    public static EntityConnection BuildConnection(BuildConnectionParams buildConnectionParams)
    {
        var sqlBuilder = new SqlConnectionStringBuilder
            {
                DataSource = buildConnectionParams.ServerName,
                InitialCatalog = buildConnectionParams.DatabaseName,
                IntegratedSecurity = true
            };

        var providerString = sqlBuilder.ToString();
        var entityBuilder = new EntityConnectionStringBuilder
        {
            Provider = buildConnectionParams.ProviderName,
            ProviderConnectionString = providerString,
            Metadata = string.Format(@"res://*/{0}.csdl|
                        res://*/{0}.ssdl|
                        res://*/{0}.msl", buildConnectionParams.ModelName)
        };

        return CreateConnection(buildConnectionParams.SchemaName, entityBuilder, buildConnectionParams.ModelName);
    }


    /// <summary>
    /// Creates the EntityConnection, based on new schema & existing connectionString
    /// </summary>
    /// <param name="schemaName">Name of the schema.</param>
    /// <param name="connectionBuilder"></param>
    /// <param name="modelName">Name of the model.</param>
    /// <returns></returns>
    private static EntityConnection CreateConnection(string schemaName, EntityConnectionStringBuilder connectionBuilder, string modelName)
    {
        Func<string, Stream> generateStream =
            extension => Assembly.GetExecutingAssembly().GetManifestResourceStream(string.Concat(modelName, extension));

        Action<IEnumerable<Stream>> disposeCollection = streams =>
        {
            if (streams == null)
                return;

            foreach (var stream in streams.Where(stream => stream != null))
                stream.Dispose();
        };

        var conceptualReader = generateStream(".csdl");
        var mappingReader = generateStream(".msl");
        var storageReader = generateStream(".ssdl");

        if (conceptualReader == null || mappingReader == null || storageReader == null)
        {
            disposeCollection(new[] { conceptualReader, mappingReader, storageReader });
            return null;
        }

        var storageXml = XElement.Load(storageReader);

        foreach (var entitySet in storageXml.Descendants())
        {
            var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
            if (schemaAttribute != null)
                schemaAttribute.SetValue(schemaName);
        }

        storageXml.CreateReader();

        var workspace = new MetadataWorkspace();

        var storageCollection = new StoreItemCollection(new[] { storageXml.CreateReader() });
        var conceptualCollection = new EdmItemCollection(new[] { XmlReader.Create(conceptualReader) });
        var mappingCollection = new StorageMappingItemCollection(conceptualCollection, 
                                                                storageCollection,
                                                                new[] { XmlReader.Create(mappingReader) });

        workspace.RegisterItemCollection(conceptualCollection);
        workspace.RegisterItemCollection(storageCollection);
        workspace.RegisterItemCollection(mappingCollection);

        var connection = DbProviderFactories.GetFactory(connectionBuilder.Provider).CreateConnection();
        if (connection == null)
        {
            disposeCollection(new[] { conceptualReader, mappingReader, storageReader });
            return null;
        }

        connection.ConnectionString = connectionBuilder.ProviderConnectionString;
        return new EntityConnection(workspace, connection);
    }
}

Usage:

/// <summary>
/// Initializes a new instance of the <see cref="DynamicAQDContext"/> class.
/// </summary>
public DynamicAQDContext()
{
    var entityConnection = DatabaseUtils.BuildConnection(new BuildConnectionParams
    {
        ProviderName = "System.Data.SqlClient",
        ServerName = "localhost\\",
        DatabaseName = "",
        ModelName = "YOURMODEL",
        SchemaName = "SCHEMA"
    });

    if(entityConnection == null)
        throw new Exception("Can't create EntityConnection");

    _entities = new LINKEntities(entityConnection);
}

more info can be found here: http://bestplayah.com/entity-framework-dynamic-schema-changes-using-database-first-approach/

Upvotes: 4

Related Questions