Строки подключения для Entity Framework

Я хочу поделиться одной и той же информацией базы данных между несколькими объектами в Silverlight... но я хочу, чтобы строка подключения называлась xyz, и чтобы все имели доступ к этой строке подключения из machine.config...

Часть метаданных сущностей будет отличаться, так как я не назвал сущности одинаковыми.

Могу ли я поместить несколько объектов в этот раздел метаданных?

Вот пример. Я хочу использовать эту строку подключения, но учтите, что я поместил несколько объектов в раздел метаданных.

В основном я хочу взять эту строку подключения

<add name="XYZ" connectionString="metadata=res://*/ModEntity.csdl|res://*/ModEntity.ssdl|res://*/ModEntity.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

И эта строка подключения

 <add name="XYZ" connectionString="metadata=res://*/Entity.csdl|res://*/Entity.ssdl|res://*/Entity.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=Entity;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

Чтобы сделать эту строку подключения

<add name="XYZ" connectionString="metadata=res://*/Entity.csdl|res://*/Entity.ssdl|res://*/Entity.msl|res://*/ModEntity.csdl|res://*/ModEntity.ssdl|res://*/ModEntity.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=SOMEPASSWORD;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

Но это просто не работает. Ни один проект не может подключиться к нему.

string encConnection = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
Type contextType = typeof(test_Entities);
object objContext = Activator.CreateInstance(contextType, encConnection);
return objContext as test_Entities; 

6 ответов

Решение

К сожалению, объединение нескольких контекстов сущностей в одно именованное соединение невозможно. Если вы хотите использовать именованные строки подключения из файла.config для определения подключений Entity Framework, у каждого из них должно быть свое имя. По соглашению это имя обычно является именем контекста:

<add name="ModEntity" connectionString="metadata=res://*/ModEntity.csdl|res://*/ModEntity.ssdl|res://*/ModEntity.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
<add name="Entity" connectionString="metadata=res://*/Entity.csdl|res://*/Entity.ssdl|res://*/Entity.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=Entity;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

Однако, если вы столкнетесь с конфликтами в пространстве имен, вы можете использовать любое имя и просто передать правильное имя в контекст при его создании:

var context = new Entity("EntityV2");

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

Другой вариант - программно создать всю строку соединения каждого контекста, а затем передать всю конструкторскую строку (а не только имя).

// Get "Data Source=SomeServer..."
var innerConnectionString = GetInnerConnectionStringFromMachinConfig();
// Build the Entity Framework connection string.
var connectionString = CreateEntityConnectionString("Entity", innerConnectionString);
var context = new EntityContext(connectionString);

Как насчет чего-то вроде этого:

Type contextType = typeof(test_Entities);
string innerConnectionString = ConfigurationManager.ConnectionStrings["Inner"].ConnectionString;
string entConnection = 
    string.Format(
        "metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string=\"{1}\"",
        contextType.Name,
        innerConnectionString);
object objContext = Activator.CreateInstance(contextType, entConnection);
return objContext as test_Entities; 

... со следующим в вашем machine.config:

<add name="Inner" connectionString="Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />

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

Вместо использования файлов конфигурации вы можете использовать базу данных конфигурации с таблицей systemConfig в заданной области и добавить туда все свои настройки.

CREATE TABLE [dbo].[SystemConfig]  
    (  
      [Id] [int] IDENTITY(1, 1)  
                 NOT NULL ,  
      [AppName] [varchar](128) NULL ,  
      [ScopeName] [varchar](128) NOT NULL ,  
      [Key] [varchar](256) NOT NULL ,  
      [Value] [varchar](MAX) NOT NULL ,  
      CONSTRAINT [PK_SystemConfig_ID] PRIMARY KEY NONCLUSTERED ( [Id] ASC )  
        WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,  
               IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,  
               ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]  
    )  
ON  [PRIMARY]  

GO  

SET ANSI_PADDING OFF  
GO  

ALTER TABLE [dbo].[SystemConfig] ADD  CONSTRAINT [DF_SystemConfig_ScopeName]  DEFAULT ('SystemConfig') FOR [ScopeName]  
GO 

С такой таблицей конфигурации вы можете создавать такие строки:

Затем из вашего приложения dal(s), обертывающего EF, вы можете легко получить необходимую конфигурацию.
Если вы не используете dal(s) и не работаете в проводном режиме напрямую с EF, вы можете создать Entity из таблицы SystemConfig и использовать значение в зависимости от того, в каком приложении вы находитесь.

Сначала попытайтесь понять, как работает строка Entity Framework Connection, затем вы поймете, что не так.

  1. У вас есть две разные модели, Entity и ModEntity
  2. Это означает, что у вас есть два разных контекста, каждый из которых имеет свою собственную модель хранения, концептуальную модель и отображение между ними.
  3. Вы просто объединили строки, но как контекст Entity узнает, что он должен получить entity.csdl, а ModEntity получит modentity.csdl? Ну, кто-то может написать какой-то интеллектуальный код, но я не думаю, что это главная роль команды разработчиков EF.
  4. Также machine.config - плохая идея.
  5. Если веб-приложения перемещаются на другой компьютер, или в среду общего хостинга, или в целях обслуживания, это может привести к проблемам.
  6. Все смогут получить к нему доступ, вы делаете это небезопасным. Если кто-либо может развернуть веб-приложение или любое приложение.NET на сервере, он получит полный доступ к вашей строке подключения, включая конфиденциальную информацию о пароле.

