ASP.NET MVC 3 or 4 - how to handle many DBs - c#

I have a case where each customer has his own DB - What i would like to do is for this to happen automatically eg when Autofac creates the controller it looks up the clientId and then gets the connection string
The following gets the string
public class ModuleContextProvider : IModuleContextProvider
{
IModuleContext context;
public IModuleContext GetContext(int moduleId)
{
if ( context == null)
{
//get module.
var rep = DependencyResolver.Current.GetService<IModuleRepository>();
var module = rep.GetById(moduleId);
context = GetContext(module);
}
return context;
}
public IModuleContext GetContext(Model.Module module)
{
if ( context == null)
{
//get module.
var rep = DependencyResolver.Current.GetService<IModuleRepository>();
context = new ModuleContext(module.DBConnection);
if ( context == null)
throw new InvalidOperationException("Could nto create DB COntext" );
}
return context;
}
}
but how do i get it into the Controllers so everyone does not need to worry about it each time , i have the following but obviously to create the repositry i need the context.
public CategoryController(ICommandBus commandBus, ICategoryRepository categoryRepository)
{
this.commandBus = commandBus;
this.categoryRepository = categoryRepository;
}
Is it possible to use a ROute eg //Controller/ModuleId/Method and then somehow get this from the Url ?
Thanks in advance for any help.
Edit: The real issue here I think is how to keep pushing the connection string around ( in this case in the form of the module) .

You may want to look into using the IDbContextFactory.
http://msdn.microsoft.com/en-us/library/hh506876(v=vs.103).aspx
what is the best practice to set up DbContext in StructureMap for console app?
It was added in the EF 4.1 Update 1.
Not sure if that will help your problem, but either way it's good to know.

If anyone needs this ..
I stored it in the cookie and retrieve in the controller as follows
IModuleWorldRepository moduleWorldRepository;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
moduleWorldRepository = GetModuleWorldRepository();
}
IModuleWorldRepository GetModuleWorldRepository()
{
UserRunTimeInfo user = (UserRunTimeInfo)User.Identity;
var context = contextProvider.GetContext(user.CurrentModuleId);
return new ModuleWorldRepository(context);
}

Related

EF how can I refresh Context with updated values in the Database?

I have a static Context that is doesn't refresh after updating values from the DB.
Here is a little background on how the systems work.
CRUD is done in a separate app.
After saving to the DB triggers notify the API on the changes.
After getting the changed Entity from the Context in the API, the entity still holds the old values.
API does what ever it needs to.
I'm using EF 6.1.3 and .net 4.6.1
Here is the Code that handles the DB trigger using SqlTblDependency.
public class ActivityListner : DataBaseListiner<Activity>
{
private IActivityService _activityService;
public ActivityListner(IActivityService actService) : base("Activity")
{
actService.ThrowIfNull("Activity Service not passed in DI container");
_activityService = actService;
}
public override void OnChange(object sender, RecordChangedEventArgs<Activity> e)
{
var changedEntity = e.Entity;
//does not get latest data
var activity = _activityService.GetActivityByID(changedEntity.ActivityID);
//these 2 methods work but are not ideal
// 1._activityService = DependencyResolver.Current.GetService<IActivityService>();
// 2. I call Context.Refresh extention method in the GetActivityByID() call above
if (e.ChangeType == TableDependency.Enums.ChangeType.Insert)
{
//...
}
else if (e.ChangeType == TableDependency.Enums.ChangeType.Update)
{
//...
}
else if (e.ChangeType == TableDependency.Enums.ChangeType.Delete)
{
//...
}
}
}
This is the extension method in the comment above
public static class ContextHelpers
{
public static void RefreshContext(this MyContext ctx)
{
var context = ((IObjectContextAdapter)ctx).ObjectContext;
var refreshableObjects = ctx.ChangeTracker.Entries().Select(c => c.Entity).ToList();
context.Refresh(RefreshMode.StoreWins, refreshableObjects);
}
}
This is the GetActivityByID() method
public Activity GetActivityById(Guid actId)
{
Context.RefreshContext();
var act = Context.Activities.FirstOrDefault(x => x.ActivityID == actId);
return act;
}
Here are the problem I would like to solve.
Is there a better way of disposing and re-creating a new context?
In step 3 above where would be the best place to new up a context? Does Entity Framework have an Event that triggers when I read from the DB where I can Dispose and re-create a new context? If that's not possible can I refresh the Context from a central location? I thought of refreshing inside of the GenericRepository or creating an extension method but I am hoping EF might have an event.

Allow the end-user to switch the Entity Framework provider at runtime

