Overriding how serenity framework handles [ConnectionKey] attribute - c#

Serenity has the class attribute ConnectionKey which allows you to specify the key for which connection string you want to use.
Code Example below:
[ConnectionKey("Default"), Module("Administration"), TableName("Languages")]
[DisplayName("Languages"), InstanceName("Language")]
[ReadPermission(PermissionKeys.Translation)]
[ModifyPermission(PermissionKeys.Translation)]
[LookupScript(typeof(Lookups.LanguageLookup))]
public sealed class LanguageRow : Row, IIdRow, INameRow
{
[DisplayName("Id"), Identity]
public Int32? Id
{
get { return Fields.Id[this]; }
set { Fields.Id[this] = value; }
}
}
In my case I am making use of AWS Secret Manager to hide any sensitive information so the connection string itself isn't sitting in my appsettings but rather an AWS secret key. Therefore when this code gets hit it throws an error because the secret key isn't a valid connection string.
To get the actual connection string I first need to make a request to the AWS Secret Manager.
In their documentation about a third of the way down the page, in the SqlConnections.New method section, they mention how I can specify a connection string that doesn't exist in my appsettings. However, I don't see how I could apply this solution to my particular problem.
Is there a way to override how Serenity handles this ConnectionKey or any other workaround for this problem?

Serenity has connection source what injected by dependency injection. You can find injection line there. If you want to manage it yourself just remove AddSqlConnections from startup and add manually your connection string source what implement IConnectionStrings interface.
There is default IConnectionStrings implementation what you can use as example.

My final solution to the problem was to make use of the static method inside the SqlConnections class, SetConnection which allowed me to associate a different connection string with the default connectionKey.
var csInfo = SqlConnections.GetConnectionString("SecretConnectionKey");
var connectionString = SecretManagerService.GetSecretAsync(csInfo.ConnectionString, RegionEndpoint.USEast1).Result;
SqlConnections.SetConnection("Default", connectionString, csInfo.ProviderName);
This piece of code was placed at the bottom of the Configure method in startup.
For this to work you would also need to add an empty default ConnectionString inside your appsettings.json files.
{
"Data": {
"SecretConnectionKey": {
"ConnectionString": "secretkey",
"ProviderName": "System.Data.SqlClient"
},
"Default": {
"ConnectionString": "",
"ProviderName": "System.Data.SqlClient"
}
},
I recognise that this is indeed a hacky approach but a time effective solution couldn't be found and this would be replaced with the upgrade to .Net 5.0.

Related

.NET Core API call to a method in a .NET Project is saying it can't find Connection strings in the app.config file

I have a Core API project that was originally accessing the database from the controller (bad practice) so I moved the data access methods into an existing .NET class library where the connection strings are stored in the app.config file. The same connection strings are also located in the Core API project's appsettings.json/appsettings.development.json folders.
I then tried to access this new .NET class library method (containing the call to the database) from my API, but I'm getting the "can't find 'connectionstringname' in app.config" error. I'm new to .NET but a colleague was saying perhaps the app.config isn't getting pulled into the API's bin folder. Has anyone had problems with .NET Core projects struggling to communicate with .NET project's connection strings and how could I go about troubleshooting this?
1.) I tried pasting the app.config file into the API's bin folder, to no avail (same error.)
I've seen a few answers on SO but haven't been able to implement them successfully, so I apologize if this has been answered before.
You need to completely throw out your dot-net-framework understanding of config settings.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0
Note the value of the url.
aspnet core fundamentals configuration
In DotNet-Core:
Highly recommend creating a first class object. (config-value-holder-poco)
And in your Ioc Registrations, inject the config-value-holder-poco into the Ioc (aka, "servicecollection" in dn-core).
And then constructor inject your settings poco holder.
As per the article.
Json "section"
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
First class config-value-holder-poco.
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
IoC registration.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
}
Constructor Injection:
public class Test2Model : PageModel
{
private readonly PositionOptions _options;
public Test2Model(IOptions<PositionOptions> options)
{
_options = options.Value;
}
public ContentResult OnGet()
{
return Content($"Title: {_options.Title} \n" +
$"Name: {_options.Name}");
}
}
and now you can take advantage of the "refresh-rate" BUILT IN options for how these are treated.
https://medium.com/#kmar.ayush/eli5-ioptions-vs-ioptionssnaphot-vs-ioptionsmonitor-fab1d7e26a75
IOptions vs IOptionsSnaphot vs IOptionsMonitor

