Переопределение конструктора DbContext, созданного кодом

Я уверен, что я сделал это раньше, но я не могу понять, как сейчас! Мой сценарий:

// 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" с теми же параметрами.

Я могу понять, почему эти ошибки, но как я могу остановить создание автогенератора в первую очередь, чтобы сделать то, что я пытаюсь достичь?

Ответ 1

Лучшее, что я могу предложить, это метод factory:

private HOLDbEntities(string contextName) : base(contextName) { }

public static HOLDbEntities Create() {
    return new HOLDbEntities(ContextName);
}

и используйте HOLDbEntities.Create(), а не new HOLDbEntities().

Ответ 2

Я проголосовал за предыдущий принятый ответ, потому что это довольно элегантный способ сделать это. Однако другим подходом было бы изменить шаблон T4, который генерирует класс dbContext.

При использовании EF DB сначала у вас есть .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)#>
<#
    }
}
#>

теперь ваш контекстный класс будет генерировать без конструктора, поэтому вы должны иметь возможность идти и создавать его в расширенном классе.

Ответ 3

i изменил context.tt следующим образом:

<#=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);
    }

поэтому я могу объявить метод OnModelConstructed в частичном классе контекста.

Ответ 4

Вот мое решение проблемы. Я редактирую файл 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;

                        }
                    }
                }
            }
        }
    }
}

Ответ 5

Я использовал функцию интерполяции строк в С# 6 в .tt генерации кода .tt.
Сгенерированный код становится

public MyEntities()
            : base($"name={MyConfigurationManager.ConnectionStringKey("MyEntities")}")
{
}

когда вы используете

public <#=code.Escape(container)#>()
    : base($"name={MyConfigurationManager.ConnectionStringKey("<#=container.Name#>")}")

в файле .tt.

public static string ConnectionStringKey(string key) в статическом классе MyConfigurationManager в моем случае добавляет инициалы логина к ключу и проверяет, есть ли у какой-либо строки подключения в ConfigurationManager.ConnectionStrings этот ключ; в этом случае этот ключ возвращается и в противном случае просто возвращает значение по умолчанию ключ.

Так что теперь строка подключения может отличаться для каждого пользователя.
Например

<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" />

означает, что пользователь FB использует последний ключ, а все остальные - первый ключ.