I have an ASP.NET service. Inside it I use the access to SQL Server, i.e. add the DbConnection etc
services.AddScoped<IDbConnection, SqlConnection>(_ => new SqlConnection(sqlcon));
services.AddScoped<ICRepository, CRepository>();
This DbConnection used in the classes which queried a db.
BUT I can execute the query 1st time and it returns the correct items.
If I execute it the 2nd time I receive:
"message": "The ConnectionString property has not been initialized."
and really the dbconnection object is empty and not initialized
How to fix it?
That is minimized piece of code
public class CRepository : ICRepository
{
private readonly IDbConnection _db;
public CRepository(IDbConnection db)
{
_db = db;
}
//....
public List<MyAction> GetMyActions()
{
var sql = $#"
SELECT id,name, description from actions;";
var result = _db.Query<MyAction>(sql).ToList();
return result;
}
...
}
ANd the first time the _db is initialized but the 2nd one - no.
Related
I have created a Repository on top of Entity Framework Core, but have some issues with how it's done.
This is an example:
public class StockPricesRepository : IStockPricesRepository
{
StockPricesDbContext _stockPricesDbContext;
ILogger _logger;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_logger = logger;
}
public void Add(StockPrice stockPrice)
{
_stockPricesDbContext.Add(stockPrice);
_stockPricesDbContext.SaveChanges();
}
public void AddOrUpdate(StockPrice stockPrice)
{
if (!Exists(stockPrice))
_stockPricesDbContext.Add(stockPrice);
else
_stockPricesDbContext.Update(stockPrice);
_stockPricesDbContext.SaveChanges();
}
private bool Exists(StockPrice stockPrice)
{
StockPrice existingStockPrice = Get(stockPrice.Ticker, stockPrice.Exchange, stockPrice.Date, stockPrice.DataProvider);
return (existingStockPrice != null);
}
public StockPrice Get(string ticker, string exchange, DateTime date, string providerName)
{
StockPrice stockPrice = null;
stockPrice =
(from sp in _stockPricesDbContext.StockPrices
where (
(sp.Ticker == ticker) &
(sp.Exchange == exchange) &
(sp.Date == date) &
(sp.DataProvider == providerName))
select sp).AsNoTracking().FirstOrDefault();
return stockPrice;
}
}
}
The StockPricesDbContext is injected into the constructur using dependency injection like this:
services.AddDbContext<StockPricesDbContext>(options => options.UseSqlServer(connectionString));
The problem with the current design is that I get some issues with multiple calls into the repository within the lifetime of the StockPricesDbContext object (even if I have AsNoTracking() on the Get method):
System.InvalidOperationException: 'The instance of entity type 'StockPrice' cannot be tracked because another instance with the same key value for {'Date', 'Ticker', 'Exchange', 'DataProvider'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
To avoid this problem I would maybe prefer to have each of the repository methods embedded in a using block like this:
using (var db = new StockPricesDbContext())
{
}
The problem with this approach is that I don't get the StockPricesDbContext from dependency injection and the connection string from AddDbContext is lost.
One workaround for that might be to get the connection string from the constructor of the repository like this:
private string _connectionString;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_connectionString = stockPricesDbContext.Database.GetDbConnection().ConnectionString;
_logger = logger;
}
I would also need to add this constructor in StockPricesDbContext:
public StockPricesDbContext(string connectionString)
{
_connectionString = connectionString;
}
and this would be the OnConfiguring method:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
if (_connectionString != null)
optionsBuilder.UseSqlServer(_connectionString);
}
}
Then I could use it in the methods like this:
using (var db = new StockPricesDbContext(_connectionString))
{
}
It kind of works, but seems a bit "unclean".
Has anyone else come across a good pattern for repositories built on top of Entity Framework Core?
The common mistake that causes this error is having several async queries that have not been committed.
There is no problem saving multiple records in EF though.
There are multiple approaches to avoid your issue.
Using AddRange(),UpdateRange(),DeleteRange()
AddRange() can add list of objects to your db and you don't need to call .Add() more than once. Similarly you can update or delete a list of objects
EFCore.BulkExtensions
Bulk Extensions in EFCore
Both are extending DbContext with Bulk operations and have the same syntax call:
context.BulkInsert(stockPriceList);
context.BulkUpdate(stockPriceList);
context.BulkDelete(stockPriceList);
context.BulkInsertOrUpdate(stockPriceList);
My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.
My Controller code
ParaEntities db = new ParaEntities();
public List<Client> GetAllClients()
{
return db.Client.ToList();
}
Please click this link to see the error message
It is weird that when I am first time to click the button to get all client information then it responses 500. In the second time, I click the button to get all client, which is success.
You should assign variable and display the data in View.
Please change the syntax as i write below.
ParaEntities db = new ParaEntities();
public List<Client> GetAllClients()
{
var getData= db.Client.ToList();
if(getData==null)
{
return null;
}
return getData;
}
This error points to a connection problem rather then code issue. Check that the connectionstring is valid and that the user specified in the connectionstring has access to the database. If you're running the application on IIS then make sure that the applicationpool user has access to the database. Here is another SO issue were they solved this error.
If you want to store the db context as a local variable in your controller class then I suggest you to instantiate it inside of the controllers constructor. Then you make sure that every time a instance of the controller is created then a new db context is created as well.
Lets say your controller namned ClientController
private ParaEntities db;
public ClientController()
{
this.db = new ParaEntities();
}
public List<Client> GetAllClients()
{
return db.Client.ToList();
}
Another approach is to wrap your db context in a using statment inside of your method. In that case you make sure that the method is using a fresh context when being called upon and that the context is being disposed when the operation is completed.
public List<Client> GetAllClients()
{
using(ParaEntities db = new ParaEntities())
{
return db.Client.ToList();
}
}
PS: both examples violates the dependency inversion principle (hard coupling to the db context) but thats for another day
Please try this
public List<Client> GetAllClients()
{
ParaEntities db = new ParaEntities();
return db.Client.ToList();
}
I have been asked to map the ASP.NET Identity classes to existing database Views for read operations, using Stored Procedures for CRUD. There are a number of StackOverflow Questions stating that is possible to map to views, also this question, this one and lastly this one.
I have mapped the classes to the Views as follows-
var applicationUser = modelBuilder.Entity<applicationUser>().HasKey(au => au.Id) //Specify our own View and Stored Procedure names instead of the default tables
.ToTable("User", "Users").MapToStoredProcedures(sp =>
{
sp.Delete(d => d.HasName("spUser_Delete", "Users"));
sp.Insert(i => i.HasName("spUser_Create", "Users"));
sp.Delete(u => u.HasName("spUser_Update", "Users"));
});
Where [Users].[User] is a SQL view retrieving data from the SQL table [Users].[tblUser].
Unfortunately I have had to leave at least one of the classes mapped to a table rather than View as Entity Framework generates the following SQL-
SELECT Count(*)
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE t.TABLE_TYPE = 'BASE TABLE'
AND (t.TABLE_SCHEMA + '.' + t.TABLE_NAME IN ('Users.ApplicationRole','Users.User','Users.AuthenticationToken','Users.UserClaim','Users.UserLogin','Users.UserRole','Users.Department','Users.PasswordResetToken','Users.UserDepartment')
OR t.TABLE_NAME = 'EdmMetadata')
go
Which returns zero as these are Views and not tables.
As a result any attempt to use the UserManager results in the exception-
Value cannot be null. Parameter name: source
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source
Source Error:
Line 48: if (ModelState.IsValid)
Line 49: {
Line 50: var userAccount = await
UserManager.FindByNameAsync(model.UserName);
Line 51:
Line 52: if (userAccount == null)
Manually changing the query to-
SELECT Count(*)
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE (t.TABLE_SCHEMA + '.' + t.TABLE_NAME IN ('Users.ApplicationRole','Users.User','Users.AuthenticationToken','Users.UserClaim','Users.UserLogin','Users.UserRole','Users.Department','Users.PasswordResetToken','Users.UserDepartment')
OR t.TABLE_NAME = 'EdmMetadata')
go
Returns the correct nine Views and would presumably not cause the error. Simply having one of the classes mapped to a table is sufficient to convince it the database is correct and to carry on as normal.
Is there any way I can persuade Entity Framework to remove the "Is a table" requirement, or assert that the tables do exist and therefore skip this step altogether?
Edit: Following a request, the code for the UserManager is included below-
AccountController.cs
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationIdentityDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
I have managed to resolve this problem by creating a custom Database Initializer which replaces the default CreateDatabaseIfNotExists initializer. The Codeguru article on Understanding Database Initializers in Entity Framework Code First was enormously helpful in helping me understand what was going on.
Code for solution-
using System.Data.Entity;
namespace NexGen.Data.Identity
{
public class IdentityCustomInitializer : IDatabaseInitializer<ApplicationIdentityDbContext>
{
public void InitializeDatabase(ApplicationIdentityDbContext)
{
return; //Do nothing, database will already have been created using scripts
}
}
}
IdentityManager-
public class ApplicationIdentityDbContext: IdentityDbContext<ApplicationUser>
{
public ApplicationIdentityDbContext() : base("DefaultConnection")
{
Database.SetInitializer(new IdentityCustomInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
As a result of this code there are no longer any probing queries by Entity Framework attempting to check if the database exists (and failing due to the assumption that tables, rather than views, were mapped) - instead the queries are immediately against the view attempting to retrieve the user data (and then executing a Stored Procedure in the case the initial action was a registration or otherwise updating the user).
please try
[Authorize]
public class AccountController : Controller
{
public AccountController()
{
InitAccountController(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationIdentityDbContext())))
}
private InitAccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
}
some more explanations:
in EF6 code we can see the following function (DatabaseTableChecker.cs):
public bool AnyModelTableExistsInDatabase(
ObjectContext context, DbConnection connection, List<EntitySet> modelTables, string edmMetadataContextTableName)
{
var modelTablesListBuilder = new StringBuilder();
foreach (var modelTable in modelTables)
{
modelTablesListBuilder.Append("'");
modelTablesListBuilder.Append((string)modelTable.MetadataProperties["Schema"].Value);
modelTablesListBuilder.Append(".");
modelTablesListBuilder.Append(GetTableName(modelTable));
modelTablesListBuilder.Append("',");
}
modelTablesListBuilder.Remove(modelTablesListBuilder.Length - 1, 1);
using (var command = new InterceptableDbCommand(
connection.CreateCommand(), context.InterceptionContext))
{
command.CommandText = #"
SELECT Count(*)
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE t.TABLE_TYPE = 'BASE TABLE'
AND (t.TABLE_SCHEMA + '.' + t.TABLE_NAME IN (" + modelTablesListBuilder + #")
OR t.TABLE_NAME = '" + edmMetadataContextTableName + "')";
var executionStrategy = DbProviderServices.GetExecutionStrategy(connection);
try
{
return executionStrategy.Execute(
() =>
{
if (connection.State == ConnectionState.Broken)
{
connection.Close();
}
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
return (int)command.ExecuteScalar() > 0;
});
}
finally
{
if (connection.State != ConnectionState.Closed)
{
connection.Close();
}
}
}
}
which corresponds to what you discover.
From this function we may says that there is a problem if, and only if, there are/is only views mapped to the model. In this case the initializer considers the database as Existing but Empty, and he tries to create the tables.
This creates problems as there are/is still views in the database with the same name as the tables the initializer wants to create.
So a work around seems to have at least one real table mapped to the context. No need for a custom initializer in this case.
I propose it as an issue : model only mapped to views
From my understanding and tests there is no need to implement an IDatabaseInitializer having an empty InitializeDatabase method like pwdst did.
From what I saw at Understanding Database Initializers in Entity Framework Code First, it is sufficient to call
Database.SetInitializer<ApplicationIdentityDbContext>(null);
when the application is initializing, or better say, before the first time the database will be accessed.
I would not put it inside the ctor of my DbContext class to avoid setting the initializer every time a DbContext instance is created. Instead, I would put it into the application's initialization method or as one of the first statements of the Main() method.
This worked fine for my application using Entity Framework 6.
I have the following in my MVC Application:
namespace WebUx.Areas.User.Controllers
{
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
Plus:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
System.Diagnostics.Debug.Write("Set Initializer\n");
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
I understand that when there's a call to the account controller then this will set the DB context but once this is set will it stay set for my application. What about later on for other users who connect. Will the DB context always be available?
The reason I am asking this is that I have other information that I want to store in a table and access with Web API. Should I code in something similar for these controllers so that each time I check that there's a DB context available or could I just use this?
The connection is tightly coupled to the DbContext. As a result, the connection will only be open when your class which inherits DbContext, UsersContext in your case, retains its scope.
In your example, UsersContext is scoped to the using block.
using (var context = new UsersContext())
{
//some actions
}
Therefore, once "some actions" are finished, the connection will close and any attempt to access lazy loading will throw an exception stating the connection is no longer available. Every time you need to access your database, you should start a new connection in my opinion. What you want to make sure is that you only make one actual trip to the database. Make sure that your query is optimized so that you do not make multiple trips to the database instead of just doing it all at once as that will affect your performance.
Edit
As a side note, the using block breaks down into this:
try{
var context = new UsersContext();
//some actions
}finally{
context.Dispose();
}