Is it possible to Pass Azure Function ServiceBusTrigger connection at runtime

My Code Look like this :
public class Startup : IWebJobsStartup
{
public async void Configure(IWebJobsBuilder builder)
{
Get Connection string Via HTTP Service.
ServiceBusConnectionString = jArray["connectionString"].ToString();
}
}
And I want to Pass Connection Like this
[FunctionName("FunctionTopicMessageLogger")]
public void Run([ServiceBusTrigger("topic", "FunctionTopicMessageLogger",Connection = **ServiceBusConnectionString** )]Message mySbMsg)
{
}
This is my Local.setting.json file for first time
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"Connectinstring": " "
}
}
Problem is that connection only take Const Parameter (So it's not allow to change) So i am trying to write in a local.setting.json and trying to read it from there, I am getting error first time when i am running , and from second time connection is there so it's working fine.
Is i am doing something wrong Please suggest.
There are two important parts in the solution for your problem.
During creation of Azure Function with Service Bus trigger you have to specify the name of the connection string parameter that will be used to retrieve connection string value. In this case if you type "ServiceBusConnectionString" you have to add such configuration parameter in the local.settings.json file in the "ConnectionStrings" section:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
},
"ConnectionStrings": {
"ServiceBusConnectionString": "xxx"
}
}
After depoying your function to Azure, you should use "Configuration" section in the portal and you should avoid using "local.settings.json" file because it is only for local development:
I hope this will help.
If your intention is to use the key Connectionstring from the local.settings.json, you should be able to get the value from there automatically just by doing this:
public static void Run([ServiceBusTrigger("topic", "FunctionTopicMessageLogger", Connection = "connectionString")]string mySbMsg)
The value for Connection is the name of the Key from the local.settings.json file.
If you don't have a lot of different Service Bus, you can create one function for each different connectionString you have.
Each function needs to know the connectionString before starting, and start waiting for message in the service bus.
If you have to do that dynamically, you don't have to use an Azure function, you can use the Service Bus SDK:
Here you can see how to manage a client manually: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-get-started-with-queues
I hope this will help you!

Azure Functions Database Connection String

