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

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.

Related

EF Core 2.0 Trouble 'Cascading' Inserts for Related Entities When Updating Principle Entity

ASP.NET Core 2 Web application using a REST API. Currently using sqlite3 for development database. (Also tried migrating to SQL Server and got same results as below).
I'm sending an entity to web client, the client makes changes to the entity that involve adding a new related entity and then that updated principle entity gets sent back as json in body of PUT a request.
I was hoping the new related entity would get created automatically, but this is not happening. The simple properties on the principle entity are updated properly, but not reference properties. I'm not getting any exceptions or anything - it just seems to be ignoring the reference properties.
Simplified Classes (I removed other properties that shouldn't affect the relationship):
public partial class DashboardItem {
public int Id { get; set; }
public int? DataObjectId { get; set; }
public DataObject DataObject { get; set; }
}
public partial class DataObject {
public int Id { get; set; }
}
Portion of DbContext Fluent API for associated property:
modelBuilder.Entity<DashboardItem>(entity => {
entity.HasOne(p => p.DataObject)
.WithMany()
.HasForeignKey(p => p.DataObjectId);
});
Controller Method for PUT:
[HttpPut("{id}")]
public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != entity.Id)
{
return BadRequest();
}
_context.Entry(entity).State = EntityState.Modified;
try{
await _context.SaveChangesAsync();
}catch (DbUpdateConcurrencyException)
{
if (!DashboardItemExists(id)){
return NotFound();
}else {
throw;
}
}
return NoContent();
}
The simplified json (without all the other properties) would look like this (I've tried different variations of have the foreign key "DataObjectId" removed from the json, set to null, or set to zero in case that might be interferring.):
{
Id:1,
DataObjectId:null,
DataObject:{
Id: 0
}
}
When debugging in the controller action method, the existing "DashboardItem" principle entity created from the request body has the reference property "DataObject" populated before getting added to the DbContext, but the new DataObject never gets created in the database. There is only a SQL UPDATE statement issued for DashboardItem and no INSERT for DataObject.
I've also tried making the controller method synchronous instead of async, using DbContext.SaveChanges() instead of .SaveChangesAsync(), since there used to be a problem with that in earlier versions of EF Core related to creating related entities, even though I'm using 2.0 which already has a fix for that. Same result.
This EFCore Doc sounds like it should just work out of the box.
This has worked for me in a prior project. What am I missing here?
Basically, my mistake was in assuming the process of updating data was much simpler than it actually is when sending the updated data from a client in a web application.
After digging a lot more, it seems that the following line in my controller method for handling the PUT request is the problem:
_context.Entry(entity).State = EntityState.Modified;
Setting the entity entry state to Modified in this way results in Entity Framework Core ignoring the reference properties for the related objects - the SQL UPDATE generated will only address the columns in the entity table.
This simple summary eventually got me started down the right path.
Summarizing what I've now learned:
This controller method is dealing with a 'detached' entity that was edited and sent back from the client. The DbContext is not yet tracking this entity since I get a new instance of the context with each http request (hence the entity is considered 'detached'). Because it is not being tracked yet, when it is added to the DbContext, the context needs to be told whether this entity has been changed and how to treat it.
There are several ways to tell the DbContext how to handle the detached entity. Among those:
(1) setting the entity state to EntityState.Modified will result in ALL properties being included in the SQL update (whether they've actually changed or not), EXCEPT for the reference properties for related entities:
_context.Entry(entity).State = EntityState.Modified;
(2) adding the entity with a call to DbContext.Update will do the same as above, but will include the reference properties, also include ALL properties on those entities in the update, whether they've changed or not:
_context.Update(entity)
Approach #2 got things working for me, where I was just trying to get the new related child entity to be created in the Update to its parent.
Beyond that, DbContext.Attach() and DbContext.TrackGraph sound like thy provide more find-grained control over specifying what specific properties or related entities to include in the update.

Piranha CMS on existing MVC 5 site

I am new to Piranha CMS (and in any CMS in general). I have a question about integrating the system to an existing MVC 5 application. Here is what I have done so far:
I have followed the steps in the Getting Started section
I have created the database and set up the connection string
I have created the admin user and can log in to the manager interface
What I am having trouble is understanding how to bring my existing pages into the manager and how to expose them to the users for editing. I did look into the documentation, but could not find anything dedicated to this topic.
Any help is greatly appreciated.
Thank you.
Daniel D.
It's really simple getting data from Piranha CMS into your existing application.
1. Page types
First of all, take a look at the different page types you need to create (i.e the different kind of page structures you need) and create them, either from the manager interface or by code. You'll find the docs here:
http://piranhacms.org/docs/pages/page-types
http://piranhacms.org/docs/extend/page-post-types
2. Create your pages
Next up, just create the pages you need in the manager and add the content. If you want to prohibit the users to add pages, you can always remove the "Add" buttons later, either by injecting CSS into the manager or by customizing it.
3. Adding the data to your models
Here you have two options, either you let your model inherit from the PageModel, or your just add a new property with the CMS data to your existing model.
// With inheritance
public class MyModel : Piranha.Models.PageModel {
...
}
// With the CMS data in a property
public class MyModel {
public Piranha.Models.PageModel CMSData { get; set; }
}
4. Getting the data
Now populating the data is just a calling a method. You can either access it by id or by permalink. Let's for example access it by permalink and let's say we have a page with the permalink about-us. Just add the following to your controller:
// With inheritance
public class MyController : Controller {
public ActionResult MyAction() {
// Get the CMS-data
var model = Piranha.Models.PageModel.GetByPermalink<MyModel>("about-us");
// Fill the model with your custom data
...
}
}
// With the CMS data in a property
public class MyController : Controller {
public ActionResult MyAction() {
// Create and fill your custom model
var model = new MyModel();
...
// Get the CMS-data
var model.CMSData = Piranha.Models.PageModel.GetByPermalink("about-us");
}
}
5. Configuration
Note that if you want your application to control the routing your should configure Piranha CMS to run in passive mode. You can find the docs on configuration here:
http://piranhacms.org/docs/api-reference/configuration
I hope this helps you get started!
Regards
/HÃ¥kan

Referencing my Models in DAL

Happy New Year everyone. Ok, i'm trying to create a 3 tier application and i have my references in the following order UI -> BLL -> DAL. The question is. The problem i'm having is with the Dbset. Because i have no reference to my models within my DAL, the dbset fails.
namespace MyApp.DAL
{
public class MyAppDb : DbContext
{
public MyAppDb() : base("name=MyAppDBstring")
{ }
public DbSet<SomeModel> SomeModels { get; set; }
}
}
How do i get this to work if it cant find my SomeModel class in my BLL? Any help would be greatly appreciated.
In this case you would need to add a reference to your BLL project in your DAL project. Right click References>Add Reference and then the Solution tab tick your BLL project.
From this you will then be able to put a using statement on the top of the class above something along the lines of:
using MyApp.BLL;

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

...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

Problems with EF-Agnostic design consumed by WCF service.

I am trying to set up EF to work on WCF and keeping the domain class models EF Agnostic.
The code is organized into 3 projects. (I am taking a stab a DDD - I am very new to it but am looking forward t learning more)
Project: QA - Domain Layer. Contains the DataContract models/entities.
References
QA.Data
Project: QA.Data - Data Layer. Contains the context and EDMX (code generation stragtegy = "none")
References
Entity Framework/System.Data.Entity
Project: QA.Repository - Data Access/Repository. Contains the repository classes
References
QA [Domain Layer]
QA.Data [Data Layer]
Entity Frame/System.DataEntity
My understanding is that the domain layer can reference the data layer but the data layer should never reference the domain. The problem that this presents is that my Domain Models/Classes are defined in the Domain layer but the Context which creates and returns them is in the Data layer. In order for my context to know to return a "Widget" object it would need a reference to the Domain layer which defined the "Widget"
My (failed) solution : My solution was to create interfaces for each Domain Model and place them in the data layer. The context would return ... IdbSet ... These interfaces would, in turn, be implemented by the Domain Models, therefore keeping my data layer from directly needing to reference my domain (which causes illegal circular references anyway). The domain models were originally contructed using "ADO.NET DbContext Generator w/WCF Support" T4 templates. This process resulted in the inclusion of the [KnownType(typeof(IWidgetPiece))] at the beginning of of the widget class defin ition. (A Widget has a navigation property ... ICollection ...)
The problem appears when I attempt to access the service, I get the following error
'QA.Data.IWidgetPiece' cannot be added to list of known types since
another type 'System.Object' with the same data contract name
'http://www.w3.org/2001/XMLSchema:anyType' is already present. If
there are different collections of a particular type - for example,
List and Test[], they cannot both be added as known types.
Consider specifying only one of these types for addition to the known
types list.
I can change these to the concrete implementations ... [KnownType(typeof(WidgetPiece))] ... but I continue to get this error because the navigation property they are referring to is still returning an IWidgetPiece interface type which it MUST do in order to satify the interface implementation.
I am trying to figure out how to keep things appropriately divided and still have the context returning what it should. the context returning Interfaces still doesn't "sit" right with me for this and other reasons but I cannot think of another way to do this, and even this is presenting the aforementioned issue. HELP!
Some code to hopefully clarify my previous ramblings ...
namespace QA.Data
{
public interface IWidgetPiece
{
String ID { get; set; }
}
public interface IWidget
{
String ID { get; set; }
ICollection<IWidgetPiece> Pieces;
}
public partial class WidgetEntities : DbContext
{
IDbSet<IWidget> Widgets { get; set; }
IDbSet<IWidgetPiece> WidgetPieces { get; set; }
}
}
namespace QA
{
[KnownType(typeof(IWidgetPiece))]
// [KnownType(typeof(WidgetPiece))]
[DataContract(IsReference = true)]
public partial class Widget : QA.Data.IWidget
{
[DataMember]
public String ID { get; set; }
[DataMember]
public virtual ICollection<IWidgetPiece> Pieces { get; set; }
}
[DataContract(IsReference = true)]
public partial class WidgetPiece : QA.Data.IWidgetPiece
{
[DataMember]
public string ID { get; set; }
}
}
namespace QA.Repository
{
public class WidgetRepository
{
public List<Widget> GetWidgetbyID(String sId)
{
WidgetEntities context = new WidgetEntities();
List<IWidget> objs = context.Widgets.Where(b => b.ID == "78").ToList();
List<Widget> widgetList = new List<Widget>();
foreach (var iwidget in widgetList)
widgetList((Widget)iwidget);
return widgetList;
}
}
}
Do you really want / need two separate models i.e. your data access layer model (edmx) and your "real" domain model? The whole point of an ORM framework like EF is so you can map your domain model to your database tables, using mappings between the physical (database) conceptual model.
Since EF4.1, you can construct your domain model and then in your data access layer map that to your database directly using a fluent API. You can also elect to reverse-engineer your POCO domain model from a database if you want to quickly get up an running.
It just seems a bit of unnecessary complexity to create an entire EF class model, only to then have to map it again into another class model (which will most likely be fairly close to the EF-generated one).