Updating the UserProfile table in MVC 4 to include other columns-Entity Framework keeps assigning dbo. ahead of the table - vba

...at least I think that's what's happening.
I'm trying to use the UserProfile Table in my MVC website to hold more than just the userID and userName.
I'm sorry I'm a terrible noob and I know this is a poor hack of the example code, but here's what I've got in the AccountController:
Public Function Register(ByVal model As RegisterModel) As ActionResult
If ModelState.IsValid Then
Using db As New UsersContext()
' Attempt to register the user
Try
WebSecurity.CreateUserAndAccount(model.UserName, model.Password)
WebSecurity.Login(model.UserName, model.Password)
Dim id As Integer = WebSecurity.GetUserId(User.Identity.Name)
Dim userProfile As UserProfile = db.UserProfile.Find(id)
If IsNothing(UserProfile) Then
Return HttpNotFound()
End If
db.Entry(UserProfile).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index", "Home")
Catch e As MembershipCreateUserException
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode))
End Try
End Using
End If
It seems to be working, except that it's appending dbo. in front of the Table name instead of the halifax. that I've got in my schema. How do I tell it what to use there? If I put in a dbo. table, the code runs, but it puts the profile information in the wrong place.

I am able to infer two questions here
1. How to change the default schema (dbo) so that table is created under new schema name (halifax)?
Answer: It appears you are using SimpleMembershipProvider (SMP) and internally
WebSecurity.InitializeDatabaseConnection
uses the default schema "dbo" assigned to your database User to create\access the tables
for the SimpleMembershipProvider.
So you will be required to make few changes to let SMP know that it should create table in a particular schema.
I did not found a direct way with SMP to tell it to create UserProfile table in a particular schema.
How I achieved this is by creating my own SMP classes like below:
[Table("UserProfile", Schema = "Halifax")]
public class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[MaxLength(100)]
[EmailAddress]
public string EmailId { get; set; }
}
Please note the schema name added to Table attribute.
You can find full instructions on how to add Simple Membership Tables as Part of Your Entity Framework.
2. From the subject of this question I could infer that you would like to add more columns into the default tables e.g. UserProfile.
Answer: As mentioned above, in order to include additional information in the SMP tables, you will have to create your own equivalent classes and add extra columns in the class. Please note I have added EmailId into the UserProfile table above. If you follow this article (although it intend to cover a different aspect of SMP) you will find instructions to add custom fields. Hope this helps.
Update:
This is how I achieved this in VB (After dozens of try :))
<Table("UserProfile", Schema:="Halifax")>
Public Class UserProfile
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.Identity)>
Public Property UserId() As Integer
Public Property UserName() As String
<MaxLength(100)>
Public Property EmailId() As String
End Class

Related

VS 2013 Controller Scaffolding Fails for the ApplicationUser Model (Multiple object sets per type are not supported)