Consider that I have configured EF with a .NET Core web app:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(...));
I can also download a package to support for example SQLite:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(...));
How can we allow a user to "select" the provider on app install? I mean - for example, in WordPress you can choose from a dropdown.
Is this possible in .NET Core? The only way I see is to restart the app only...
Here is an example on how you can implement a DbContextFactory or a DbContextProxy<T> which will create the correct provider and return it.
public interface IDbContextFactory
{
ApplicationContext Create();
}
public class DbContextFactory() : IDbContextFactory, IDisposable
{
private ApplicationContext context;
private bool disposing;
public DbContextFactory()
{
}
public ApplicationContext Create()
{
if(this.context==null)
{
// Get this value from some configuration
string providerType = ...;
// and the connection string for the database
string connectionString = ...;
var dbContextBuilder = new DbContextOptionsBuilder();
if(providerType == "MSSQL")
{
dbContextBuilder.UseSqlServer(connectionString);
}
else if(providerType == "Sqlite")
{
dbContextBuilder.UseSqlite(connectionString);
}
else
{
throw new InvalidOperationException("Invalid providerType");
}
this.context = new ApplicationContext(dbContextBuilder);
}
return this.context;
}
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing){
if (disposing){
disposing?.Dispose();
}
}
}
Also make sure you implement the disposable pattern as show above, so the context gets disposed as soon as the factory gets disposed, to prevent the DbContext remaining in memory longer than necessary and free unmanaged resources as soon as possible.
Finally register the factory as scoped, as you would the context itself:
services.AddScopedd<IDbContextFactory, DbContextFactory>();
A more advanced and generic/extendable approach is by creating a IDbContextProxy<T> class which uses a bit of reflection to get the correct constructor and the DbContextOptionsBuilder to it.
Also possible to create a IDbContextBuilder which abstracts the provider creation.
public class SqlServerDbContextBuilder IDbContextBuilder
{
public bool CanHandle(string providerType) => providerType == "SqlServer";
public T CreateDbContext<T>(connectionString)
{
T context = ... // Create the context here
return context;
}
}
Then you can pick the correct provider w/o a hard coded if/else or switch block just by doing
// Inject "IEnumerable<IDbContextBuilder> builders" via constructor
var providerType = "SqlServer";
var builder = builders.Where(builder => builder.CanHandle(providerType)).First();
var context = builder.CreateDbContext<ApplicationContext>(connectionString);
and adding new types of provider is as easy as adding the dependencies and an XxxDbContextBuilder class.
See here, here or here for more information about this and similar approaches.
I think you can use repositories which are using a db context you specified and you can pass a parameter to context constructor to choose the endpoint. I am not sure on this but it might work for your situation.
I followed this article for repository pattern, I recommend to read it :)
http://cpratt.co/generic-entity-base-class/

Mvc .net Password change

I am new with C# and MVC aplications.
I have weird issue with password change in MVC aplication.
The thing is next, when i fill my form and enter old and new password and hit enter, everything seams to went fine but the password is not updated in databse. Then when I do from second try it went fine.
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
bool changePasswordSucceeded;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
if (changePasswordSucceeded)
{
var user = GetUser();
user.PasswordEntropyScore = model.PasswordEntropyScore;
var userRepo = new UserRepository(MvcApplication.DbSession);
userRepo.Update(user);
return this.RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError(string.Empty, "The current password is incorrect or the new password is invalid.");
}
}
return this.View(model);
}
UserRepository Update() method is here:
public class UserRepository : Repository<User>
{
private Logger logger;
public UserRepository(ISession sesh) : base(sesh)
{
this.logger = LogManager.GetCurrentClassLogger();
}
public new bool Update(User user)
{
if (string.IsNullOrWhiteSpace(user.UserName) || string.IsNullOrWhiteSpace(user.Email))
{
var ex = new ArgumentException("Username or Email cannot be blank");
this.logger.Error("User.Update - username or email was null/empty string.", LoggerHelper.GetErrorString(ex, user));
throw ex;
}
return base.Update(user);
}
}
And repository base.Update() method:
public class Repository<T> : IRepository<T>
where T : Entity
{
protected readonly ISession session;
protected static Logger logger;
public Repository()
{
throw new NotImplementedException("Must instantiate repositories with an ISession");
}
public Repository(ISession sesh)
{
this.session = sesh;
logger = new LogFactory().GetCurrentClassLogger();
}
public bool Update(T entity)
{
this.session.SaveOrUpdate(entity);
this.session.Flush();
return true;
}
}
So all this code went fine when first time I tried to change password but it doens't udapted it in database. Then when I try the second time to change the password then it update in database.
If I change the code to directly call base.Update() method instead to call first UserRepostirory.Update() method as wrapper then it is fine.
Does anyone has idea what the issue is.
there is not much comment in your code to understand what you really did but if you use entity framework you can simply do like below:
entitymodel model= new entitymodel();
var query= model.users.first(o=>userID==inputID);
query.password=inputPassword;
model.SaveChanges();
As per the code I do not see any issues. Is it possible to add try catch block to Repository class Update Method and check if any exception is occurred when you are trying to save for the first time.
Can you check if MvcApplication.DBSession is set before hitting Update method for the first time
There is a possibility of having entity framework local cache which may be updating password to older one. Perform below steps.
Remove below lines from your code.
Change password
Restart application and check if password is changed or not.
If password is changed then clear entity framework local cache(by code).
NOTE: There is no need of updating password in db again. ChangePassword function makes changes to DB directly.

ASP.NET MVC 5 page hangs on db call

