GOAL using Database-First Paradigm (not Code-First) for a deployed desktop wpf application, with unique databases for end users:
1) Have EntityFramework use a connection string determined at run time.
2) Not deploy different app.config files.
Things attempted:
1) Overload the constructor - while successful, this solution is undesired as it leaves the door open for developers to make mistakes, makes unit testing more difficult.
2) Attempted modifying the connection / context factory - threw Exception.
3) Change the default constructor - could be successful, this solution is undesired as the default constructor is autogenerated.
4) Attempted modifying the ConfigurationSettings - threw Exception, it is read-only.
5) Have a customer side deployment of app.config - while plausible, this solution is undesired as it requires a rewrite of our deployment engine.
Help?
EDIT:
Some code related to first item we tried (overloading the constructor):
public partial class DatabaseContext
{
public DatabaseContext(EntityConnection con)
: base(con, true)
{
}
}
public static class DbContextHelper
{
public static string ConnectionString { get; set; }
public static CounterpartDatabaseContext GetDbContext()
{
EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = ConnectionString,
Metadata = #"res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl"
};
EntityConnection con = new EntityConnection(builder.ToString());
return new DatabaseContext(con);
}
}
Usage:
public void SomeMethod()
{
using(DatabaseContext db = DbContextHelper.GetDbContext())
{
// db things
}
}
EDIT code for adding connection string with config manager:
public MainWindow()
{
InitializeComponent();
ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings("DatabaseContext", #"metadata=res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl;provider=System.Data.SqlClient;provider connection string="data source=sqldev;initial catalog=Dev;persist security info=True;user id=user;password=password;MultipleActiveResultSets=True;App=EntityFramework"", "System.Data.EntityClient"));
}
the config manager code just throws an exception, so no point in any code after that.
Generated DatabaseContext class is both partial. With partial you can add code in another file (just remember about partial keyword there) and still be to re-generate everything. Generator will only overwrite the file it generated, all other files with extra additions to that partial class will not evaporate. No problem with mantaining generated and handwritten parts there.
Also, the generated class is not sealed. You can inherit from it. So, instead of using DatabaseContext directly, you might try inheriting from it and start using the derived class. This derived class will not inherit the constructors, but will inherit all other public important things. You will be able then to provide your own constructor, even default one, that will i.e. call parameterized base class ctor. Actually, I have not tried it that way, but it looks simple and may work.
What I propose is not using DbContextHelper.GetContext() (which is static obviously) (which you think the devs may misuse or forget), but rolling in your own DbContext class.
In the project where you have the EDMX and generated DatabaseContext context, add a file with:
public partial class DatabaseContext
{
protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}
it will add a new overload, it will expose the base DbContext constructor that takes the connstring.
Then add another file to that:
public class ANewContext : DatabaseContext
{
public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}
and that's all. Since your helper was static anyways, then we can call it like that. Just change it to return the connstring props, which it had needed to determine anyways.
Now rename the classes:
DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext
or something like that, and I bet noone will be ever confused as to which one of them should be used everywhere. Usage:
public void SomeMethod()
{
using(var db = new DatabaseContext()) // that's ANewContext after renaming
{
...
}
}
With partial in the InternalDatabaseContextDontUseMe, you will be able to regenerate the model, and the extra added ctor will not be deleted. With one extra inheritance level, the autogenerated default constructor will be hidden, and devs using the derived class will not be able to accidentally call it, they'll receive new default ctor that will do what's needed.
If you are really interested in hearing about what I found while digging in the EF source, LazyContext, Factories, Resolvers and etc, look at this article of fine. I put there everything I could recall today and while it's somewhat chaotic, it may help you if you like digging&decompiling. Especially the EntityConnection-DbProviderFactory-Resolvers mentioned at the end.
Related
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
}
}
To make the above code work i have added below line in web.config
But for other project where i am using the assembly reference i am getting exception:
{"The default DbConfiguration instance was used by the Entity
Framework before the 'EntityFrameworkConfiguration' type was
discovered. An instance of 'EntityFrameworkConfiguration' must be set
at application start before using any Entity Framework features or
must be registered in the application's config file. See
http://go.microsoft.com/fwlink/?LinkId=260883 for more information."}
Your question doesn't state how you are using this custom DbConfiguration.
You could get this error a couple of different ways.
Configuration style setup
as described here: Entity Framework Config File Settings
Configuration as code
as described here: Entity Framework Code-Based Configuration (EF6 onwards)
You can hack at this style by doing things like DbConfiguration.SetConfiguration(xxxx). I didnt find this useful at all.
What this really comes down to is how you construct your DbContext.
Configuration file style constructors
https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/DbContext.cs#L75
With no arguments - EF6 is uses the configuraiton files to determine the right DbCofniguration to use
with some "connection string-like" arguments again EF6 is using configuration files to determine the DbConfiguration
no config, or bad config - and you will get this sort or exception
Configuration as Code style constructors
https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/DbContext.cs#L139
I think this yields better control.
Attribute your DbContext, then use a manually created DbConnection
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
}
}
[DbConfigurationType(typeof(EntityFrameworkConfiguration))]
public class MyContext : DbContext
{
public MyContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{ }
public DbSet<Stuff> Stuff { get; set; }
}
using(var conn = new SqlConnection(asqlserverConnectionString))
using (var db = new MyContext(conn, true))
{
var value = await db.Stuff.Where(s => s.xxx.Equals(primaryKey)).Select(s => new { s.BinaryContent } ).SingleOrDefaultAsync();
}
If you are using Code-based configuration, try updating the config file thusly:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
...Your EF config...
</entityFramework>
In my case i have two different edmx files and both are in different class libraries.
I got this error when i added those two libraries in the same project.
I don't have any single clue how is that sorted out but; when i call any method from my first DbContext class, the second one worked like miracle happened. It was throwing this error when second context class called first.
My Ef version is: 6.4
We are wrapping an existing logging library in our own logging service in a C# application to surround it with predefined methods for certain logging situations.
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
public LoggingBlockLoggingService(LogWriter writer)
{
this.writer = writer;
}
....//logging convenience methods, LogWarning(), etc...
}
I would like to modify this implementation so that it takes in the Type of the class that instantiates it (the underlying logger, LogWriter in this case, would be a singleton). So either make this implementation (and the interface ILoggingService) generic:
public class LoggingBlockLoggingService<T> : ILoggingService<T>
{
...
private string typeName = typeof(T).FulName;
...
Or add an additional constructor parameter:
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
private string typeName;
public LoggingBlockLoggingService(LogWriter writer, Type type)
{
this.writer = writer;
this.typeName = type.FullName;
}
....//Include the typeName in the logs so we know the class that is logging.
}
Is there a way to configure this once in Unity when registering our types? I'd like to avoid having to add an entry for every class that wants to log. Ideally, if someone wants to add logging to a class in our project, they'd just add an ILoggingService to the constructor of the class they are working in, instead of adding another line to our unity config to register each class they are working on.
We are using run time/code configuration, not XML
Yes, you can use:
container.RegisterType(typeof(IMyGenericInterface<>), typeof(MyConcreteGenericClass<>));
In your case, when there's simple direct generic-param--to--generic-param mapping the Unity maybe actually handles that, but I doubt that any more advanced cases are not handled, because something at some point of time must provide the mapping of generic-parameters between the types (liek reordering Key-Value vs. Value-Key etc).
If Dave's answer is not enough, I'm fairly sure that you could write a plugin to Unity/ObjectBuilder that would register a new strategy or set of strategies that would cover just any type mapping you would like, including automatic assembly scanning or materialization of generics.
See the series of articles at http://www.orbifold.net/default/unity-objectbuilder-part-ii/ and the section near
Context.Strategies.AddNew< buildkeymappingstrategy >(UnityBuildStage.TypeMapping);
My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.
I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections
ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.
I've an API DLL (API.dll, for example) which, in addition to many other thinks, makes available an abstract class (AbstractClass).
Now making use of that AbstractClass I've implemented it on two different dlls:
First.API.Implementation.dll with ConcreteImplementation1
Second.API.Implementation.dll with ConcreteImplementation2
Both ConcreteImplementation1 and ConcreteImplementation2 are implementation of the same abstract class.
What I want is an application where I can choose which of those two dlls to use and, through that, choose which implementation to use without the user having to change anything within the code and, if possible, without stopping the application.
Some configuration file where I can bring the application to use whatever implementation I want. Something like:
<appconfiguration>
<implementation_to_use>
<dll>First.API.Implementation.dll</dll>
<class>ConcreteImplementation1</class>
</implementation_to_use>
</appconfiguration>
I know near to nothing about dependency injection, apart from its concept, but I guess thats the perfect fit for this task.
I've researched several DI/IoC libraries but I'm not familiar with all the concepts and names. I can use whatever library I want. For what I can say these are the most used: StructureMap, Ninject and Sprint.NET
Moreover, apart from all the dlls and implementation I need to indicate a file to be used by that application. Can I indicate its path in that same file?
I just need some tips and directions to implement such a thing. Some examples using one of those libraries, would be awesome.
Thanks.
To get you started using StructureMap, create a console application, include in it:
structuremap.config:
<?xml version="1.0" encoding="utf-8" ?>
<StructureMap MementoStyle="Attribute">
<DefaultInstance
PluginType="DemoIoC.AbstractBase,DemoIoC"
PluggedType="DemoIoC.ConcreteImplementation1,DemoIoC"
Scope="Singleton" />
</StructureMap>
The PluginType and PluggedType attributes are "FullyQualifiedClassName,AssemblyName"
By default it will look for assemblies in the executable folder, I'm not sure how you would specify another location for the assemblies
There are plenty of options for Scope, e.g. Singleton, Transient, etc
Program.cs:
namespace DemoIoC
{
using System;
using StructureMap;
public static class Program
{
public static void Main(string[] args)
{
// here you initialize structuremap from the config file.
// You could probably use a FileSystemWatcher to reinitialize
// whenever the structuremap.config file changes
ObjectFactory.Initialize(x =>
{
x.UseDefaultStructureMapConfigFile = true;
});
var concrete = ObjectFactory.GetInstance<AbstractBase>();
concrete.Method1();
Console.ReadKey(true);
}
}
}
AbstractBase.cs:
namespace DemoIoC
{
public abstract class AbstractBase
{
public abstract void Method1();
}
}
ConcreteImplementation1.cs:
namespace DemoIoC
{
using System;
public class ConcreteImplementation1 : AbstractBase
{
public override void Method1()
{
Console.WriteLine("Called ConcreteImplementation1");
}
}
}
This is an odd one. All the sudden when I create LINQ to SQL projects in Visual Studio would it not create the following line in the designer, up to three weeks ago or so, it used to create this line, now I have to manually do it. Then if I make changes in the dbml file it removes it and I have to manuallhy add again. huge pain. Here is the lines I am speaking of:
public DataContext():
base(global::System.Configuration.ConfigurationManager.ConnectionString["SiteSqlServer"].ConnectionString, mappingSource)
{
OnCreated();
}
I am using Visual Studio 2008 sp1.
It's a known problem.
You have to implement sort of factory for DB context. Otherwise you will always stuggle with the designer and never know which connection you are using.
public partial class CustomDataContext {
public static CustomDataContext Create()
{
return new CustomDataContext(ConfigurationManager.ConnectionStrings["CustomConnectionString"].ConnectionString);
}
}
Another option is inheriting from your CustomDataContext, and creating a default constructor:
public CustomDataContext : InternalCustomDataContext /* created in designer */
{
protected string GetCustomConnectionString() {
return ConfigurationManager.ConnectionStrings["CustomConnectionString"].ConnectionString;
}
public CustomDataContext() : base(GetCustomConnectionString())
{}
}
Look at the connection properties in the linq2sql designer, try the different settings until you find the one that creates that initializes the connection in the context.
I recall receiving that in a project, and as far as I remember its related to the way you make/configure the connection. I fixed it at the time, but I don't recall the exact property/place.