I'm trying to run integration tests for my ASP.NET MVC application using Entity Framework 6.
The error I get is
System.Data.Entity.Core.EntityException: The underlying provider failed on Rollback. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: connection
The code looks like this:
Database.SetInitializer(new PrimaryInitializerTest());
_context = new PrimaryContextTest();
_context.Database.Initialize(true);
using (var dbt = _context.Database.BeginTransaction())
{
dbt.Commit();
dbt.Rollback();
}
I also tried having an dbt.UnderlyingTransaction.Connection.Open() call just below the using statement, and a dbt.UnderlyingTransaction.Connection.Close() call just below the call to Rollback(). That gave me the error Connection is not closed.
PrimaryInitializerTest class
protected override void Seed(PrimaryContextTest context)
{
// (...) Input some values
base.Seed(context);
}
PrimaryContextTest class
public class PrimaryContextTest : DbContext
{
public PrimaryContextTest() : base("PrimaryContextTest")
{
Database.SetInitializer(new DropCreateDatabaseAlways<PrimaryContextTest>());
}
public DbSet<Story> Stories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
}
Connection string
<add name="PrimaryContextTest"
connectionString="Data Source=(LocalDb)\mssqllocaldb;Initial Catalog=PrimaryContextTest;Integrated Security=SSPI;AttachDbFilename=|DataDirectory|\PrimaryContextTest.mdf"
providerName="System.Data.SqlClient" />
Context string
<context type="fcon.DAL.Tests.PrimaryContextTest, fcon, Version=1.0.0.0, Culture=neutral">
<databaseInitializer type="fcon.DAL.Tests.PrimaryInitializerTest, fcon" />
</context>
What could I be doing wrong?
Might mention that the database doesn't exist in the App_Data folder...
You're calling Commit and then Rollback, but the comments point that mistake out.
The error isn't very intuitive, I mean, an ArgumentNullException should never work its way out of an SDK from down the stack.
But I've had this when I've accidentally reused the same transaction instance, or called Commit twice, or tried to rollback twice in layered error recovery logic.
Related
I have an Azure function application that when I debug it in my local PC, it won't give any error and will work correctly, but when I deploy it into the Azure Functions web, it will throw an error indicating that my Entities connection doesn't have the ProviderName specified.
I followed the solutions given by Missing ProviderName when debugging AzureFunction as well as deploying azure function, I have created a myDBContextConfig and added it to the Auto Generated dbContext file from my .edmx, but I still continue to have the same problem after deploying.
Here are some screenshots and config data I'm using:
the Error:
The local.settings.json:
{ "IsEncrypted": false,
"Values": {
//Some Values
},
"ConnectionStrings": {
"Entities": {
"ConnectionString": "metadata=res://*/Entities.csdl|res://*/Entities.ssdl|res://*/Entities.msl;provider=System.Data.SqlClient;provider connection string='data source=ES-HHASQL01\\SQLES;initial catalog=Entities;persist security info=True;Integrated Security=False;User ID=****;Password=****;MultipleActiveResultSets=True;application name=EntityFramework'",
"ProviderName": "System.Data.EntityClient"
}
}
}
And the Azure functions connection String:
The hidden connection follows the style of the quoted text below.
metadata=res:///Models.myDB.csdl|res:///Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System.Data.SqlClient;provider connection string='data source=[yourdbURL];initial catalog=myDB;persist security info=True;user id=xxxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework
If any one can help me with this I would really appreciate it. Thank you.
Edit:
Well I have tested the issue with the code provided by #Joey Cai, and made the next changes:
the new App Settings connection string is has follows:
Data Source=tcp:*.database.windows.net,1433;Initial Catalog=***MultipleActiveResultSets=true;User ID=*;Password=***;Connection Timeout=30;App=EntityFramework;
I added the database configuration class to the Auto generated code from the dbContext has follows:
[DbConfigurationType(typeof(EntitiesConfig))]
public partial class Entities : DbContext
{
public Entities()
: base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
and created the class has especified by: #Joey Cai
namespace SameHasDbContext
{
public class EntitiesConfig : DbConfiguration
{
public EntitiesConfig()
{
SetProviderServices("System.Data.EntityClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
}
But know I'm getting this error when running the azure function only if the connection type is SQLServer or SQLAzure, if it's set to Custom it still shows the ProviderNamer error shown before:
You can't add EF metadata about the connection in Azure Functions, as they do not use an app.config in which to specify it. This is not a part of the connection string, but is metadata about the connection besides the connection string that EF uses to map to a specific C# Class and SQL Provider etc. To avoid this, you could following the code as below.
[DbConfigurationType(typeof(DBContextConfig))]
partial class
{
public Entities(string connectionString)
: base(connectionString)
{
}
}
public class DBContextConfig : DbConfiguration
{
public DBContextConfig()
{
SetProviderServices("System.Data.EntityClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
I have a context which I derive my entity I have 5 databases these database are off the same scheme that I need to switch from at run time. The user would be selecting at the launch of the application which database they would be connecting to.
The database that is of the same scheme is SMBASchedulerEntities its just the catalogue name that is different
public class SourceContext : ContextBase
{
public SMBASchedulerEntities _sourceEntities = new SMBASchedulerEntities();
public SystemDa _systemDB = new SystemDa();
public void AddToPatient(Patient newPatient)
{
_sourceEntities.Patients.Add(newPatient);
SaveChanges();
}
public void AddToAppointmentTypes(AppointmentType AppointmentTypes)
{
_sourceEntities.AppointmentTypes.Add(AppointmentTypes);
SaveChanges();
}
}
As you can see there I reference the entities within my context so I would like to have a property that I can call such as changeDatabase and that it would take affect without restarting the application is that at all possible.
You can pass the name of the connection string you want to connect to, when instantiating your DbContext. First, you declare your DbContext like so:
public class SMBASchedulerEntities : DbContext
{
public SMBASchedulerEntities(string connectionString): base(connectionString)
{
}
}
You keep all your connection strings in your Web.config or App.config (depending on the type of project):
<connectionStrings>
<add name="DefaultConnection1" connectionString="server=localhost;user id=MyAppUser;password=SecretPass;database=MyDatabase1" providerName="System.Data.SqlClient" />
<add name="DefaultConnection2" connectionString="server=localhost;user id=MyAppUser;password=SecretPass;database=MyDatabase2" providerName="System.Data.SqlClient" />
<add name="DefaultConnection3" connectionString="server=localhost;user id=MyAppUser;password=SecretPass;database=MyDatabase3" providerName="System.Data.SqlClient" />
<add name="DefaultConnection4" connectionString="server=localhost;user id=MyAppUser;password=SecretPass;database=MyDatabase4" providerName="System.Data.SqlClient" />
<add name="DefaultConnection5" connectionString="server=localhost;user id=MyAppUser;password=SecretPass;database=MyDatabase5" providerName="System.Data.SqlClient" />
</connectionStrings>
Then you use it like this:
using (var db = new SMBASchedulerEntities("DefaultConnection1"))
{
// use MyDatabase1 through connection string "DefaultConnection1"
}
using (var db = new SMBASchedulerEntities("DefaultConnection2"))
{
// use MyDatabase2 through connection string "DefaultConnection2"
}
Dispose your DbContext
It's recommended to dispose the DbContext after using it. If you still want to use your idea with SourceContext, you could implement something like this:
public class SourceContext : ContextBase, IDisposable
{
public SMBASchedulerEntities _sourceEntities;
public SystemDa _systemDB = new SystemDa();
public SourceContext(string connectionString)
{
_sourceEntities = new SMBASchedulerEntities(connectionString);
}
public void AddToPatient(Patient newPatient)
{
_sourceEntities.Patients.Add(newPatient);
SaveChanges();
}
public void ChangeDatabaseTo(string connectionString)
{
if (_sourceEntities != null)
_sourceEntities.Dispose();
_sourceEntities = new SMBASchedulerEntities(connectionString);
}
public void AddToAppointmentTypes(AppointmentType AppointmentTypes)
{
_sourceEntities.AppointmentTypes.Add(AppointmentTypes);
SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_sourceEntities != null)
{
_sourceEntities.Dispose();
}
}
}
}
...then finally use it like this:
using(var context = new SourceContext("DefaultConnection1"))
{
context.AddPatient(patient); // add to Database1
context.ChangeDatabaseTo("DefaultConnection2");
context.AddPatient(patient); // add to Database2
context.ChangeDatabaseTo("DefaultConnection4");
context.AddPatient(patient); // add to Database4
}
By making your SourceContext an IDisposable, you have the opportunity to properly dispose the DbContext instance. Take note I took care of disposing the existing DbContext before changing it.
So, turns out the easiest way to do this is to put all of the DB names and connection strings in the appsettings.json file.
You can make it very complex, of course, but what I did was make a select drop down, let the user select the DB they want to use, then fire the onclick method to pull the connection string out of the app settings.json file, create an instance of the DBCONTEXT with that name’s connection string and use that DB context throughout whatever you need from there.
Public static class AppSettings
{
public static string GetConnectionString(string sDBinstanceName)
{
IConfigurationRoot Configuration;
IConfigurationBuilder builder = new ConfigurationBuilder().SetpathName(<wherever your appsettings.json file is>,”appsettings.json”).ReadJsonFile();
Configuration = builder.Build();
var tmpSettings = Configuration.GeyAppSettings();
string dbConstr = tmpSettings[“ConnectionStrings”][sDBinstanceName];
return dbConstr;
}
}
This is not MY snippet, but a derivative of what I found online. As it is from memory, it is not 100% correct, but I will fix it on Monday. I will update with author when I find it again(Monday).
Once you get the connection string you treat the DBCONTEXT like any other object, create it, use the tables (MEF) and whatever else you need, then destroy it correctly or let the garbage collection clean it up.
I got this working yesterday and please note: There are ZERO DBCONTEXTs registered in the Program.cs file as they are not needed.
This system is simple, lets you allow the user control to which Database they need based on a simple selection mechanism and answers the question.
At our shop we have MULTIPLE databases with exact same schema because we segregate data based on the timeframe for that data, but I can envision situations where you could use different schemas depending on the selection, we just do not do that.
For instance we have a Production and Development DB that are EXACTLY the same schema (different data) so the one DBCONTEXT is used for both in MEF.
With the method above we can let the user see the data in either at their command, it’s just getting there is a whole lot easier.
I'm following this guide:
https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
My project is named WebApplication1. I have installed Entity Framework via nuget.
I have a class called LoyaltyUsersContext.cs:
namespace WebApplication1.DAL
{
public class LoyaltyUsersContext : DbContext
{
public LoyaltyUsersContext() : base("LoyaltyUsersContext")
{
}
public DbSet<LoyaltyUser> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
I have added the following value to my in web.config:
<add name="LoyaltyUsersContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=Webapplication1;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
However, when I go to Server Explorer > Add Connection (MiscroSoft SQL Server, localdb doesn't show up under Server name - there aren't anything. There's also a "LoyaltyUsersContext(WebApplication1) under Data Connections, but I get an error when I try to refresh it.
Any idea why I can't seem to connect to localdb? I'm hoping it's just a connection string error or something.
I'm new to EF 6 and I'm getting an error;
'The context cannot be used while the model is being created.'
I have a console app and in the App.Config I have the following;
<connectionStrings>
<add name="MemberFollowupEmailContext" connectionString="Data Source=Dev1;Initial Catalog=dbPublicDev;Persist Security Info=True;User ID=*****;Password=******" providerName="System.Data.SqlClient" />
</connectionStrings>
Then I have a class for the Context;
namespace MemberFollowupEmails
{
public class MemberFollowupEmailContext : DbContext
{
public DbSet<tblMemberFollowupEmail> MemberFollowupEmails { get; set; }
}
}
In my program.cs I have;
static void Main(string[] args)
{
MemberFollowupEmailContext db = new MemberFollowupEmailContext();
var a = db.MemberFollowupEmails;
}
However I am constantly getting the exception when I look at (a);
{"The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe."}
What do I need to do to be able to CRUD to my database from the console app?
I'm currently trying to use the same DbContext (I have two databases, of identical structure) in my application. I'm not quite sure what I'm doing wrong, but here's my current code - hopefully it should be pretty obvious what I'm trying to do. I'm using EF Database First (which the error at the bottom seems not to suggest).
My context factory code:
public class HOLContextFactory
{
public static HOLDbEntities Create()
{
return new HOLDbEntities(); // Works
}
public static HOLDbQuoteEntities CreateQuote()
{
return new HOLDbQuoteEntities(); // Gives error
}
}
public partial class HOLDbQuoteEntities : HOLDbEntities
{
public HOLDbQuoteEntities()
: base("HOLDbQuoteEntities") // This should send "HOLDbQuoteEntities" as the base connection string?!
// Also tried "name=HOLDbQuoteEntities"
{
}
}
Web.config connection strings:
<add name="HOLDbEntities" connectionString="metadata=res://*/HOLDbContext.csdl|res://*/HOLDbContext.ssdl|res://*/HOLDbContext.msl;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />
<add name="HOLDbQuoteEntities" connectionString="metadata=res://*/HOLDbContext.csdl|res://*/HOLDbContext.ssdl|res://*/HOLDbContext.msl;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" /> // using diff database - same structure
Error I'm getting when using "HOLDbQuoteEntities" :
Code generated using the T4 templates for Database First and Model
First development may not work correctly if used in Code First mode.
To continue using Database First or Model First ensure that the Entity
Framework connection string is specified in the config file of
executing application. To use these classes, that were generated from
Database First or Model First, with Code First add any additional
configuration using attributes or the DbModelBuilder API and then
remove the code that throws this exception**
Entity Framework needs to use the actual entities object:
public class HOLContextFactory
{
public static HOLDbEntities Create()
{
// default connection string
return new HOLDbEntities();
}
public static HOLDbEntities CreateQuote()
{
// specified connection string
return new HOLDbEntities ("HOLDbQuoteEntities");
}
}
public partial class HOLDbEntities
{
public HOLDbEntities(string connectionString)
: base(connectionString)
{
}
}
}
I've done the same thing in one of my project. I am creating my entity context using metadata=res://*/
Try this:
<add name="HOLDbEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />
<add name="HOLDbQuoteEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />