重写代码生成的DbContext构造函数
本文关键字:构造函数 DbContext 代码生成 重写 | 更新日期: 2023-09-27 18:01:16
我确信我以前在某个阶段做过这个,但我不知道现在该怎么做!我的场景:
// This is generated from EDMX
public partial class HOLDbEntities : DbContext
{
public HOLDbEntities()
: base("name=HOLDbEntities")
{
}
}
现在,我希望这个连接字符串很容易改变(我想从HOLDbEntities实现),所以我需要重写这个构造函数。
我试过:
public partial class HOLDbEntities
{
private const string _contextName = "HOLDbEntities";
public static string ContextName { get { return _contextName; } }
public HOLDbEntities()
: base(ContextName)
{
}
}
但是这抛出一个错误:
HOLDbEntities已经定义了一个名为"HOLDbEntities"的成员,具有相同的参数类型。
我可以理解为什么这个错误,但是我如何阻止构造函数被自动生成在第一个地方,为了做我想要实现的?
我支持之前接受的答案,因为它是一种相当优雅的方法。然而,另一种方法是修改生成dbContext类的T4模板。
当你首先使用EF数据库时,你有一个。edmx文件,在它下面你有一个[Entity]. context。tt文件。进入该文件并删除(或修改)以下代码:
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)#>
<#
}
}
#>
现在您的上下文类将在没有构造函数的情况下生成,因此您应该能够在扩展类中创建一个。
我建议最好使用工厂方法:
private HOLDbEntities(string contextName) : base(contextName) { }
public static HOLDbEntities Create() {
return new HOLDbEntities(ContextName);
}
和使用HOLDbEntities.Create()
而不是new HOLDbEntities()
。
我改变了上下文。
<#=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);
}
所以我可以在上下文的部分类中声明一个 onmodelconstruct 方法
这是我对这个问题的解决方案。我按照Dylan Hayes的建议编辑了TT文件,并用我自己的构造函数替换了它。在我的例子中,我只需要更改某些模式的模式名。我在配置文件中设置了一个变量来告诉我我所处的环境,并使用了正确的模式。
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;
}
}
}
}
}
}
}
我在.tt
代码生成文件中使用了c# 6的字符串插入功能。
生成的代码变成
public MyEntities()
: base($"name={MyConfigurationManager.ConnectionStringKey("MyEntities")}")
{
}
public <#=code.Escape(container)#>()
: base($"name={MyConfigurationManager.ConnectionStringKey("<#=container.Name#>")}")
在.tt
文件。
MyConfigurationManager
中的 public static string ConnectionStringKey(string key)
将登录的首字母添加到密钥中,并检查ConfigurationManager.ConnectionStrings
中的任何连接字符串是否具有该密钥,在这种情况下返回该密钥,否则仅返回默认密钥。
所以现在每个用户的connectionstring可能不同。
例如
<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" />
表示用户F.B.使用后一个键,而其他用户使用前一个键。