I am trying to seed users and roles into my database.
Currently using Code First Entity Framework with Automatic Migrations in C# MVC4.
Whenever I call
Update-Database -Force
I get the following error:
Running Seed method.
System.InvalidOperationException: You must call the
"WebSecurity.InitializeDatabaseConnection" method before you call any
other method of the "WebSecurity" class. This call should be placed in
an _AppStart.cshtml file in the root of your site.
at WebMatrix.WebData.SimpleRoleProvider.get_PreviousProvider()
at WebMatrix.WebData.SimpleRoleProvider.RoleExists(String roleName)
at System.Web.Security.Roles.RoleExists(String roleName)
at GratifyGaming.Domain.Migrations.Configuration.Seed(GratifyGamingContext
context) in C:\Users\Unreal\Documents\Visual Studio
2010\Projects\GratifyGaming\GratifyGaming.Domain\Migrations\Configuration.cs:line
36
at System.Data.Entity.Migrations.DbMigrationsConfiguration1.OnSeed(DbContext
context)
at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String
targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class.
This call should be placed in an _AppStart.cshtml file in the root of
your site.
The offending line of code is the Role.Exists
I have tried putting the WebSecurity.InitializeDatabaseConnection in Global.asax, Seed(), and created an _AppStart.cshtml with no success. I have trawled the internet looking for a possible solution and none of them have worked (including other stack overflow articles). Some notable blog posts are below.
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
http://odetocode.com/Blogs/scott/archive/2012/08/31/seeding-membership-amp-roles-in-asp-net-mvc-4.aspx
See code below.
[Configuration.cs]
protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
{
var criteria = new List<Criterion>
{
new Criterion { ID = 1, IsMandatory=true, Name = "Gameplay", Description="The playability of the games core mechanics" },
new Criterion { ID = 2, IsMandatory=true, Name = "Art Style", Description="The artistic feel of the game as a whole. Elements such as story, style and originality come into play." },
new Criterion { ID = 3, IsMandatory=true, Name = "Longevity", Description="How long did this game keep you entertained?" },
new Criterion { ID = 4, IsMandatory=true, Name = "Graphics", Description="How good does the game look?" }
};
criteria.ForEach(s => context.Criterion.AddOrUpdate(s));
context.SaveChanges();
if (!Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
if (!WebSecurity.UserExists("user"))
WebSecurity.CreateUserAndAccount(
"user",
"password");
if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
Roles.AddUsersToRoles(new[] { "user" }, new[] { "Administrator" });
}
The Criterion seed code works without fail.
[_AppStart.cshtml]
#{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",
"UserName", autoCreateTables: true);
}
}
Normal login to my site works perfectly with this located here.
[web.config]
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
[AccountModel.cs]
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public virtual ICollection<Game> AttachedGames { get; set; }
public virtual ICollection<UserGratificationRecord> GratificationHistory { get; set; }
[ForeignKey("UserLevel")]
public int? AcheivementID { get; set; }
public virtual Acheivement UserLevel { get; set; }
public int? NumOfGratifictions { get; set; }
}
UPDATE
I think the WebSecurity.InitializeDatabaseConnection is not even being run - I can place more than one in my Seed method, and not get the 'can only be called once' error that you would normally get.
My seed method is in my domain project along with all the models, while everything else is in a WebUI project. Not sure if this has anything to do with it.
Just put that lazy init into the top of your Seed method
protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile",
"UserId",
"UserName",
autoCreateTables: true);
}
In your App_Start, try adding:
var configuration = new Data.Migrations.Configuration();
var migrator = new DbMigrator(configuration);
migrator.Update();
You will have to make your configuration.cs file public
public class Configuration : DbMigrationsConfigurati
This should make your seed method get called when you run your program
Delete your existing reference to WebMatrix.WebData
Add reference WebMatrix.WebData version 2.
Error will stop.
Related
I am trying to get a user registered through a sign up page. When I am clicking register button an error shows syaing:
System.Data.SqlClient.SqlException: 'Cannot drop database "Officework" because it is currently in use.'
"Officework" is the name of my project.
I have made this project using code first approach. To make a distinction between business logic and database I have implemented repository.
This is were error is showing when I'm clicking on Register button:
public void InsertStudent(Population student)
{
context.Populations.Add(student);
}
This is my Sign Up controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SignUp([Bind(Include = "Email,FirstName,LastName,MobileNumber,DateOfBirth,Password")] Population population)
{
if (ModelState.IsValid)
{
population.Password =password_hiding.encrypt(population.Password);
studentRepository.InsertStudent(population);
studentRepository.Save();
return RedirectToAction("SignIn");
}
return View(population);
}
The password_hiding is the object of the class ""Password", which I have used to encrypt password in database and decrypt it when showing to "User".
Password.cs class code
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace OfficeWork.ED
{
public class Password
{
public string encrypt(string clearText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
public string Decrypt(string cipherText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
}
}
The code of my model class
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
namespace OfficeWork.Models
{
public class Population
{
[Key]
public int ID { get; set; }
[Required]
[Display(Name ="Email")]
[EmailAddress(ErrorMessage ="Enter valid email address")]
public string Email { get; set; }
[StringLength(10)]
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[StringLength(10)]
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Mobile Number")]
[Required]
public long MobileNumber { get; set; }
[Display(Name = "Date Of Birth")]
[Required]
public DateTime DateOfBirth { get; set; }
[Display(Name = "Password")]
[Required]
[MaxLength(10000)]
public string Password { get; set; }
}
}
I also looked for the different kind of solution. One of them suggested to use Pooling=false in my connection string to close other connection, but it doesn't work in my case.
Here is my complete connection string:
<connectionStrings>
<add name="PopulationDBContext" connectionString="Data Source=ANIRUDH-PC\SQLEXPRESS;Initial Catalog=Officework;Integrated Security=True;Pooling=false;"
providerName="System.Data.SqlClient" />
</connectionStrings>
You need to replace the current Database Initialization Strategy, this sounds like you have the default DropCreateDataBaseAlways or the DropCreateDatabaseIfModelChanges strategy.
There are four different database initialization strategies:
CreateDatabaseIfNotExists: This is the default initializer. As the name suggests, it will create the database if none exists as per the configuration. However, if you change the model class and then run the application with this initializer, then it will throw an exception.
DropCreateDatabaseIfModelChanges: This initializer drops an existing database and creates a new database, if your model classes (entity classes) have been changed. So, you don't have to worry about maintaining your database schema, when your model classes change.
DropCreateDatabaseAlways: As the name suggests, this initializer drops an existing database every time you run the application, irrespective of whether your model classes have changed or not. This will be useful when you want a fresh database every time you run the application, for example when you are developing the application.
Custom DB Initializer: You can also create your own custom initializer, if the above do not satisfy your requirements or you want to do some other process that initializes the database using the above initializer.
This is usually declared in the constructor for your DbContext, but it can also be configred from a static initializer or in the app.config, the following is an example on how to remove the Db Initialization Strategy Altogether:
public partial class ConfigData : DbContext
{
static ConfigData()
{
//Database.SetInitializer(
// new System.Data.Entity.MigrateDatabaseToLatestVersion<ConfigData, Migrations.Configuration>());
Database.SetInitializer<ConfigData>(null);
SqlProviderServices.SqlServerTypesAssemblyName = typeof(SqlGeography).Assembly.FullName;
}
...
}
If you remove the strategy then you will be forced to manually call the EF update-database command from the package manager console, for small projects or singleton databases, this might be an option.
You can easily write your own strategy, say to only apply Upgrades:
public class CheckAndMigrateDatabaseToLatestVersionOnly<TContext, TMigrationsConfiguration>
: IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
public virtual void InitializeDatabase(TContext context)
{
var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));
if (migratorBase.GetPendingMigrations().Any())
{
migratorBase.Update();
}
}
}
Then you could use that in the following change to the previous snippet:
Database.SetInitializer<ConfigData>(
new CheckAndMigrateDatabaseToLatestVersionOnly<ConfigData, Migrations.Configuration>());
I'm try to use PetaPoco with SQLite on MVC5. I created a model to make the mapping.
namespace LittleDemo.Models.UserModel
{
[PetaPoco.TableName("User")]
[PetaPoco.PrimaryKey("UserId", AutoIncrement = true)]
public class User
{
[Column]
public int UserId { get; set; }
[Column]
public string Name { get; set; }
}
}
Then I create a controller file
public ActionResult UserView()
{
ViewBag.Message = "Your User page.";
var dbUser = new PetaPoco.Database("sqlite");
var userA = new User {Name = "A"};
dbUser.Save(userA);
return View();
}
create the database and put some data in. then create a view to show all the list of the data. but it returns a error message "no user table found".
How can I assign the table with PetaPoco.
I have invested a lot of time to make it work but I found nothing.
You could achieve same using https://github.com/schotime/NPoco
Nuget available here https://www.nuget.org/packages/NPoco/
You need to do some work around to make it work.
open app.config/web.config
Step 1:
install https://www.nuget.org/packages/NPoco/
Step 2:
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
<remove invariant="System.Data.SQLite" /><add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
Step 3:
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source = C:\db\test.db;" providerName="System.Data.SQLite" />
</connectionStrings>
Step 4:
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
static void Main(string[] args)
{
var config = ConfigurationManager.ConnectionStrings["DefaultConnection"];
var db = new MyDb(config.ConnectionString, config.ProviderName);
List<User> users = db.Fetch<User>(string.Empty);
Console.WriteLine(users.Count);
}
public class MyDb : NPoco.Database
{
public MyDb()
: base("DefaultConnection")
{
}
public MyDb(string connectionString, string providerName)
: base(connectionString, providerName)
{
}
}
Reference :
https://github.com/venkata-ramana/SQLiteSample
Let me know if you have any queries, Thank you.
I am following along to this tutorial on EF6 and Codefirst. http://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
I started a solution and just added the Models, the contexts and the initializer
namespace TestContoso.DAL
{
public class SchoolContext : DbContext
{
public SchoolContext()
: base("Name=SchoolContextDB")
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
and created a dummy Home Controller and Index view.
namespace TestContoso.Controllers
{
public class HomeController : Controller
{
private SchoolContext db = new SchoolContext();
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
I have also set the Connection String and set the initializer in web.config
<entityFramework>
<contexts>
<context type="TestContoso.DAL.SchoolContext, TestContoso">
<databaseInitializer type="TestContoso.DAL.SchoolInit, TestContoso"/>
</context>
</contexts>
When I run the solution, my expectation is that the database would get created, however I notice that the OnModelCreating event never fires so the db is never created. Why isn't the db getting created? How would I cause the Code First migration to fire?
The article gives you an alternative. Try the alternative method of adding the initialization strategy via code to see if that works.
As an alternative to setting the initializer in the Web.config file is to do it in code by adding a Database.SetInitializer statement to the Application_Start method in in the Global.asax.cs file.
Database.SetInitializer(new CreateDatabaseIfNotExists<SchoolContext>());
And as specified in the comments. Run a query against the data or follow the section on 'Set up EF to initialize the database with test data' to automatically seed the database.
The OnModelCreating event only seemed to get fired when there was some query querying that model, to test that I just added a dummy query in the Index controller and it worked.
public class HomeController : Controller
{
private SchoolContext db = new SchoolContext();
//
// GET: /Home/
public ActionResult Index()
{
var students = from s in db.Students
select s;
return View();
}
}
}
I have the following account controller
public class AccountController : Controller
{
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
public AccountController(IMembershipService membership)
{
MembershipService = membership;
}
[HttpPost]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.EmailorUserName, model.Password))
{
.....
}
}
}
from my unit testing project I want to simulate a login
public class AccountControllerTest2
{
[Test]
public void Login_UserCanLogin()
{
string returnUrl = "/Home/Index";
string userName = "user1";
string password = "password1";
Mock<AccountMembershipService> Membership = new Mock<AccountMembershipService>();
AccountController Controller = new AccountController(Membership.Object);
var model = new LoginModel
{
EmailorUserName = userName,
Password = password
};
var result = Controller.Login(model, returnUrl) as RedirectResult;
Assert.NotNull(result);
Assert.AreEqual(returnUrl, result.Url);
}
}
my web config in my main application uses custommembership provider
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider" type="QUBBasketballMVC.Infrastructure.CustomMembershipProvider" connectionStringName="UsersContext" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
I keep getting this error
QUBBasketballMVC.Tests.Controllers.AccountControllerTest.Login_UserCanLogin:
System.Web.Management.SqlExecutionException : An error occurred during the execution of the SQL file 'InstallCommon.sql'. The SQL error number is 5123 and the SqlException message is: CREATE FILE encountered operating system error 5(Access is denied.) while attempting to open or create the physical file 'C:\PROGRAM FILES (X86)\NUNIT 2.6.3\BIN\APP_DATA\ASPNETDB_TMP.MDF'.
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.
Creating the ASPNETDB_7b94db5a0b5b4fbbbe22fa8e91e4cc68 database...
It seems that you are still initializing the real membership database, meaning that the MembershipService hasn't been completely mocked out. You shouldn't need to add the membership config to your unit tests, given that you intend mocking it out completely.
You almost certainly want to mock the Interface to your service abstraction IMembershipService, viz:
Mock<IMembershipService> Membership = new Mock<IMembershipService>();
As an aside, the lazy initialization code
if (MembershipService == null)
MembershipService = new AccountMembershipService();
isn't ideal from a testing point of view, given that it means that the controller has 2 modes of operation, whereby it can either create the MembershipService itself, or accept one as a constructor dependency.
As an alternative, you might consider an IoC framework here to manage dependency lifespans, and this way there is only one set of code to be tested.
Ok so here's some context: I'm using EF5, MVC4 and SQL CE4 to build up a web application. I've been loosely following this tutorial with a few differences.
My context class and POCO objects are in their own assembly.
I'm using SQL CE4 instead of SQL Express Local DB
My classes aren't as simple as the tutorial
I've already used a workaround to get simple classes to work register.
I had thought using enums in EF5 was supported in EF5, but can they be used in Keys?
When I try to add a control (Add Controller, MVC controller with read/write actions and views, using Entity Framework) for a simple class (1 int key property, 1 string property), it works.
I get varied errors when trying to add a class that has a property which is part of a key (primary or foreign)
Unable to retrieve metadata for 'blah'. Using the
same DbCompiledModel to create contexts against different types of
database servers is not supported. Instead, create a separate
DbCompiledModel for each type of server being used.
Index was out of range. Must be non-negative and less than the size of
the collection.
Parameter name: index
C:\Program Files (x86)\Microsoft Visual Studio\11.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 4\
CodeTemplates\AddController\ControllerWithContext.tt(0,0) : error :
Running transformation: System.IndexOutOfRangeException: Index was outside the bounds of the
array.
---StackTrace---
The only similarities I've found between these classes is that they have an emun that's tied to a key. Other classes with non-key enums generate correctly. Is this the issue or have I completely missed the mark?
Edit: Example of a class which fails
public class A
{
public virtual AIdEnum Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
Ok, so I've just ran this up quickly with SQL CE 4 and it appears to work great:
public class EnumTestContext : DbContext
{
public EnumTestContext() : base("CEDB")
{
}
public DbSet<MyClass> MyClasses { get; set; }
}
public enum MyEnum
{
EnumValue1,
EnumValue2
}
public class MyClass
{
[Key, Column(Order = 0)]
public MyEnum MyEnumKey { get; set; }
[Key, Column(Order = 1)]
public int MyIntKey { get; set; }
[Column(Order = 2)]
public string Name { get; set; }
}
I then add an entity like this:
using (var context = new EnumTestContext())
{
context.MyClasses.Add(new MyClass()
{
MyEnumKey = MyEnum.EnumValue1,
MyIntKey = 22,
Name = "Hello World"
});
context.SaveChanges();
}
This is all working fine for me - does this help?
You need put this line:
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
before the DbContext lifecicle beggin.
A exemple that you can to download in the MSDN Gallery
It seems my issue is that Controller creation doesn't work with SQLCE 4.0 connectionStrings so using a conectionString of provider of System.Data.SqlClient handled that issue.
The next problem I had was that connectionString values (such as encryption) were not respected through this means so I now have 2 different constructors to get around this bug.
#if DEBUG
public Context()
: base("name=DefaultConnection")
{ ; }
#else
/// <summary>
/// Default Constructor
/// </summary>
public Context()
: base("name=CompactConnection")
{ ; }
#endif
Here's my configuration:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
<connectionStrings>
<add name="CompactConnection" providerName="System.Data.SqlServerCe.4.0"
connectionString="Data Source="|DataDirectory|\DB.sdf";encryption mode=platform default;password="P4$$w0rd!""/>
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MsSqlCe-20121028004432;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MsSqlCe-20121028004432.mdf" />
</connectionStrings>
Of course, this is just a workaround for a deeper issue. If anyone else knows of the root cause of this issue, I'd love to know.