I\'m sure I\'ve done this before at some stage, but I can\'t figure out how to now! My scenario:
// This is generated from EDMX
public partial class HOLDbEnt
i changed the context.tt as follows:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
}
foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
{
// Note: the DbSet members are defined below such that the getter and
// setter always have the same accessibility as the DbSet definition
if (Accessibility.ForReadOnlyProperty(entitySet) != "public")
{
#>
<#=codeStringGenerator.DbSetInitializer(entitySet)#>
<#
}
}
#>
var Method = (typeof(Entities)).GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FirstOrDefault(x => x.Name == "OnModelConstructed");
if (Method!=null) Method.Invoke(this,null);
}
so i can declare a OnModelConstructed method in a partial class of the context.
I used the string interpolations feature of C# 6 in the .tt
code generation file.
The generated code becomes
public MyEntities()
: base($"name={MyConfigurationManager.ConnectionStringKey("MyEntities")}")
{
}
when you use
public <#=code.Escape(container)#>()
: base($"name={MyConfigurationManager.ConnectionStringKey("<#=container.Name#>")}")
in the .tt
file.
public static string ConnectionStringKey(string key)
in static class MyConfigurationManager
in my case adds the initials of the login to the key and checks whether any connection string in ConfigurationManager.ConnectionStrings
has that key in which case that key is returned and otherwise just returns the default key.
So now the connectionstring may be different per-user.
E.g.
<add name="MyEntities" connectionString="metadata=res://*/Base.csdl|res://*/Base.ssdl|res://*/Base.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=local.base;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
<add name="MyEntitiesFB" connectionString="metadata=res://*/Base.csdl|res://*/Base.ssdl|res://*/Base.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=local.fb.base;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
means that user F.B. uses the latter key while all others the former key.
Here is my solution to the problem. I edit the TT file as Dylan Hayes suggested and replaced the constructor with my own. In my case, i needed to change the schema names of certain schemas only. I set a variable in the config file to tell me what environment I was in and used the right schema.
using System.Configuration;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Reflection;
using System.Xml;
namespace WS.Framework.WSJDEData
{
public partial class WSJDE : DbContext
{
public WSJDE()
: base("name=WSJDE")
{
ObjectContext context = (this as IObjectContextAdapter).ObjectContext;
string environment = ConfigurationManager.AppSettings.Get("Environment");
const string devCTL = "TESTCTL";
const string devDTA = "TESTDTA";
const string qaCTL = "CRPCTL";
const string qaDTA = "CRPDTA";
const string prodCTL = "PRODCTL";
const string prodDTA = "PRODDTA";
var x = Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.ssdl");
XmlReader[] sReaders = new XmlReader[]
{
XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.ssdl"))
};
XmlReader[] mReaders = new XmlReader[]
{XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.msl"))};
StoreItemCollection sCollection = new StoreItemCollection(sReaders);
ObjectContext objContext = ((IObjectContextAdapter) context).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
EdmItemCollection cCollection = workspace.GetItemCollection(DataSpace.CSpace) as EdmItemCollection;
StorageMappingItemCollection csCollection = new StorageMappingItemCollection(cCollection, sCollection,
mReaders);
workspace.RegisterItemCollection(sCollection);
workspace.RegisterItemCollection(csCollection);
EntityContainer container = workspace.GetItem<EntityContainer>("WSJDEModelStoreContainer", DataSpace.SSpace);
foreach (EntitySetBase entitySetBase in container.BaseEntitySets)
{
string schema = entitySetBase.Schema;
if (schema != null)
{
string name = schema.Substring(schema.Length - 3);
if (name == "CTL")
{
switch (environment)
{
case "Dev":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, devCTL);
break;
case "QA":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, qaCTL);
break;
case "Prod":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, prodCTL);
break;
}
}
if (name == "DTA")
{
switch (environment)
{
case "Dev":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, devDTA);
break;
case "QA":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, qaDTA);
break;
case "Prod":
typeof (EntitySetBase).GetField("_schema",
BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(entitySetBase, prodDTA);
break;
}
}
}
}
}
}
}
I up-voted the previous accepted answer because it is a fairly elegant way of doing it. However another approach would be to modify the T4 template that generates the dbContext Class.
When using EF DB first you have a .edmx file and under that you have an [Entity].Context.tt file. Go into that file and remove (or modify) the following code:
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
}
foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
{
// Note: the DbSet members are defined below such that the getter and
// setter always have the same accessibility as the DbSet definition
if (Accessibility.ForReadOnlyProperty(entitySet) != "public")
{
#>
<#=codeStringGenerator.DbSetInitializer(entitySet)#>
<#
}
}
#>
now your context class will generate without a constructor, so you should be able to go and create one in an extended class.
The best I can suggest is a factory method:
private HOLDbEntities(string contextName) : base(contextName) { }
public static HOLDbEntities Create() {
return new HOLDbEntities(ContextName);
}
and use HOLDbEntities.Create()
rather than new HOLDbEntities()
.