How do I add or access an app.config file in Azure functions to add a database connection string?
If you're not supposed to add an app.config and there is a better way to access an external database to execute the function please let me know best practices. Thanks!
Jan_V almost nailed it, which led me to experiment with this in the local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true;",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"MyConnectionString": "[YourConnectionStringHere]"
}
}
This allows you to use the ConfigurationManager.ConnectionStrings[] we are all used to.
var sqlConnection = ConfigurationManager
.ConnectionStrings["MyConnectionString"].ConnectionString;
The best way to do this is to add a Connection String from the Azure portal:
From your Function App UI, click Function App Settings
Settings / Application Settings
Add connection strings
They will then be available using the same logic as if they were in a web.config, e.g.
var conn = System.Configuration.ConfigurationManager
.ConnectionStrings["MyConn"].ConnectionString;
Or if you're using a non-.NET language, you can use App Settings instead, which become simple environment variables at runtime that your functions can access.
Configuration Manager will be replaced by the new Asp.Net Core Configuration System in Functions Runtime v2.
So if you are using .Net Core you should follow John Gallants Blog article:
https://blog.jongallant.com/2018/01/azure-function-config/
Works with local.settings.json and Settings in Azure Function
Works with App Settings and Connection Strings
Todd De Land's answer only works for local environment. However per this doc, published Azure Function needs connection strings be stored as app settings and retrieved by GetEnvironmentVariable.
Adding System.Configuration assembly reference is unnecessary.
string cs = Environment.GetEnvironmentVariable("MyConnectionString",EnvironmentVariableTarget.Process);
Here are the steps to make environment strings retrievable for both local and published environment
To support local environment, in local.settings.json, specify your connection strings inside Values node
To support published environment, go to portal.azure.com > your Azure Function > function node > Application Settings
Finally, call GetEnvironmentVariable from your Azure Function (cant get stackoverflow to display this code correctly)
Thats it.
I went through several similar questions and answers here. Many of them are either misleading or assuming everybody is on the same level and understands how the azure functions are working. there is no answer for newbies like me. I would like to summarize here my solution step by step.
most important thing is that we understand local.settings.json file
IS NOT FOR AZURE. it is to run your app in the local as the name is
clearly saying. So solution is nothing to do with this file.
App.Config or Web.Config doesnt work for Azure function connection strings. If you have Database Layer Library you cant overwrite connection string using any of these as you would do in Asp.Net applications.
In order to work with, you need to define your connection string on the azure portal under the Application Settings in your Azure function. There is
Connection strings. there you should copy your connection string of your DBContext. if it is edmx, it will look like as below. There is Connection type, I use it SQlAzure but I tested with Custom(somebody claimed only works with custom) works with both.
metadata=res:///Models.myDB.csdl|res:///Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System.Data.SqlClient;provider
connection string='data source=[yourdbURL];initial
catalog=myDB;persist security info=True;user
id=xxxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework
After you set this up, You need to read the url in your application and provide the DBContext. DbContext implements a constructor with connection string parameter. By default constructor is without any parameter but you can extend this. if you are using POCO classes, you can amend the DbContext class simply. If you use Database generated Edmx classes like I do, you don't want to touch the auto generated edmx classes, instead you want to create partial class in the same namespace and extend this class as below.
This is auto generated DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
this is the new partial class, you create
namespace myApp.Data.Models
{
[DbConfigurationType(typeof(myDBContextConfig))]
partial class myDBEntities
{
public myDBEntities(string connectionString) : base(connectionString)
{
}
}
public class myDBContextConfig : DbConfiguration
{
public myDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
}
After all you can get the connection string from azure settings, in your Azure Function project with the code below and provide to your DbContext
myDBEntities is the name you gave in the azure portal for your connection string.
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;
using (var dbContext = new myDBEntities(connString))
{
//TODO:
}
Addition to the answer from #ToddDeLand.
With a local.settings.json like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true;",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"MyConnectionString": "[YourConnectionStringHere]"
}
}
You can then access your connection string like this, no NuGets needed.
var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:MyConnectionString");
Microsoft recommends this approach here:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library#environment-variables
If you add the connection string to values:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true;",
"AzureWebJobsDashboard": "",
"MyConnectionString": "[YourConnectionStringHere]"
}
}
You can access your connection string like this:
var connectionString = Environment.GetEnvironmentVariable("MyConnectionString");
Sources:
https://stackoverflow.com/a/52219491/3850405
https://github.com/Azure/Azure-Functions/issues/717#issuecomment-400098791
I believe common practice is use environment variables for azure functions, then you can setup the environment variables in the Azure Function:
(Function App Settings -> Configure app settings -> App settings section)
Maybe would be more helpful if you can also let us know which language you are using?
If you are using function runtime v3, then following approach will work for you.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true;",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"MyConnectionString": "[YourConnectionStringHere]"
}
}
In your function's startup file
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
var config = builder.ConfigurationBuilder.Build();
var connectionString = config.GetConnectionString("MyConnectionString");
}
I have tried below code snippet on my local database that seems easy. Let's have a look.
Nuget Extention:
Download following reference from Nuget Package Manager On your project Dependencies part
using System.Data.SqlClient;
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"sqldb_connection": "Data Source=.;Initial Catalog=DatabaseName;Connection Timeout=30;Integrated Security=True;"
}
}
Read Connection On Function Body:
//Read database Connection
var sqlConnection = Environment.GetEnvironmentVariable("sqldb_connection");
Function Read Write Operation Using Connection String:
// Convert all request perameter into Json object
var content = req.Content;
string jsonContent = content.ReadAsStringAsync().Result;
dynamic requestPram = JsonConvert.DeserializeObject<AzureSqlTableClass>(jsonContent);
// Validate required param
if (string.IsNullOrEmpty(requestPram.FirstName))
{
return req.CreateResponse(HttpStatusCode.OK, "Please enter First Name!");
}
if (string.IsNullOrEmpty(requestPram.LastName))
{
return req.CreateResponse(HttpStatusCode.OK, "Please enter Last Name!");
}
//Read database Connection
var sqlConnection = Environment.GetEnvironmentVariable("sqldb_connection");
var responseResults = 0;
//Read Write Uisng Connection String
using (SqlConnection conn = new SqlConnection(sqlConnection))
{
conn.Open();
var text = "INSERT INTO AzureSqlTable VALUES ('" + requestPram.FirstName + "', '" + requestPram.LastName + "', '" + requestPram.Email + "') ";
using (SqlCommand cmd = new SqlCommand(text, conn))
{
responseResults = await cmd.ExecuteNonQueryAsync();
}
conn.Close();
}
return req.CreateResponse(HttpStatusCode.OK, responseResults);
Note: While publish your function on azure portal just replace the connection string on local.settings.json file. It will work
accordingly. See the screen shot below:
Try this method.
public static string GetConnectionString(string name)
{
string conStr = System.Environment.GetEnvironmentVariable($"ConnectionStrings:{name}",
EnvironmentVariableTarget.Process);
// Azure Functions App Service naming
if (string.IsNullOrEmpty(conStr))convention
conStr = System.Environment.GetEnvironmentVariable($"SQLAZURECONNSTR_{name}",
EnvironmentVariableTarget.Process);
return conStr;
}
some of the above suggestions work. However there is a more straight forward way of setting a connection string. It is by using the 'publish' screen one sees after hitting the publish setting. see picture from documentation here
The Best way to handle connection strings is by using "Azure Key Vault". You can store all the important secrets in Key Vault and consume in the Application.
As suggested by other members you can use Application Settings.
help full links: https://learn.microsoft.com/en-us/azure-stack/user/azure-stack-key-vault-manage-portal?view=azs-2002
https://learn.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1
Below worked for me both locally & in Azure for an http trigger function that queries cosmos db
added Microsoft.Azure.WebJobs.Extensions.CosmosDB nuget package reference to project
connection string settings:
local.settings.json
{
"ConnectionStrings": {
"CosmosDBConnection": "AccountEndpoint=foobar;"
}
}
in Azure portal > function apps > platform features > configurations > Application settings > New application settings >
Name: CosmosDBConnection Value: AccountEndpoint=foobar;
update > save
sample c# Azure function
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
[CosmosDB(databaseName:"dbName",
collectionName:"collectionName",
ConnectionStringSetting = "CosmosDBConnection")] DocumentClient documentClient,
ILogger log){
.....
}
You should store connection string in azure key vault and enable MSI on azure function and add access policy on azure key vault to read key value.
Azure Functions Version 4: How to configure your connection string.
Concepts:
A. There are four versions of Azure Functions. The below was written for version 4. Edit your project file if you are unsure of which version you are using. <AzureFunctionsVersion>v4</AzureFunctionsVersion>
B. You must configure both the local dev environment, and the server environment separately. They are different and require different configuration, but the code which uses the configuration will be the same.
C. Use environmental variables. The usual appconfig.json file is not supported in Azure Functions, being a lightweight execution environment. (You could also use AzureKey vault but that is a different topic.)
To set your dev environment variable, add it to local.settings.json Note that this file is NOT published, and applies only to your local machine as the name implies.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"DefaultConnectionString": "MyConnectionString"
}
}
For your published environment, go into the Azure Portal and set it there.