Другой альтернативой является то, что вы можете создать свой собственный конструктор для своего контекста и передать свою собственную строку подключения, а также можете написать некоторые условия if и т. Д. Для загрузки значений по умолчанию из web.config.

Лучше всего было бы оставить строки подключения такими, какие они есть, назначить пулу приложений идентификатор, который будет иметь доступ к вашему серверу баз данных и не включать имя пользователя и пароль в строку подключения.

Чтобы разрешить одному и тому же edmx доступ к нескольким базам данных и поставщикам баз данных, и наоборот, я использую следующую технику:

1) Определите ConnectionManager:

public static class ConnectionManager
{
    public static string GetConnectionString(string modelName)
    {
        var resourceAssembly = Assembly.GetCallingAssembly();

        var resources = resourceAssembly.GetManifestResourceNames();

        if (!resources.Contains(modelName + ".csdl")
            || !resources.Contains(modelName + ".ssdl")
            || !resources.Contains(modelName + ".msl"))
        {
            throw new ApplicationException(
                    "Could not find connection resources required by assembly: "
                    + System.Reflection.Assembly.GetCallingAssembly().FullName);
        }

        var provider = System.Configuration.ConfigurationManager.AppSettings.Get(
                        "MyModelUnitOfWorkProvider");

        var providerConnectionString = System.Configuration.ConfigurationManager.AppSettings.Get(
                        "MyModelUnitOfWorkConnectionString");

        string ssdlText;

        using (var ssdlInput = resourceAssembly.GetManifestResourceStream(modelName + ".ssdl"))
        {
            using (var textReader = new StreamReader(ssdlInput))
            {
                ssdlText = textReader.ReadToEnd();
            }
        }

        var token = "Provider=\"";
        var start = ssdlText.IndexOf(token);
        var end = ssdlText.IndexOf('"', start + token.Length);
        var oldProvider = ssdlText.Substring(start, end + 1 - start);

        ssdlText = ssdlText.Replace(oldProvider, "Provider=\"" + provider + "\"");

        var tempDir = Environment.GetEnvironmentVariable("TEMP") + '\\' + resourceAssembly.GetName().Name;
        Directory.CreateDirectory(tempDir);

        var ssdlOutputPath = tempDir + '\\' + Guid.NewGuid() + ".ssdl";

        using (var outputFile = new FileStream(ssdlOutputPath, FileMode.Create))
        {
            using (var outputStream = new StreamWriter(outputFile))
            {
                outputStream.Write(ssdlText);
            }
        }

        var eBuilder = new EntityConnectionStringBuilder
        {
            Provider = provider,

            Metadata = "res://*/" + modelName + ".csdl"
                        + "|" + ssdlOutputPath
                        + "|res://*/" + modelName + ".msl",

            ProviderConnectionString = providerConnectionString
        };

        return eBuilder.ToString();
    }
}

2) Измените T4, который создает ваш ObjectContext, чтобы он использовал ConnectionManager:

public partial class MyModelUnitOfWork : ObjectContext
{
    public const string ContainerName = "MyModelUnitOfWork";
    public static readonly string ConnectionString
        = ConnectionManager.GetConnectionString("MyModel");

3) Добавьте следующие строки в App.Config:

<Конфигурация>
  
    
  
  
    
    
  

ConnectionManager заменит ConnectionString и Provider на то, что когда-либо находится в App.Config.

Вы можете использовать один и тот же ConnectionManager для всех ObjectContexts (чтобы они все считывали одни и те же настройки из App.Config), или отредактировать T4, чтобы он создавал один ConnectionManager для каждого (в своем собственном пространстве имен), чтобы каждый читал отдельные настройки.

Я понимаю, что вам нужна одна и та же строка соединения с разными метаданными. Таким образом, вы можете использовать строку подключения, как указано ниже, и заменить "" часть. Я использовал вашу данную connectionString в той же последовательности.

connectionString="<METADATA>provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True&quot;"

Для первого соединения String заменить <METADATA> с "metadata=res://*/ModEntity.csdl|res://*/ModEntity.ssdl|res://*/ModEntity.msl;"

Для второго соединения String заменить <METADATA> с "metadata=res://*/Entity.csdl|res://*/Entity.ssdl|res://*/Entity.msl;"

Для третьего соединения заменить строку <METADATA> с "metadata=res://*/Entity.csdl|res://*/Entity.ssdl|res://*/Entity.msl|res://*/ModEntity.csdl|res://*/ModEntity.ssdl|res://*/ModEntity.msl;"

Удачного кодирования!

Приложения Silverlight не имеют прямого доступа к machine.config.

Другие вопросы по тегам