I'm trying to get up and running with an MVC 5 project using ASP.NET Identity 2.0. My starting point is the example app in this tutorial. My initial page is Home/Index. When I try to do a search (expecting a null return value) the app simply hangs and I can't figure out why. Instantiation of the db context is causing the Seed() method to be called, which seems normal, but then it hangs on the roleManager.FindByName(roleName) call (I've commented it in the code below). Pausing the debugger showed where it was stuck but I don't know where to go from there. Relevant classes are as follows.
Controller:
public class HomeController : ApplicationController
{
public ActionResult Index()
{
var user = db.Users.Find("dummyVal");
return View();
}
[Authorize]
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Base Controller:
public abstract class ApplicationController : Controller
{
protected ApplicationDbContext db;
public ApplicationController()
{
db = new ApplicationDbContext();
}
}
DB Initializer:
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEF(context);
base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
public static void InitializeIdentityForEF(ApplicationDbContext db) {
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin#example.com";
const string password = "Admin#123456";
const string roleName = "Admin";
//Create Role Admin if it does not exist
// EXECUTION HANGS ON THIS CALL...
var role = roleManager.FindByName(roleName);
if (role == null) {
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null) {
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name)) {
var result = userManager.AddToRole(user.Id, role.Name);
}
// Create dummy user
const string dummyUserName = "PeterVenkman";
const string dummyUserPwd = "gf%^^ftf83X";
var dummyUser = userManager.FindByName(dummyUserName);
if (dummyUser == null) {
dummyUser = new ApplicationUser { UserName = dummyUserName, Email = dummyUserName };
var result = userManager.Create(dummyUser, dummyUserPwd);
result = userManager.SetLockoutEnabled(dummyUser.Id, false);
}
}
}
The error is because you are instantiating the dbContext object in the controller and not getting it from the owin context. The presence of two different context objects one in the controller and one in the startup working on the same db is causing this deadlock. Simplest way to resolve this is replace the code
db = new ApplicationDbContext();
with
db = System.Web.HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>();
fixes the issue
I had the same problem and this is how I fixed it.
Delete your current database (I was using .mdf)
Create a new database
Clean and rebuild
Update database - if needed -
Unfortunately, I do not know the source of the problem. Please edit the answer/post a comment if you know :)
For other people with the same issue, make sure the username/password in ApplicationDbInitializer are valid. It will hang if you set the admin password to 123 for example.
Edit: This post provides an explanation + answer
http://forums.asp.net/post/5802779.aspx
Suhas Joshi has the correct answer. The ApplicationDbContext object should be in essence a singleton that is managed by the "Microsoft ASPNET Identity Owin" package (installed using NuGet). When you manually create a new instance of it in your ApplicationController there is contention for the same resource causing this deadlock.
Note that the "Get()" extension used comes from the following library which you must have a reference to in your ApplicationContext class.
using Microsoft.AspNet.Identity.Owin;
Thanks Suhas Joshi for pointing this out as it saved me greatly. I would have just up-voted your answer, but unfortunately I don't have a strong enough reputation yet :).

Should repositories be implemented as singletons as best practice?

I have a small webapp that uses EntityFramework to store stuff via repositories into the database.
What I've done so far (based on all the tutorials I read) is create a repository where I need it, as shown below:
In CustomMembershipProvider:
public CustomMembershipProvider()
{
_userRepository = new UserRepository(new TenantApplicationContext());
}
In my RegisterController:
public TenantRepository TenantRepository { get; set; }
public UserRepository UserRepository { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
if (TenantRepository == null) { TenantRepository = new TenantRepository(TenantApplicationContext); }
if (UserRepository == null) { UserRepository = new UserRepository(TenantApplicationContext); }
base.Initialize(requestContext);
}
The point is, that I instantiate the UserRepository twice. This becomes a problem when I create a User in one instance, and try to retrieve it in the other instance, and I did not call SaveChanges in between.
The problem lies here:
// Snippet from the register controller class
if (!UserRepository.Exists(model.AccountableEmailAddress))
{
// 1 - Create the user via a custom MembershipProvider
// Note, the CustomMembershipProvider has it's own instance of UserRepository
var createStatus = MembershipService.CreateUser(
model.AccountableUser,
model.Password,
model.AccountableEmailAddress);
if (createStatus == MembershipCreateStatus.Success)
{
// Left out irrelevant code
AdministerUserAndTenant(tenant.Name, model.AccountableEmailAddress);
}
}
private void AdministerUserAndTenant(string tenantName, string emailAddress)
{
// 2 - Try to retrieve the user from a different (!) instance of UserRepository
var user = UserRepository.GetUser(emailAddress);
var tenant = TenantRepository.GetTenantByName(tenantName);
tenant.Users.Add(user);
TenantApplicationContext.SaveChanges();
}
I hope you can still follow, tried to leave out unnecessary parts.
What is the best way to deal with issues like this?
PS: I'm not very fond of the Singleton pattern, so if possible don't go there :).
When exactly does it become a problem? Cause that's where the answer lies. Classes that should know of each other's unsaved changes should use the same repository instance. Since they are probably related, you'll manage passing a reference between them.
If there's reason why all of your application should have one single repository, use Dependency Injection.

Categories