In a VS 2013 RTM, MVC 5 project with EF 6, I tried to scaffold a controller based on the ApplicationUser (default with individual accounts authentication). Both ApplicationUser and IdentityUser are mapped to a Users table.
The wizard opens the context file for editing and tries to add a new db set for ApplicationUser (ApplicationUsers) and then fails with this error:
Unable to retrieve metadata for ApplicationUser. Multiple object sets per type are not supported. The object sets ApplicationUsers and Users can both contain instances of type ApplicationUser
The solution does not have any reference to, or instance of ApplicationUsers.
Is this a known issue? Can the scaffolding be run using command line with options (from PMC)?
Note: scaffolding also adds an extra db set to the context class if I specify a model that references ApplicationUser (the app works if I remove it and fix references in the generate controller).
Wow. I'm really surprise that no one actually got to the root of this, and instead, are just recommending workarounds.
IdentityDbContext already contains a property:
`public virtual IDbSet<TUser> Users { get; set; }
When you subclass IdentityDbContext to create your own application-specific context, you must specify what class satisfies the TUser generic. The default is:
public ApplicationDbContext : IdentityDbContext<ApplicationUser>
Which then means that you functionally have a property already via inheritance in the form of:
public IDbSet<ApplicationUser> Users { get; set; }
If you then add another property to your application-specific context such as:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
You now have the same entity tracked by two DbSets, and you get that error. The solution? Simply don't add your own DbSet for ApplicationUser. There's no need to rename or override anything.
Short-Version: Rename your ApplicationUser class to User.
I've been running into this problem for about a month with absolutely no luck...until now!
Initially, I thought it was a preview issue, but after persisting into the RTM along with the latest libraries, I became incredibly annoyed, since this problem persisted into Migrations too.
However, IdentityDbContext, according to the error message, seems to be creating two DbSets: ApplicationUsers and Users. We only want Users when looking at the source code:
public class IdentityDbContext<TUser> : DbContext where TUser : Microsoft.AspNet.Identity.EntityFramework.IdentityUser
{
...
public virtual IDbSet<TUser> Users { get; set; }
...
}
From this, we (and the scaffolding engine, and the migrations engine) should only see "Users", not "ApplicationUsers".
To rectify this situation, you will need to adjust your application class to account for this rather strange error. Simply rename your ApplicationUser class to User:
using Microsoft.AspNet.Identity.EntityFramework
...
public class ApplicationUser : IdentityUser
{
Your Stuff
}
To:
using Microsoft.AspNet.Identity.EntityFramework
...
public class User: IdentityUser
{
Your Stuff
}
Attempt to Scaffold again. If you receive another error along the lines of the class cannot be found, save your project, close VS2013, re-open VS2013, load the project, re-build the project, and finally attempt to scaffold. The IdentityDBContext should no longer be creating a dummy "ApplicationUsers" DBSet object causing both Entity Migrations and Scaffolding to issue these errors.
Hope this helps!
P.S. Any mapping done ought not to affect this problem, so you should be able to still map to the same table if you wish to.
EDIT:
If you receive further problems, undo the rename. I ran into some problems (more scaffolding and query errors), and after I went back to ApplicationUser, those problems disappeared and the problem above did not re-occur. Just a heads up.
Read the above problems en solutions.
My error text was:
Multiple object sets per type are not supported. The object sets
'ApplicationUsers' and 'Users' can both contain instances of type
'DataLayerIdentity.Models.ApplicationUser'
I suspect the error was created when I was playing around and scaffolded the model: ApplicationUser in a new controller.
Solved it by removing the below from : ApplicationDbContext.cs
public System.Data.Entity.DbSet<DataLayerIdentity.Models.ApplicationUser> ApplicationUsers
{
get;
set;
}
No Other changes where made to solve the problem. I hope this helps someone.
When you use scaffolding to generate control, vs will auto insert 1 line to your db context
public System.Data.Entity.DbSet<...API.Models.ApplicationUser> ApplicationUsers { get; set; }
Just delete that line, and in your controller. change
db.ApplicationUsers to db.Users
Here is the simplest solution. When you add/scaffold a view (list) based on ApplicationUser as the model, VS2013 ADDS the following to the IdentityModels.vb or .cs file.:
Public Property ApplicationUsers As System.Data.Entity.DbSet(Of ApplicationUser)
Just remove this property and the problem goes away.
If you are trying to create an ApplicationUsersController please follow these steps.
Delete this line from IdentityModels.cs
public System.Data.Entity.DbSet<Project.Models.ApplicationUser> ApplicationUsers { get; set; }
Build the project
control + shift + b
Generate the controller
Right click on the 'Controllers' folder.
Add > Controller
MVC Controller with views, using Entity Framework
Model Class: ApplicationUser
Add
Go back to IdentityModels.cs and delete this line AGAIN
public System.Data.Entity.DbSet<Project.Models.ApplicationUser> ApplicationUsers { get; set; }
Build the project
control + shift + b
Change the database calls to from ApplicationUsers to Users in ApplicationUsersController.cs
control + f to bring up 'Find and Replace'
Click 'Replace in files'
Find what: db.ApplicationUsers
Replace with: db.Users
Replace All
Press play and cross fingers :)
What you can also do:
Create an empty controller, and add the code for the DataContext yourself
protected ApplicationDbContext db { get; private set; }
public HomeController() : this(new ApplicationDbContext())
{
}
public HomeController(ApplicationDbContext db)
{
this.db = db;
}
Then create your methods like Index, Create and so on, and create the views with right-clicking and "Add view..." for each.
Scaffold your views with the list, create, whatever template that is appropriate and choose the ApplicationUser as an model.
Important: Delete the entry in "Data context class", or you will get a similar error again. But if you leave the "Data context class" empty, the scaffolding of the view will work out fine.
I fixed problem by removing DbSet from context and then changing references in controller from ApplicationUsers to Users. It worked - but now i see no point in scaffolding users.
To much things has to by maintained on top level and it just does not work right. Now i know that view models and repository are the way I want to go.

Implementing GUID in Yii

If you have a data model in which one table is a guid table with just a guid column, and many tables have a primary key referencing that guid, how would you recommend incorporating this type of logic into Yii? To create a new model of any guidable thing, you have to create a guid first. Where is the right place to put this sort of logic?
Edit: To be more specific, here is the issue I am facing:
I have a table of guids, tbl_guid, with one column guid that is a MySQL BIGINT
Some tables, like tbl_foo, have a PK guid referencing guid in tbl_guid
The Foo model has the relation self::BELONGS_TO, 'Guid', 'guid'
I do not want to create a new guid until I am definitely ready to create Foo
I'd like to somehow delay the saving of my guid until I'm actually saving (and have otherwise validated) Foo
However, Foo never validates, because it doesn't have a guid.
Edit 2: I've posted my own answer, but I am hoping somebody has a better answer/improvement to suggest. Here are the issues with my answer:
How can we force the owner to comply with some interface, so we don't have to throw in a bunch of conditionals to check for whether or not the owner has an attribute or method, etc
Even though the database will not accept null for guid, it still seems wrong to remove guid from the list of required attributes.
This is the best I've been able to come up with:
Create a new CActiveRecordBehavior for guidable models:
public function beforeSave() {
if (!$this->owner->guid) {
$guid = new Guid;
$guid->save();
$this->owner->guid = $guid->guid;
}
}
Attach the behavior on the model, or define it in the model's behaviors array.
public function init() {
$behavior = new GuidBehavior;
$this->attachBehavior('GuidBehavior', $behavior);
}
Remove the required-ness of guid so validation doesn't fail:
array('name', 'required'), //guid isn't here
Test
$brand->save();
Inheritance. If you implemented my BaseModel code a while ago, you can override the __construct() method in the BaseModel to create an instance of the GUID class.
BaseModel:
public function __construct()
{
parent::__construct();
$newGuid = new GUID();
return $this;
}
See http://www.yiiframework.com/doc/api/1.1/CActiveRecord

remove from collection without load all collection data. confused which collection mapping to use

I have a many-to-many relationship between Assignment and User
When trying to delete an user from an assignment, I see all users are loaded in the collection.
How to I avoid that?
public class User
{
public virtual int Id { get; private set; }
public virtual IList<Assignment> Assignments { get; set; }
}
public class Assignment
{
public virtual int Id { get; private set; }
public virtual ICollection<User> Users { get; set; }
}
Mappings:
HasManyToMany(user => user.Assignments).Table("UserToAssignment").ParentKeyColumn("UserId").ChildKeyColumn("AssignmentId").Inverse().ExtraLazyLoad();
HasManyToMany(productAssignment => productAssignment.Users).AsSet().Table("UserToAssignment").ParentKeyColumn("AssignmentId").ChildKeyColumn("UserId").LazyLoad();
Calling code:
assignment.Users.Remove(user)
Initially I used Bag instead of Set for Assignment mapping, but when updating it, it was deleting and then reinserting alot of rows in the AssignmentsToUsers table. So I changed to using Set.
But now I see a problem with using Set: it brings all data in memory.
What is the recommended way of doing this?
You can't avoid this and I would ignore it if performance is acceptable. If performance is a problem, there are three ways I can think of to tackle it:
If the other side of the collection (User.Assignments) is lighter weight then remove the assignment from the user instead.
Model the many-to-many table and delete the object directly. You would have to be certain that the Users collection is not going to be loaded prior to this because the in-memory representation will still contain the deleted record.
Direct delete using SQL -- this has the same caveat as #2.
You should use extra lazy mode also for Assignment.Users.

How to create a NHibernate proxy object with some initiliazed fields (other than Id)?

I want to create an object proxy, similar to what ISession.Load is returning, but with some fields initialized. For other properties, when accessed, the proxy will fetch the entire object from database.
Consider the following example:
public class User
{
protected User() {
}
public User(int id, string username, string email) {
// ...
}
// initialize the following fields from other datasources
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
// the rest of fields when accessed will trigger a select by id in the database
public virtual string Field1 { get; set; }
public virtual string Field2 { get; set; }
public virtual DateTime Field3 { get; set; }
public virtual ISet<Comment> Comments { get; set; }
}
The Id, UserName, Email are well-known in my case, so I could create an object proxy containing these fields, and for the others leave the default proxy behavior. In addition to throwing an exception if this id is not found in the database, i could throw an exception if preinitialized fields do not match or overwrite them silently. I am using NHibernate.ByteCode.Castle for proxy factories.
Edit:
The purpose for this is to be able to have some projection properties from an entity which can be queried elsewhere (say. a lucene index) and to avoid database calls. Then instead of wrapping these fields in a custom component class containing only these subset of properties, I want to use the proxy object directly so that I am able to load the rest of fields if needed. In the best case scenario I wouldn't hit the database at all, but in some corner cases I'd like to access other fields, too. The SELECT N+1 problem's impact can be greatly reduced by using batching.
An hypothetical version of code I want to use would be:
// create User object proxy with some fields initialized
var user = Session.Load<User>(5, new { UserName = "admin", Email = "admin#company.com" });
Console.WriteLine(user.Id); // doesn't hit the database
Console.WriteLine(user.UserName); // doesn't hit the database
Console.WriteLine(user.FullName); // doesn't hit the database
if (somecondition) {
Console.WriteLine(user.Field1); // fetches all other fields
}
You can specify an eager fetch inside the query to actually retrieve the needed associations. This could be done in different ways depending on what query style ( Criteria,Hql or LINQto NH ) you are using. But the key is changing the fetch mode.
for non-collection properties, I wouldn't do that;
the cost of prefetching them from the DB when you load your entity is (usually) so small that I wouldn't even bother.
for collection properties, just mark the collection fetch strategy as 'lazy=true'.
The only case where I would think about doing something like that is when I have a very large number of properties which I don't need (in your example- say Field1..Field20).
In that case I would either:
1. Define those properties together as a component, or
2. create a DTO for fetching only a subset of your entity's properties.
specifying lazy = "true" (or Not.LazyLoad() for Fluent NHib) on properties Field1, Field2, Field3, Comments mappings may help, though not sure about Select N+1 issue.
another way to go is specifying lazy = "false" for UserName, Email

Inheritance with Fluent NHibernate produces double results

I have the following scenario:
public class Login
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
}
public class User : Login
{
public IList<Account> Accounts { get; set; }
}
If I have two maps (ClassMap<Login> and ClassMap<User>), all queries returns double results - one per maps I'd guess.
Is it possible to avoid this? I don't have a discriminator value. There are a lot more properties on the user, so I'd like to be able to just get a sub set of these via the login.
Since User extends Login, querying Login will, by default, return User instances too.
To avoid this, polymorphism="explicit" must be used in the XML mappings.
For Fluent mappings, use Polymorphism.Explicit().
Perhaps specifying different data tables for the two entities Login and User might do the trick, so you would have to explicitely query either the Login or User depending on your needs. Otherwise, Diego Mijelshon's answer sounds pretty good!