EF migrations need default ctor, however, I don't know the connection string?

EF code first migrations require me to put a default constructor on my DbContext class, this requires me to know the exact connection string - however, I don't know the connection string yet.
My context and the entities reside in a MyProject.Data assembly with no information about the actual database whatsoever. The connection string is in the MyProject.Executable's app.config file. Still I want to put all the migrations into the MyProject.Data project, because that is where they belong.
Is there any way to do EF migrations without having a default CTOR, etc.?
If the connectionstring is in the app.config/Web.config of your main project (the one you will be building), you can use ConfigurationManager.ConnectionStrings to retrieve them.
If you are wondering how it will work if it's in a different project: This will only be executed at runtime. At runtime, your projects have been compiled into one big application, and everything listens to the same app.config/Web.config file (the one that was in your startup project when you built it).
From the link:
var connectionString =
ConfigurationManager.ConnectionStrings["WingtipToys"].ConnectionString;
using (var connection = new SqlConnection(connectionString))
{
//...
}
The same applies for any constructor where you need to know the connectionstring, like your DBContext.
According to this SO answer, you only need to pass the name of your connectionstring, so that means you don't even need to use ConfigurationManager.
From the linked answer:
After reading the docs, I have to pass the name of the connection string instead:
var db = new NerdDinners("NerdDinnerDb");
You can refer to the name of connection string placed in your default start up project. This is why parameter of DbContext constructor is nameOrConnectionString.
public class MyContext : DbContext
{
public MyContext()
: base("Name=NameOfConnectionString")
{
}
}

