Entity Framework will not recognize webpage_UsersInRoles - asp.net-mvc-4

I am using EF5 VS2012 and using SimpleMembership. I have let MS auto-create the SQL tables
WebSecurity.InitializeDatabaseConnection("SqlRoleManagerConnection", "webpages_Users", "UserID", "Username", true);
And whenever I attempt to create a model from DB to create EDMX, it omits the webpages_UsersInRoles table. There are references to this table in the XML but it does not appear on the diagram and no classes are generated for it. I am running VS2012 Update 1 so this is not related to the commonly reported bug. I have also manually selected Run Custom Tool which does not fix.
As you probably know, this missing table only contains two FK fields to link the Users and Roles tables.
I have attempted creating a new project and new EDMX files and they all produce the same result - missing webpages_UsersInRoles diagram & classes.
EDIT: I can repeating go into Update from DB and select the table and it will not add to the diagram or class. What is the reason for this behavior and how can I force EF to connect everything so I can use this table and class?

Generally you don't reference that table directly in your code. You work with the Membership API.
Here's a good post to read about it: Seeding Membership & Roles in ASP.NET MVC 4
Sample code:
private void SeedMembership()
{
if (!Roles.RoleExists("Administrator"))
{
Roles.CreateRole("Administrator");
}
if (!Roles.RoleExists("Teacher"))
{
Roles.CreateRole("Teacher");
}
if (!Roles.RoleExists("Student"))
{
Roles.CreateRole("Student");
}
if (!WebSecurity.UserExists("leniel"))
{
WebSecurity.CreateUserAndAccount("leniel", "mypass");
}
if (!Roles.GetRolesForUser("leniel").Contains("Administrator"))
{
Roles.AddUsersToRoles(new[] { "leniel" }, new[] { "Administrator" });
}
if (!WebSecurity.UserExists("tester"))
{
WebSecurity.CreateUserAndAccount("tester", "test123");
}
if (!Roles.GetRolesForUser("tester").Contains("Administrator"))
{
Roles.AddUsersToRoles(new[] { "tester" }, new[] { "Administrator" });
}
}

I think it's the way EF works with Many-To-Many relationships,
below are 2 explanations and how to work with them:
http://nileshhirapra.blogspot.in/2012/03/entity-framework-insert-operation-with.html
http://weblogs.asp.net/zeeshanhirani/archive/2008/08/21/many-to-many-mappings-in-entity-framework.aspx

Related

ABP IO Code sample for run multiple databases for multi-tenancy

