Строки подключения для 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="Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True"" 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="Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=Entity;MultipleActiveResultSets=True"" 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="Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=SOMEPASSWORD;MultipleActiveResultSets=True"" 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="Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True"" 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="Data Source=SOMESERVER;Initial Catalog=SOMECATALOG;Persist Security Info=True;User ID=Entity;Password=Entity;MultipleActiveResultSets=True"" 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, затем вы поймете, что не так.
- У вас есть две разные модели, Entity и ModEntity
- Это означает, что у вас есть два разных контекста, каждый из которых имеет свою собственную модель хранения, концептуальную модель и отображение между ними.
- Вы просто объединили строки, но как контекст Entity узнает, что он должен получить entity.csdl, а ModEntity получит modentity.csdl? Ну, кто-то может написать какой-то интеллектуальный код, но я не думаю, что это главная роль команды разработчиков EF.
- Также machine.config - плохая идея.
- Если веб-приложения перемещаются на другой компьютер, или в среду общего хостинга, или в целях обслуживания, это может привести к проблемам.
- Все смогут получить к нему доступ, вы делаете это небезопасным. Если кто-либо может развернуть веб-приложение или любое приложение.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:
xml version = "1.0" encoding = "utf-8"?><Конфигурация>ConnectionStrings> AppSettings> Конфигурация>
ConnectionManager заменит ConnectionString и Provider на то, что когда-либо находится в App.Config.
Вы можете использовать один и тот же ConnectionManager для всех ObjectContexts (чтобы они все считывали одни и те же настройки из App.Config), или отредактировать T4, чтобы он создавал один ConnectionManager для каждого (в своем собственном пространстве имен), чтобы каждый читал отдельные настройки.
Я понимаю, что вам нужна одна и та же строка соединения с разными метаданными. Таким образом, вы можете использовать строку подключения, как указано ниже, и заменить "" часть. Я использовал вашу данную connectionString в той же последовательности.
connectionString="<METADATA>provider=System.Data.SqlClient;provider connection string="Data Source=SomeServer;Initial Catalog=SomeCatalog;Persist Security Info=True;User ID=Entity;Password=SomePassword;MultipleActiveResultSets=True""
Для первого соединения 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.