Entity Framework is a great tool for handling data access, but it isn’t perfect. In some projects, I use EF in library projects that will be used by any number of other projects. The consuming projects simply add a reference and add markup to the config. Recently I realized that the markup needed in the config is very ugly and really gives away the fact that EF is being used in the library. For the consuming project, a connection string must be added to the config which looks like the following

<connectionStrings>
    <add name="TestEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=My-PC\SQLEXPRESS;Initial Catalog=myDatabase;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

This might be ok for some, but what is really going on here? This connection string is confusing and is cluttered and what happens when the connection string needs to be changed? Not everyone is familiar with EF and using the modeler isn’t always an option, especially if you’re consuming a library that you don’t have access to and even still, the modeler wouldn’t update the connection string in the consuming project, only the library project itself. A more appropriate config would look something like

  <appSettings>
    <add key="EFDemoConfig_ConnStr" value="MyConnStr"/>
  </appSettings>
<connectionStrings>
    <add name="MyConnStr" connectionString="Data Source=My-PC\SQLEXPRESS;Initial Catalog=myDatabase;Integrated Security=True;MultipleActiveResultSets=True"/>
  </connectionStrings>

In this config there is an appSetting key which holds the value of the connection string name and then a normal connection string entry. This is much more readable and maintainable. But this wouldn’t work because EF needs specific information to be in the connection string. We solve this by creating a partial class.

Writing the solution

Create a new library project and name it EFConfigDemo. Add a new Entity Data Model and leave the default name as ‘Model1.edmx‘. Name the entities connection ‘TestEntities‘ and make the namespace ‘TestModel‘. It doesn’t matter what data source you connect it to, any will do.

After you add the model, take a look at your project. You’ll notice an App.config was added. If you look in the config you will see it looks like

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="TestEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=My-PC\SQLEXPRESS;Initial Catalog=myDatabase;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

Notice the EF specific information such as “metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl”.

Add a new class file called ‘EFTestClass‘ and add a new method called ‘TestEFConnection

namespace EFConfigDemo
{
    public class EFTestClass
    {
        public void TestEFConnection()
        {
            TestEntities context = new TestEntities();
        }
    }
}

This is all the code that is needed.

Add a new Console Application project to the solution and call it ‘DemoTest‘. Add a project reference to the EFConfigDemo project. Change your Main method to match

static void Main(string[] args)
        {
            EFConfigDemo.EFTestClass eftc = new EFConfigDemo.EFTestClass();
            eftc.TestEFConnection();
        }

If you were to run this, you would receive the following exception

"The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid."

This is because the consuming application (your console application project) does not have the config setup properly. Before we correct this, let’s take a look at a scenario

Scenario

You’re developing a 3rd party library for handling data logging and of course, you’re using Entity Framework to handle data access. Everything is going well and the project is finished, tested and ready for release. All that is left is the documentation on how to setup and use the library. As is, your documentation would have to explain how to add the EF specific connection string and then it would have to explain to how to modify it to point to the proper data source.

Due to the complexity of the EF specific connection string, this could cause some serious issues if a developer accidentally deleted/added something they shouldn’t have.

The better documentation would simply explain how to add an appSettings entry and point it to the proper connection string name. Done.

Fixing the EF config

Back in the library project add a new class and call it ‘TestEntities.cs‘ and add the following code

using System.Configuration;

namespace EFConfigDemo
{
    public partial class TestEntities
    {
        public static string ConnectionString = string.Empty;

        private static readonly string _appConfigKey = "EFDemoConfig_ConnStr"; //Setting name, can be anything but should be clear
        private static readonly string _edmxPath = "Model1"; //This can be extracted from the config, otherwise it's Namespace + Entities name

        static TestEntities()
        {
            if (string.IsNullOrEmpty(ConfigurationManager.AppSettings[_appConfigKey]))
                throw new System.Configuration.ConfigurationException(_appConfigKey + " was not found in Application Settings");

            string connStrName = ConfigurationManager.AppSettings[_appConfigKey];

            if (ConfigurationManager.ConnectionStrings[connStrName] == null)
                throw new System.Configuration.ConfigurationException(connStrName + " was not found in Connection Strings");

            string connStr = ConfigurationManager.ConnectionStrings[connStrName].ConnectionString;

            if (string.IsNullOrEmpty(connStr))
                throw new System.Configuration.ConfigurationException(connStrName + " cannot have an empty connection string");

            string metaData = @"metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string='{1}'";

            ConnectionString = string.Format(metaData, _edmxPath, connStr);
        }

        public static TestEntities New()
        {
            TestEntities context = new TestEntities(ConnectionString);

            return context;

        }
    }
}
Note: You will need to add a reference to 'System.Configuration' to the library project.

We start by creating a partial class for our entities context class. At the top are two settings, the first is for the appSettings key. This is what the code will look for in the config. The value of this appSetting will be the name of the connection string defined in the connection string section of the config file. The second setting is the name of the edmx file. If you’re not sure what this is, you can find it in the default connection string that EF generates. In our case it’s ‘Model1‘.

When the entities context (TestEntities in our example) is first initialized, an EF specific connection string is built by getting the actual connection string from the config and then adding the EF specific information to it. The New() method builds a context by passing in the connection string we built in the constructor and is how we will request our context from now on.

Change the TestEFConnection() method in the EFTestClass.cs file to use the new factory method

 public void TestEFConnection()
        {
            //TestEntities context = new TestEntities();
            TestEntities context = TestEntities.New();
        }

Wrap up

Add an Application Configuration to the DemoTest project and add the following code

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="EFDemoConfig_ConnStr" value="MyConnStr"/>
  </appSettings>
  <connectionStrings>
    <add name="MyConnStr" connectionString="Data Source=My-PC\SQLEXPRESS;Initial Catalog=myDataBase;Integrated Security=True;MultipleActiveResultSets=True"/>
  </connectionStrings>
</configuration>

Now run the project and everything will be just fine.

Advertisements