重写代码生成的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构造函数

我支持之前接受的答案,因为它是一种相当优雅的方法。然而,另一种方法是修改生成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=&quot;data source=localhost;initial catalog=local.base;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" 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=&quot;data source=localhost;initial catalog=local.fb.base;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

表示用户F.B.使用后一个键,而其他用户使用前一个键。