英文:
How to register SQL provider for Entity Framework 6 in runtime?
问题
我正在开发使用Entity Framework 6的dll(该dll用于.Net Framework 4.8),并尝试在运行时设置SQL提供程序,因为该dll必须独立工作,不依赖任何配置文件。原因是它将上传到BPM工具,该工具将在需要时部署dll,因此我不想修改多个配置文件。
为了使dll在这种情况下与Entity Framework配合使用,我使用ILMerge创建单个dll,其中包含我的dll、EntityFramework.dll和EntityFramework.SqlServer.dll。
我已经成功在运行时通过为DbContext创建构造函数来指定连接字符串,该构造函数调用以连接字符串作为参数的基本方法,并且它起作用。我已经在Visual Studio中针对本地数据库进行了测试,没有问题。
然后我上传了dll到BPM工具并调用了测试方法,但产生了以下错误:
“找不到与不变名称'System.Data.SqlClient'的ADO.NET提供程序的Entity Framework提供程序。确保在应用程序配置文件的'entityFramework'部分中注册了提供程序。”
我尝试了几种解决方法,最近的一种取得了一些进展,即修改了DbConfiguration。它解决了提供程序的问题,但引入了新问题:
“在建立到SQL Server的连接时发生与网络相关或特定于实例的错误。找不到服务器或服务器不可访问。请验证实例名称是否正确,并验证SQL Server是否已配置为允许远程连接。(提供程序:SQL Network Interfaces,错误:26 - 无法定位服务器/指定的实例)”
此问题在启动后60秒后出现,因此似乎是连接的默认超时。
DbContext和DbConfiguration的代码如下:
public class SqlProviderDbConfiguration : DbConfiguration
{
public SqlProviderDbConfiguration()
{
// 程序注册提供程序
SetDefaultConnectionFactory(new SqlConnectionFactory());
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(SqlProviderDbConfiguration))]
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
static BloggingContext()
{
// 设置配置
DbConfiguration.SetConfiguration(new SqlProviderDbConfiguration());
}
// 通过调用接受连接字符串的DbContext构造函数指定连接字符串
public BloggingContext(string connectionString) : base(connectionString)
{
}
// 默认构造函数,在我的代码中未使用但仍在EF工作时运行
public BloggingContext()
{
}
}
调用来自BPM或测试的方法的开头:
public int TestConnection(string blogName, string connectionString)
{
// 更新数据库架构,以防它不是最新版本
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BloggingContext, Configuration>());
int blogId = -1;
using (var db = new BloggingContext(connectionString))
{
var blog = new Blog { Name = blogName };
db.Blogs.Add(blog);
db.SaveChanges();
问题出现在db.Blogs.Add(blog);
行。
从Visual Studio中我传递连接字符串Data Source=(localdb)\mssqllocaldb;Initial Catalog=EmbeddedEntityFrameworkTest.BloggingContext;Integrated Security=True;MultipleActiveResultSets=True
,这只是默认值的复制,以前在DbConfiguration中的更改之前起作用。
作为一种合理性检查,我尝试使用相同的连接字符串使用SqlConnection打开连接,它可以毫无问题地打开:
using(SqlConnection xx = new SqlConnection(connectionString))
{
xx.Open();
}
我尝试找出为什么找不到服务器,但迄今为止没有成功。我会继续寻找,但如果有人对我做错或遗漏了什么有想法,我会非常感激。
提前感谢您。
最好的问候,
Ondrej
英文:
I'm working on dll that is using Entity Framework 6 (dll is for .Net Framework 4.8) and I'm trying to set SQL provider in runtime because dll has to work alone, without any config file. Reason is that it will be uploaded to BPM tool that will deploy dll when and where it is needed so I would have to modify multiple config files which I would like to avoid.
To make dll work with Entity Framework in this situation I'm using ILMerge to create single dll that contains my dll, EntityFramework.dll and EntityFramework.SqlServer.dll.
I have managed specify connection string in runtime by creating constructor for DbContext, that is calling base method that takes connection string as a parameter and it worked. I have tested from Visual Studio against local DB without issues.
Then I have uploaded dll to BPM tool and called test method which produced following error:
No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SqlClient'. Make sure the provider is registered in the 'entityFramework' section of the application config file
I have tried several variations on how to solve this and latest, that yielded some progress is modifying DbConfiguration. It resolved provider issues but introduced new one:
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
This issues rises 60 second after start so it seems like default timeout for connection.
Code for DbContext and DbConfiguration is following:
public class SqlProviderDbConfiguration : DbConfiguration
{
public SqlProviderDbConfiguration()
{
// Register the provider programmatically
SetDefaultConnectionFactory(new SqlConnectionFactory());
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(SqlProviderDbConfiguration))]
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
static BloggingContext()
{
// Set the configuration
DbConfiguration.SetConfiguration(new SqlProviderDbConfiguration());
}
// Specify connection string by calling DbContext constructor that takes connection string
public BloggingContext(string connectionString) : base(connectionString)
{
}
// Default constructor, fails to compile without it, is not used in my code but still runs when EF is working
public BloggingContext()
{
}
}
Beginning of method that is called from BPM or in tests:
public int TestConnection(string blogName, string connectionString)
{
// update database schema in case it is not on latest version
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BloggingContext, Configuration>());
int blogId = -1;
using (var db = new BloggingContext(connectionString))
{
var blog = new Blog { Name = blogName };
db.Blogs.Add(blog);
db.SaveChanges();
Issues appears on line db.Blogs.Add(blog);
From Visual Studio I'm passing connection string Data Source=(localdb)\mssqllocaldb;Initial Catalog=EmbeddedEntityFrameworkTest.BloggingContext;Integrated Security=True;MultipleActiveResultSets=True
which is just copy of default value and it worked before changes in DbConfiguration.
As a sanity check, I have tried to open connection using SqlConnection with same connection string that is passed to DbContext and it opens without any issue:
using(SqlConnection xx = new SqlConnection(connectionString))
{
xx.Open();
}
I have tried to find why server is not found but with no luck so far. I will continue in search but if anyone have idea what I'm doing wrong or missing, I would appreciate any help.
Thank you in advance.
Best regards,
Ondrej
答案1
得分: 0
I have managed to find working solution. I have limited DbConfiguration only to
> SetProviderServices("System.Data.SqlClient",
> SqlProviderServices.Instance);
and passed SqlConnection with custom connection string to DbContext in constructor.
This solution works from Visual Studio with both inbuild database and SQL server and it is also working from BPM tool with SQL server while only single dll is deployed without config file or modifications to BPM's config. Deployed dll contains my dll, EntityFramework.dll and EntityFramework.SqlServer.dll (merged using ILMerge).
Limitation is obviously specifying SqlConnection so other providers wont work but it might be possible to expand on this concept further.
public class SqlProviderDbConfiguration : DbConfiguration
{
public SqlProviderDbConfiguration()
{
// Register the provider programmatically
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(SqlProviderDbConfiguration))]
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
static BloggingContext()
{
// Set the configuration
DbConfiguration.SetConfiguration(new SqlProviderDbConfiguration());
}
// create my own SQL connection
public BloggingContext(string connectionString)
: base(new SqlConnection(connectionString), true)
{
}
public BloggingContext()
{
}
}
public int TestConnection(string blogName, string connectionString)
{
// use DbContext with custom connection string
using (var db = new BloggingContext(connectionString))
{
// work with context as usual
英文:
I have managed to find working solution. I have limited DbConfiguration only to
> SetProviderServices("System.Data.SqlClient",
> SqlProviderServices.Instance);
and passed SqlConnection with custom connection string to DbContext in constructor.
This solution works from Visual Studio with both inbuild database and SQL server and it is also working from BPM tool with SQL server while only single dll is deployed without config file or modifications to BPM's config. Deployed dll contains my dll, EntityFramework.dll and EntityFramework.SqlServer.dll (merged using ILMerge).
Limitation is obviously specifying SqlConnection so other providers wont work but it might be possible to expand on this concept further.
public class SqlProviderDbConfiguration : DbConfiguration
{
public SqlProviderDbConfiguration()
{
// Register the provider programmatically
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(SqlProviderDbConfiguration))]
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
static BloggingContext()
{
// Set the configuration
DbConfiguration.SetConfiguration(new SqlProviderDbConfiguration());
}
// create my own SQL connection
public BloggingContext(string connectionString)
: base(new SqlConnection(connectionString), true)
{
}
public BloggingContext()
{
}
}
public int TestConnection(string blogName, string connectionString)
{
// use DbContext with custom connection string
using (var db = new BloggingContext(connectionString))
{
// work with context as usual
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论