ASP.NET MVC4 - Error When Customize Default Accounts Models & Controller - asp.net

I am in the begining of making a simple website using ASP.NET MVC4 CodeFirst Approach. The web site is built around users who are able to register, make posts etc. The existing UserProfile class was modified for the accommodation other fields (ex: FirstName, LastName etc).
When I ran the website I got a similar error:
Invalid column name 'FirstName'.
Invalid column name 'LastName'.
Invalid column name 'Phone'.
I red that this is because the Database is not updated as the model is updated. So I set the following on the Application_Start() in Global.asax with the intention of droppnng the database always (at least until I get the hang of it).
protected void Application_Start()
{
Database.SetInitializer(new DropCreateDatabaseAlways<UsersContext>());
//Other default generated stuff below...
}
I also tryed the DropCreateDatabaseIfModelChanges, but both methods didn't drop the database.
Isn't this possible? Why is it? Am I doing some thing wrong?
I believe it would be possible to store the info in a different table and link it to this, but I prefer to keep it in one table.
Also is it a bad idea to add UserProfiles to the websites context (lets say: DatabaseContext (which has other entities like Posts, Comments etc) and change the AccountsController to use DatabaseContext instead of UsersContext?
Thanks in advance.

Have you run Enable-Migrations in the Package Manager Console? You should see a migration folder in your project, with a configuration.cs file, ensure you have enabled automatic migrations.
public Configuration(){
AutomaticMigrationsEnabled = true;
//if you drop columns - consider this carefully...
AutomaticMigrationDataLossAllowed = true;
}

Related

ASP.NET MVC - 'no such table': AspNetUsers

I'm creating a web app that will allow users to log in to execute certain actions. I followed this tutorial (for simplicity, you can search for "Generate the database to store membership data" for the exact database setup I used).
Everything works fine in development, but when I deploy it and try to register a new user, it gives me the error:
An unhandled exception has occurred: SQLite Error 1: 'no such table: AspNetUsers'.
Is this because I need to do a 'dotnet ef database update' in deployment? If so, is there anyway I can avoid it, such that I can just deploy and have the database setup itself and be ready? The tutorial semi-talked about it by calling 'dbContext.Database.Migrate();', but I have no dbContext in the Configure() method... so I'm not sure how to fill in the gaps.
If there is any information about my code you'd like me to post, I'll be happy to post it for you. Thanks in advance!
I did happen to figure out how to fix the problem, but please bear with me on the details of the solution. It has been so long that I can only remember that Step 1 is definitely necessary, but I'm fairly fuzzy on if we also need to do Step 2. Some point down the line when I have time, I'll revisit this to confirm, but for now, I hope this will give you enough help to overcome the problem.
Step 1: Create your AspNetUsers table in your custom context class that inherits from IdentityDbContext<IdentityUser> with the following code. For the purposes of my example, I'm calling this class: CustomDbContext.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>().ToTable("AspNetUsers");
base.OnModelCreating(modelBuilder);
}
Note: If you aren't following the guide or using this for authentication, you'd simply have your custom context class inherit from DbContext.
Step 2: In your Startup.cs, you'll need to put the following code in your Startup() method, where you are literally ensuring that the CustomDbContext database (AspNetUsers) is created and available in production.
using (var client = new CustomDbContext())
{
client.Database.EnsureCreated();
}
Note: There is speculation that you could do client.Database.Migrate() in that using statement instead according to this (credit to Thomas Schneiter for letting me know), but I have not personally tried it to see if it works. There is another SO post relating to this particular piece of code with an answer I posted.

Using custom user instead of ASP.NET IdentityUser