Please notice that I am talking about ABP.io, not the Boilerplate framework.
The in-build free module Tenant-Management is developed to work with multiple tenants and a unique database. however, the documentation says that the framework has a built-in friendly way to use the multiple database approach, including:
new dbContext
database migration and seeding
Connection String service
I am new in ABP IO, and I want a sample that employs the framework elements to implement a single database for every tenant.
I get started by overriding the tenant create sync method of the tenant management module as follows.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITenantAppService), typeof(TenantAppService), typeof(ExtendedTenantManagementAppService))]
public class ExtendedTenantManagementAppService : TenantAppService
{
public ExtendedTenantManagementAppService(ITenantRepository tenantRepository,
ITenantManager tenantManager,
IDataSeeder dataSeeder) : base(tenantRepository, tenantManager, dataSeeder)
{
LocalizationResource = typeof(WorkspacesManagerResource);
ObjectMapperContext = typeof(WorkspacesManagerApplicationModule);
}
public override async Task<TenantDto> CreateAsync(TenantCreateDto input)
{
var tenant = await TenantManager.CreateAsync(input.Name);
input.MapExtraPropertiesTo(tenant);
await TenantRepository.InsertAsync(tenant);
await CurrentUnitOfWork.SaveChangesAsync();
using (CurrentTenant.Change(tenant.Id, tenant.Name))
{
//TODO: Handle database creation?
// create database
// migrate
// seed with essential data
await DataSeeder.SeedAsync(
new DataSeedContext(tenant.Id)
.WithProperty("AdminEmail", input.AdminEmailAddress)
.WithProperty("AdminPassword", input.AdminPassword)
);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
}
Any code sample?

How to use existing data from the database in Codeception FactoryMuffin?

I'm trying to set up easy test data in my Acceptance tests:
public function shouldUseAFakeAccountHolder(AcceptanceTester $I) {
$I->have(AccountHolder::class);
// ...
}
I've copied the example code from the Codeception documentation and modified it with my entity names (as well as fixing the bugs).
<?php
public function _beforeSuite()
{
$factory = $this->getModule('DataFactory');
// let us get EntityManager from Doctrine
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(AccountHolder::class, [
'firstName' => Faker::firstName(),
// Comment out one of the below 'accountRole' lines before running:
// get existing data from the database
'accountRole' => $em->getRepository(AccountRole::class)->find(1),
// create a new row in the database
'accountRole' => 'entity|' . AccountRole::class,
]);
}
The relationship using existing data 'accountRole' => $em->getRepository(AccountRole::class)->find(1) always fails:
[Doctrine\ORM\ORMInvalidArgumentException] A new entity was found through the relationship 'HMRX\CoreBundle\Entity\AccountHolder#accountRole' that was not configured to cascade persist operations for entity: HMRX\CoreBundle\Entity\AccountRole#0000000062481e3f000000009cd58cbd. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'HMRX\CoreBundle\Entity\AccountRole#__toString()' to get a clue.
If I tell it to create a new entry in the related table 'accountRole' => 'entity|' . AccountRole::class, it works, but then it adds rows to the table when it should be using an existing row. All the role types are known beforehand, and a new random role type makes no sense because there's nothing in the code it could match to. Creating a duplicate role works, but again it makes so sense to have a separate role type for each user since roles should be shared by users.
I've had this error before in Unit tests, not Acceptance tests, when not using Faker / FactoryMuffin, and it's been to do with accessing each entity of the relationship with a different instance of EntityManager. As soon as I got both parts using the same instance, it worked. I don't see how to override the native behaviour here though.
It works (at least in Codeception 4.x) by using a callback for the existing relation:
<?php
public function _beforeSuite()
{
$factory = $this->getModule('DataFactory');
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(AccountHolder::class, [
'firstName' => Faker::firstName(),
'accountRole' => function($entity) use ($em) {
$em->getReference(AccountRole::class)->find(1);
},
]);
}
I've found it here: https://github.com/Codeception/Codeception/issues/5134#issuecomment-417453633

How to seed initial data after adding new entity?

I've noticed a Seed folder in MyProject.EntityFrameworkCore project with the code to seed initial data to the database.
If I add code to populate the database with my new entity, where and how will the code be called?
Do the .NET Core and the full .NET Framework versions work the same way?
It is run:
On application startup, called in the PostInitialize method of YourEntityFrameworkModule:
public override void PostInitialize()
{
if (!SkipDbSeed)
{
SeedHelper.SeedHostDb(IocManager);
}
}
If you build Migrator project and run the .exe, called in Run method of MultiTenantExecuter:
public void Run(bool skipConnVerification)
{
// ...
Log.Write("HOST database migration started...");
try
{
_migrator.CreateOrMigrateForHost(SeedHelper.SeedHostDb);
}
// ...
}
If you add new code to populate your custom entity, remember to check before adding, like this:
var defaultEdition = _context.Editions.IgnoreQueryFilters().FirstOrDefault(e => e.Name == EditionManager.DefaultEditionName);
if (defaultEdition == null)
{
// ...
/* Add desired features to the standard edition, if wanted... */
}
Yes, the .NET Core and full .NET Framework versions work the same way.

How to easily convert .sdf developed in Vs 2010 to SQL Server database

I don't have SQL Server installed my machine. Hence I decided to start working with a SQL Server Compact Edition (.sdf) in VS2010. After then I installed SQL Server at the moment, now I'd like to convert from .sdf to a "real" SQL Server database.
How can I do it ? Please advise.
What you should to is to change your connectionString that points to your Compact SQL to your new SQL Server instance.
After that in you Context file, inside of a static constructor which is called:
static YourDbContext()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<YourDbContext>());
}
This should create your database and tables based on your models. If you need to insert any data you should Enable-Migrations and in a configuration file that is createdc8 override Seed method.
One important thing DO NOT ASSUME that you have rights or privileges to CREATE or DROP DATABASE or to execute table modificiations.
I will assume that you used EF Code First approach. Your context file could look something like this:
public YourDbContext : DbContext
{
static YourDbContext()
{
// Database.SetInitializer<DbContext>(null); // Change this line to the next one
Database.SetInitializer(new CreateDatabaseIfNotExists<YourDbContext>());
}
// The rest of implementation
}
Inside of Visual Studio in Package Manager Console execute:
Enable-Migrations -ProjectName YourProjectName
(If you have more than one DbContext implementation you will need to follow the instructions from the error message that Enable-Migrations throws back at you.)
Once this is done you will notice a new folder Migrations with one file Configuration.cs. Open it and you will see method Seed.
protected override void Seed(YourDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
// Here you can call your context.DbSetImplementation.Add(new Something {...});
}
That's about it.