EF 5 + SQL Server CE 4: How to specify custom location for database file?

I am developing a client system that needs a small local database.
I want to avoid installation of SQL Server Express and have decided to go with SQL Server 4.
I use Entity Framework 5 for data access and have created my custom context.
Everything works fine in development where I can use app.config to either set specific file location or dynamic Data Source=|DataDirectory|\MyDatabase.sdf.
But on deploy I want the database to be located in the users documents folder:
\My Documents\ApplicationName\MyDatabase.sdf
How can I do that?
All I need is actually to be able to set custom connection string in code!
This is what I tried so far:
private MyApplicationDataContext(string connectionString)
: base(connectionString)
{
}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
//var connectionString = string.Format("provider=System.Data.SqlServerCe.4.0;provider connection string=\"Data Source={0}\"", path);
var connectionString = string.Format("Data Source={0}", path);
return new MyApplicationDataContext(connectionString);
}
As you can see I tried two kinds of connection strings but both caused exceptions.
Keyword not supported: 'provider'.
and
The provider did not return a ProviderManifestToken string.
Ah, I finally got it right!
I include the adjusted code if someone else has the same problem.
The trick was to set the connection string on the Database.DefaultConnectionFactory
private MyApplicationDataContext()
{ }
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Set connection string
var connectionString = string.Format("Data Source={0}", path);
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", connectionString);
return new MyApplicationDataContext();
}
In EF 6 this can be done with DbConfiguration:
App.config:
<entityFramework codeConfigurationType="MyDbConfiguration, MyAssembly">
</entityFramework>
And inside your assembly create a class like:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
The SqlCeConnection instance is used to connect to Sql Ce database file. If I want to connect to MSSQL database, I will use SqlConnectionStringBuilder and SqlConnection instances to build my DbConnection instance.
// The ctor for `DbConnection`.
private MyApplicationDataContext(DbConnection conn) : base(conn, true) {}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Connection string builder for `Sql ce`
SqlCeConnectionStringBuilder sb = new SqlCeConnectionStringBuilder();
sb.DataSource = path;
// DbConnection for `Sql ce`
SqlCeConnection dbConn = new SqlCeConnection(sb.ToString());
return new MyApplicationDataContext(dbConn);
}
For EF 6
I arrived at this answer thanks to Matthias's post, but included some worthwhile info from Entity Framework Code-Based Configuration (EF6 onwards).
To specify a custom database location you will need to do some configuration. Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration. Given that my project required the database location to be set dynamically, I went with the code-based configuration, which is described below.
(Note that the config file takes precedence over code-based configuration. In other words, if a configuration option is set in both code and in the config file, then the setting in the config file is used.)
According to EF documentation (above link) there are 4 approaches to implement your custom configuration,which include: Using DbConfiguration, Moving DbConfiguration, Setting DbConfiguration explicitly, and Overriding DbConfiguration.
Using DbConfiguration
Code-based configuration in EF6 and above is achieved by creating a subclass of System.Data.Entity.Config.DbConfiguration. The following guidelines should be followed when subclassing DbConfiguration:
Create only one DbConfiguration class for your application. This class specifies app-domain wide settings.
Place your DbConfiguration class in the same assembly as your DbContext class. (See the Moving DbConfiguration section if you want to change this.)
Give your DbConfiguration class a public parameterless constructor.
Set configuration options by calling protected DbConfiguration methods from within this constructor.
Following these guidelines allows EF to discover and use your configuration automatically by both tooling that needs to access your model and when your application is run.
Example (modified from Matthias's answer):
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
//var directory Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var directory = #"C:\Users\Evan\Desktop\TestFolder"; // Directory may or may not already exist
Directory.CreateDirectory(directory); // create directory if not exists
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
Note that the config file does not need to be altered unless there are existing configuration settings that override your custom configuration. Also, I changed the directory to illustrate that While EF is capable of creating a new database if it doesn't already exist, it will not create parent directories, which is why I included this line: Directory.CreateDirectory(directory). Given that this approach worked for my project I didn't explore the remaining 3 configuration methods, but you can find info on them in the link provided above and I will include the documentation here as a reference in case the link breaks.
Moving DbConfiguration
There are cases where it is not possible to place your DbConfiguration class in the same assembly as your DbContext class. For example, you may have two DbContext classes each in different assemblies. There are two options for handling this.
The first option is to use the config file to specify the DbConfiguration instance to use. To do this, set the codeConfigurationType attribute of the entityFramework section. For example:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
...Your EF config...
</entityFramework>
The value of codeConfigurationType must be the assembly and namespace qualified name of your DbConfiguration class.
The second option is to place DbConfigurationTypeAttribute on your context class. For example:
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}
The value passed to the attribute can either be your DbConfiguration type - as shown above - or the assembly and namespace qualified type name string. For example:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}
Setting DbConfiguration explicitly
There are some situations where configuration may be needed before any DbContext type has been used. Examples of this include:
Using DbModelBuilder to build a model without a context
Using some other framework/utility code that utilizes a DbContext
where that context is used before your application context is used
In such situations EF is unable to discover the configuration automatically and you must instead do one of the following:
Set the DbConfiguration type in the config file, as described in the
Moving DbConfiguration section above
Call the static DbConfiguration.SetConfiguration method during
application startup
Overriding DbConfiguration
There are some situations where you need to override the configuration set in the DbConfiguration. This is not typically done by application developers but rather by thrid party providers and plug-ins that cannot use a derived DbConfiguration class.
For this, EntityFramework allows an event handler to be registered that can modify existing configuration just before it is locked down. It also provides a sugar method specifically for replacing any service returned by the EF service locator. This is how it is intended to be used:
At app startup (before EF is used) the plug-in or provider should
register the event handler method for this event. (Note that this
must happen before the application uses EF.)
The event handler makes a call to ReplaceService for every service
that needs to be replaced.
For example, to repalce IDbConnectionFactory and DbProviderService you would register a handler something like this:
DbConfiguration.Loaded += (_, a) =>
{
a.ReplaceService<DbProviderServices>((s, k) => new MyProviderServices(s));
a.ReplaceService<IDbConnectionFactory>((s, k) => new MyConnectionFactory(s));
};
In the code above MyProviderServices and MyConnectionFactory represent your implementations of the service.
You can also add additional dependency handlers to get the same effect.
Note that you could also wrap DbProviderFactory in this way, but doing so will only effect EF and not uses of the DbProviderFactory outside of EF. For this reason you’ll probably want to continue to wrap DbProviderFactory as you have before.
You should also keep in mind the services that you run externally to your application - e.g. running migrations from Package Manager console. When you run migrate from the console it will attempt to find your DbConfiguration. However, whether or not it will get the wrapped service depends on where the event handler it registered. If it is registered as part of the construction of your DbConfiguration then the code should execute and the service should get wrapped. Usually this won’t be the case and this means that tooling won’t get the wrapped service.

Categories