I'm new on ASP.NET Identity and trying to customize the identity which is provided while creating new MVC project.
ASP.NET Identity automatically creates few tables for handle authentication and authorization itself.
My main goal is just create Users table but others. I've tried following code to prevent creating these tables:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Ignore<IdentityUserRole>();
modelBuilder.Ignore<IdentityUserClaim>();
modelBuilder.Ignore<IdentityRole>();
}
When I want to create a user, following error returning:
The navigation property 'Roles' is not a declared property on type 'ApplicationUser'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property.
I found that built-in Identity user has following structure:
IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
What I need is creating a custom IdentityUser which not contains role, claim, etc. I just want the table 'Users' when I run the project.
Any possibility to create my own IdentityUser or customize built-in one? Or any suggestion to create just 'Users' table and work with it?
Many thanks in advice.
This is what I did in order to get a solution:
I removed the base.OnModelCreating
Then I added the ignore in this order.
//base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("User");
modelBuilder.Entity<IdentityUser>().ToTable("User").Ignore(p => p.Roles);
modelBuilder.Ignore<IdentityUserRole>();
modelBuilder.Ignore<IdentityUserLogin>();
modelBuilder.Ignore<IdentityUserClaim>();
modelBuilder.Ignore<IdentityRole>();
Doing this I got a final error that it said: Could not drop object 'dbo.Role' because it is referenced by a FOREIGN KEY constraint.
For that I changed the migration moving the DropTable("dbo.Role"). I know this is hack, but I did not find out how EF migrations move the droptables sentences to the right order.
Richard

MVC 4 SimpleMembership - Doing it right the first time

Folks,
I just started to design a new web project. From VS2012, I generated a new Internet website project. It comes with a database that is already laid out with a few tables. This is fine.
Now, I need to extend the database with a few new tables and update UserProfile with few new fields. However, as this is going to be a new database, I don't want to enable migration steps and add code-bloat that is not really needed.
Here is the workaround I found:
Extend UserProfile with new fields as you would want to.
Add new tables in AccountModels.cs. For example,
[Table("Items")]
public class Items {
...
}
For each new table, add a DbSet field to UsersContexts class in AccountModels.cs.
public class UserContext : DbContext {
...
public DbSet<Items> Items {get; set; }
}
Delete the original database file, create a new database file with the same name, and run the application.
Voila. The new database gets filled with all the tables.
Is this the right way to do it?
Although it works, I am not really sure about step 3. I figured somehow the framework needs to know that a new table needs to be created and essentially adding a new field to UserContext triggers the creation of the new table. Is this the approach right?
Thank you in advance for your help.
Regards,
Peter
By not using code first migrations, deleting the database manually is the best thing you can do. There is also the DropCreateDatabaseIfModelChanges initializer but be careful to never use it in a release version of your app.
I would also recommend to write the DbContext in a seperate cs file. When you use the fluent api to configure the EF relations (what i would do), it can get really big. Think about putting the DAL and your models in seperate projects when your solution gets bigger. Using multiple contexts for one db can also cause problems, so naming it SomeAppContext would be better.

Concurrency exception with Devexpress ASPXGridView and EntityFramework 4.3.1