How do I manage profiles using SimpleMembership?

I have an ASP.NET MVC 4 site based off the internet template. I am using the SimpleMembership which i set up with that template.
I can modify the Users table which has been creted for me but I am unsure as to the "correct" way to modify the extra fields I have added. I want Fullname, Email etc and have added them to the user table but there appears no way to update through the SimpleMembership WebSecurity.* static methods.
Are you supposed to just update those properties yourself using EF outside of the SimpleMembership API?
1 - You need to enable migrations, prefereably with EntityFramework 5
2 - Move your
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
to your Seed method in your YourMvcApp/Migrations/Configuration.cs class
protected override void Seed(UsersContext context)
{
WebSecurity.InitializeDatabaseConnection(
"DefaultConnection",
"UserProfile",
"UserId",
"UserName", autoCreateTables: true);
if (!Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
if (!WebSecurity.UserExists("lelong37"))
WebSecurity.CreateUserAndAccount(
"lelong37",
"password",
new {Mobile = "+19725000000", IsSmsVerified = false});
if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
}
Now EF5 will be in charge of creating your UserProfile table, after doing so you will call the WebSecurity.InitializeDatabaseConnection to only register SimpleMembershipProvider with the already created UserProfile table, also tellling SimpleMembershipProvider which column is the UserId and UserName. I am also showing an example of how you can add Users, Roles and associating the two in your Seed method with custom UserProfile properties/fields e.g. a user's Mobile (number) and IsSmsVerified.
3 - Now when you run update-database from Package Manager Console, EF5 will provision your table with all your custom properties
For additional references please refer to this article with sourcecode:
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
They made it easy to modify the profile with SimpleMembership. SimpleMembership is using the code first EF model and the user profile is defined in the file AccountModels.cs that is generated as part of the Internet template for MVC 4. Just modify the class UserProfile and add the new fields in the class definition. For example, adding a field for email would look something like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
Here is an example on how you would access the email field:
var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var email = user.Email;
Here is what the database looks like after adding the email field.
There is a good blog that describes some of the changes in SimpleMembership here. You can also find more detailed information on customizing and seeding SimpleMembership here.
if you look right around line 273 of the accountcontroller you'll find this line
db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
Looks like even OOTB they (MS) are doing just as you suggested and using EF to update.
I too, am looking for the "correct" way of updating and accessing these properties.
Edit:
Here's my solution (I'm happy if someone says there's an OOTB way to do this).
wrap UserProfile (the .net Entity from SimpleMembership) in a session class.
public static class sessionHelpers {
public static UserProfile userProfile
{
get
{
if (HttpContext.Current.Session["userProfile"] != null)
{
return HttpContext.Current.Session["userProfile"] as UserProfile;
}
else
{
using (UsersContext db = new UsersContext())
{
HttpContext.Current.Session["userInfo"] =
db.UserProfiles.Where(x => x.UserName ==
HttpContext.Current.User.Identity.Name).FirstOrDefault();
return db.UserProfiles.Where(x => x.UserName ==
HttpContext.Current.User.Identity.Name).FirstOrDefault();
}
}
}
set { HttpContext.Current.Session["userProfile"] = value; }
}
}
From this you can access the profile table by doing
string foo = sessionHelpers.userProfile.FIELDNAME;
where sessionHelpers is my wrapper class. The if block just ensures that if it hasn't been set in the current session that accessing it will attempt to get it.
You need to add them to your Database ( done according to description )
add them to the view ( edit , add , delete , and view ) unless modified
add them to your model in UserProfiles
Then it will work.