My Issue
I have a simple WebForms project for testing concurrency.
I am using:
1. Entity Framework 4.3.1 code first approach.
2. DevExpress ASP.net controls to visualize my data. Specifically an ASPXGridView control.
3. MySQL as database backend.
Now I am having an issue with the concurrency check.
Even if I am the only user editing the data, if I edit the same record twice using the DevExpress ASPXGridView I get a concurrency exception!
The exception I get is :
System.Data.OptimisticConcurrencyException
My Setup
** Simplified here for brevity
My code first entity is defined something like this:
public class Country
{
//Some constructors here
[Required, ConcurrencyCheck]
public virtual string LastUpdate { get; set; }
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int CountryID { get; set; }
//Various other data fields here
}
You can see I have added a single field called LastUpdate which the concurrecny check is being tested against due to setting the [ConcurrencyCheck] attribute.
On my web page with the DevExpress ASPXGridView I am using an EntityDataSource to make the binding between the grid view and the entity framework. The grid view is using a popup editor. I have the following events hooked:
protected void Page_Load(object sender, EventArgs e)
{
//Hook entity datasource to grid view
dbgCountries.DataSource = CountriesEntityDataSource;
dbgCountries.DataBind();
}
protected void CountriesEntityDataSource_ContextCreating(object sender, EntityDataSourceContextCreatingEventArgs e)
{
//Create and hook my DBContext class to the entity
//datasources ObjectContext property.
var context = new MyDBContextClass();
e.Context = ((IObjectContextAdapter)context ).ObjectContext;
}
protected void dbgCountries_InitNewRow(object sender, DevExpress.Web.Data.ASPxDataInitNewRowEventArgs e)
{
//I create a new MyDBContextClass here and use it
//to get the next free id for the new record
}
protected void dbgCountries_CustomErrorText(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewCustomErrorTextEventArgs e)
{
//My code to catch the System.Data.OptimisticConcurrencyException
//excpetion is in here.
//I try to rtefresh the entity here to get the latest data from
//database but I get an exception saying the entity is not being
//tracked
}
protected void dbgCountries_RowValidating(object sender, DevExpress.Web.Data.ASPxDataValidationEventArgs e)
{
//Basic validation of record update in here
}
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//I set the LastUpdate field (my concurrency field)
//to the current time here
}
I also have some button events hooked to test a direct concurrecny test.
eg
- Get Entity
- Update Entity
- Update DB directly with sql
- Save Entity
- Get concurrency exception as expected
eg
- Get Entity
- Update Entity
- Save Entity
- No issue.
- Get Entity again.
- Update Entity again.
- Save Entity again.
- No issue.
These buttons work as expected. Only ther grid updates seem to have an issue.
Maybe it is because the grid needs to use ObjectContect and my entity framework classes are using DBContext?
My Attempted Fixes
I have scoured the internet trying to find a solution. Checked DevExpress forums, checked other posts here on StackOverflow, various posts on the internet, Microsoft MSDN articles on concurrency and I just can not work this out.
None of the posts were as 'simple' as mine. They all had other data involved. eg a master/detail
relashionship. custom editors. etc. I am using all inbuild DevExpress controls and just display a
single grid view on my db table / entity.
Some posts suggest refreshing the entities. I tried this but get an exception saying the entity is
not being tracked in the object state manager.
I tried refreshing the entity framework by destroying and recreating my object context / db
context but somehow I still get the concurrency issue.
I tried refreshing using the DBContexct and also the ObjectContext. Neither worked.
(objContext.Refresh(RefreshMode.StoreWins, entity). I either get an exception as stated]
earlier sayign the entity is not being tracked, or if I tell it to refresh only non modifed
entities then nothing happens at all (no refresh, no excpetion)
I tried making my DBContext global but this is no good as WebForms appears to want to recreate its
entire state and rehook its grids data context etc after every web refresh. (page loads, user
clicks edit, user clicks ok to update)
Now all of these solutions seem to takle what to do AFTER the concurrency exception. Seeing that I should not even be getting the exception in the first place I guess they would not help.
Suggestions
Do any of you have suggestions on how to make this work?
Do I have to maybe force the entity framework to refresh manually after posting data from the grid?
(I only just thought of this one now)
It seems a pretty simple setup I have. Maybe I am missing something very obvious. I have not worked with WebForms or EntityFramework much yet so there could be simple (and perhaps obvious) solutions I am missing?
Any help appreciated.
Thanks
Peter Mayes
I have managed to solve my issue.
It may not be the most correct solution but it is working and any progress at this point is much appreciated.
Approach
I tried refreshing Entity Framework after posting data in the ASPXGridView.
Many attempts. None worked.
I tried using a TimeStamp attribute on my Country entity but this did
not seem to map very well to MySQL. (However I might try this again now
I have solved the issue)
I then thought maybe my DevArt MySQL dot connector and MySQL was at fault.
So I switched over to MSSQL and its standard connector. This showed the same
issue am having with MySQL & co.
Finally I was mucking around with various attempts and noticed that if I go to a different
page on my web site, then back again the issue does not occur.
E.g.:
Edit Country and Save. No Issues.
Switch to other site page.
Switch back to Countries.
Edit Country and Save. No Issues.
The difference being, if I do not switch pages the second edit creates a concurrency exception.
With some more testing with co-workers I got a hunch that maybe the viewstate for the
entity datasource was not being refreshed after a post/update on the ASPGridView.
So what I did was:
> Set EntityDataSource.StoreOriginalValuesInViewState = FALSE
This stopped all concurrency working as no old/pre edit values were being stored and so
were not available for the concurrecny check.
I then thought I would force the oldvalues to be what was in the editor before I edited.
I was using ASPXGridView.RowUpdating to do this.
I thought thats ok, I can just use the OldValues passed to ASPXGridView.RowUpdating to
ensure entity framework is good to go.
Doing this I found some very odd behaviour...
If I:
- open edit form in browser A
- open edit form in browser B
- save changes in browser B (DB updates with new values here)
- save changes in browser A (DB updated here too. but should have been a
concurrency exception!)
The reason post from A was succeeding was that OldValues on A had been magically updated
to the new values B had posted!
Remember the edit form on A was open the whole time so it should not have updated its OldValues underneath. I have no idea why this occurs. Very odd.
Maybe OldValues are not retrieved by the DevExpress ASPXGridView until the
edit form is closing?
Anyway, then I thought. Fine, I will just work around that oddity. So to do so I created
a static member on the web page to store a copy of my Country entity.
When the user goes to open the editor I get the current values and store them.
Then when ASPXGridView.RowUpdating fires I push the stored old values back into the
OldValues data. (I also update my timstamp/concurrency field here too in the NewValues
data)
With this approach my concurrency now works. Hurah!.
I can edit locally as much as I want and get no conflicts. If I edit in two browsers at once the second one to post raises concurrency exception.
I can also switch between MySQL and MSSQL and both work correctly now.
Solution Summary
Set EntityDataSource.StoreOriginalValuesInViewState = FALSE. (I did this in the designer.)
Create private member to hold pre-edit country values
private static Country editCountry = null;
Hook StartRowEditing on ASPXGridView. In here I get the current country values and store them as 'pre edit' values. Note that CopyFrom is just a helper method on my entity.
protected void dbgCountries_StartRowEditing(object sender, DevExpress.Web.Data.ASPxStartRowEditingEventArgs e)
{
if (editCountry == null)
{
editCountry = new Country();
}
var context = new MyDBContext();
var currCountry = context.Countries.Where(c => c.CountryID == (int)(e.EditingKeyValue)).Single();
editCountry.CopyFrom(currCountry);
}
Hook RowUpdating on ASPXGridView. Here is where I make sure old values are correct before update goes ahead. This ensures concurrency will work as expected.
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//Ensure old values are populated
e.OldValues["RowVersion"] = editCountry.RowVersion;
//No need to set other old values as I am only testing against
//the one field for concurrency.
//On row updating ensure RowVersion is set.
//This is the field being used for the concurrency check.
DateTime date = DateTime.Now;
var s = date.ToString("yyyy-MM-dd HH:mm:ss");
e.NewValues["RowVersion"] = s;
}

SubSonic build errors

I'm trying to use SubSonic for a new project with an existing database but when I try to build the project after generating the SubSonic files I'm getting these same two exceptions on different classes:
Partial declarations of 'MyData.UserCollection' must not specify different base classes
Type 'MyData.UserCollection' already defines a member called 'UserCollection' with the same parameter types
I'm able to successfully build a new project using the Northwind DB so I believe the error has something to do with the way the tables are setup but I'm not sure where to go from there. Any help is appreciated.
Search for UserCollection in your project. SubSonic generated a partial class for this in the User.cs generated file. You either have a UserCollection of your own in which case you should probably rename it or put it in another namespace. Either that, or you tried to add functionality to the UserCollection and you derive it from another type.
Last possibility is that you have a User table and a Users table. SubSonic will change Users to User. I'm not sure, but it might cause generation errors. I haven't tried it though.
Rob,
Thanks for the help. You got me going on the right track. Apparently, the generator doesn't like tables with the word "Collection" in the name. I see now that the error was with:
public partial class UserCollectionCollection : ActiveList<UserCollection, UserCollectionCollection>
public partial class UserCollection : ActiveList<User, UserCollection>
By adding:
regexMatchExpression="Collection"
regexReplaceExpression="Group"
to the provider in my web.config file I was able to build successfully.

Resources