I'm using Entity Framework and MVC3, and my problem is that I can't scaffold Controllers if the Model inherits from another Class.
Example:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class User : Person
{
public string Email { get; set; }
public string Password { get; set; }
}
public class Context : DbContext
{
public DbSet<Person> PersonSet { get; set; }
}
When I try to Add the Controller User using template Controller with read/write actions and views, using Entity Framework I get this error:
'User' is not part of the specified 'Context' class, and the 'Context' class could not be modifed to add a 'DbSet' property to it. (For example, the 'Context' class might be in a compiled assembly.)
I could add public DbSet<User> UserSet { get; set; } to the Context but I don't think it is the right aproach.
At the moment your User is not persisted type because context doesn't know that type so you cannot use scaffolding of persisted entity types to create controller for User type. If you want to store User and its properties in database add this to your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>();
}
If you don't want to persist User to database you cannot use scaffolding for controller creation.
Related
I am trying to implement view model to get the data from multiple table. However, I am getting the following error
InvalidOperationException: The entity type 'RoleManagement.Models.RolePermissionsViewModel' requires a key to be defined.
Below is my view model
public class RolePermissionsViewModel
{
public List<LMS_RolePermissions> RolePermissions { get; set; }
public List<LMS_UserPermissions> UserPermissions { get; set; }
}
Where LMS_RolePermissions and LMS_UserPermissions are two different tables in the database. Basically I want to get the data from these two tables in view model. To get the data I have written below code
RolePermissionsViewModel rolemodel = new RolePermissionsViewModel
{
RolePermissions = dbContext.RolePermissions.ToList(),
UserPermissions = dbContext.UserPermissions.ToList()
};
and DBContext class
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
public DbSet<LMS_LocLanguage> LMS_LocLanguage { get; set; }
public DbSet<LMS_Permissions> Permissions { get; set; }
public DbSet<LMS_RolePermissions> RolePermissions { get; set; }
public DbSet<LMS_UserPermissions> UserPermissions { get; set; }
public DbSet<RolePermissionsViewModel> RoleUserPermission { get; set; }
}
I do not want to Key to be defined and table should NOT be created.
How can I solve this problem ?
it is advised to place seperately your domainmodels and viewmodels. all tables in the applicationcontext are created by entity framework convention if if i use DbSet<myclass> or mention it in another class that used with Dbset. your answer should be excluding types with data annotations NotMapped and with fluent api modelbuilder.ignore<RolePermissionsViewModel>();. (of course,you will remove DbSet firstly. if i read and understand correctly, you say to your codes "please dont create this,i beg on you" after you command database to set.)
I am developing an asp.net mvc 3.0 app and using EF 4.1 for my data access layer and unit of work pattern.
Here are my models :
public class UpdateUserViewModel
{
public User User { get; set; }
}
public partial class Role
{
public int Id { get; set; }
}
public partial class User
{
public Guid Id { get; set; }
public virtual Role Role { get; set; }
}
I pass UpdateUserViewModel to a view and then post the form in the view to the following action :
public ActionResult UpdateUser(User user)
{
var userObj = unitofwork.UserRepository.GetByID(user.Id);
TryUpdateModel(userObj, "User");
userObj.Role = unitofwork.RoleRepository.GetByID(user.Role.Id);
unitofwork.UserRepository.Update(userObj);
unitofwork.Save();
}
Problem raises when I Update user role in the view (it is a dropdown list) and try to update it. it gives me the following error :
The property 'Id' is part of the object's key information and cannot be modified.
But if i dont update the user role, it works fine.
Would you help me please ?
You can call TryUpdateModel with an 'exclude' list of properties - and just exclude the ID property if that's appropriate for your scenario.
e.g.
TryUpdateModel(user, null, null, new [] { "Id"} )
If model-first, we use [MetadataType(typeof(ConceptMetadataSource))] to attach a MetadataSource file which contains all the data annotations like [HiddenInput(DisplayValue = false)] or [Display(Name = "Title")].
For example:
[MetadataType(typeof(ConceptMetadataSource))]
public partial class Concept
...
Now, I am using database-first approach since there is an existing database. This time, the entity classes are automatically created by edmx model. At the beginning of each entity class, there is lines of comment below:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
Since the code will be regenerated once we modify a table in the database, the data annotations will be wiped out each time the entity classes are regenerated.
Can anyone tell me what is the best method to annotate those entity classes? Thank you.
All you have to do is create another partial class and use metadatatype attribute. Here is the sample code
//This is generated by EDMX
namespace DataLayer
{
using System;
using System.Collections.Generic;
public partial class Customer
{
public Customer()
{
this.CustomerAddresses = new HashSet<CustomerAddress>();
this.CustomerOrders = new HashSet<CustomerOrder>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailId { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public virtual ICollection<CustomerAddress> CustomerAddresses { get; set; }
public virtual ICollection<CustomerOrder> CustomerOrders { get; set; }
}
}
Add following code manually
namespace DataLayer
{
[MetadataType(typeof(CustomerMetaData))]
public partial class Customer
{
}
public class CustomerMetaData
{
[StringLength(10, ErrorMessage = "First name must be 25 characters or less in length.")]
[Required(ErrorMessage = "First name is required.")]
public String FirstName { get; set; }
}
}
Okay, here is the answer.
The trick is, the auto-generated classes are all partial classes. The compilation process will combine all partial classes with the same name.
If we have public partial class Concept generated by DbContext, all we need to do is to create another one started with public partial class Concept. This new partial class can be created in a different folder, but we need to its namespace should be updated into the same as the auto-generated partial class.
In this newly created partial class, we can add all kinds of data-annotations such as
[Required(ErrorMesssage="This Field is required")]
Or, we can even add new properties like
FullName {get {return string.Format("{0} {1}", FirstName, LastName);}}
If the model is updated from the database again, only the auto-generated partial classes will be updated. Those newly manually added partial classes, which contain our annotations and other manipulations will remain intact.
define a view model like
public class VMConcept
{
public Concept NewConcept {get; set;}
}
[MetadataType(typeof(ConceptMetadataSource))]
public partial class Concept{}
public class ConceptMetadataSource {
[Required(ErrorMesssage="This Field is required")]
public string PropertyName {get; set;}
}
I have an abstract class
public abstract class Member
{
public string ID { get; set; }
public string Username { get; set; }
public int MemberType { get; set; }
public abstract string MemberName { get; set; }
public int Status { get; set; }
}
public class Person : Member
{
public string LastName { get; set; }
public string FirstName{ get; set; }
}
public class Business : Member
{
public string BusinessName { get; set; }
public string TaxNo { get; set; }
}
The class was mapped using fluent API,
Now, is there a way to update the "Status" property from the view(having Member model) without using or going to a subclass (Person/Business)?
I just tried it, but it says "Cannot create an abstract class.", when submitting the page.
Or there is a correct way to do this?
Thanks
Not in EF. You have to instantiate an object to work with EF, and you can't instantiate an abstract class.
You could make the class not be abstract. Or you could use a stored proc to update the field, or some direct sql.
It sounds like your problem is that your action method has an abstract type as a parameter, which the default model binder can't do anything with. If you are dead set on using the same view for two different classes, you may need to implement your own model binder to inspect in the incoming request and decide which type, Person or Business, to instantiate.
Check out this link for more information on creating a custom model binder:
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
This seems like a similar problem to the one I've answered previously here:
ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism
I'm building an ASP.NET MVC3 website with an code first database and have the following question:
Is it possible to make an instance of MyDbContext class with an additional argument set which will be used for filtering the results of calls to mydbcontext.
I want to use this for restricting the resultset to the current user that is logged in on my asp.net mvc website.
Any directions would be great!
I don't see why that should be a problem. Something like this should work:
public class Northwind : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind : Northwind
{
public IQueryable<Products> GetFilteredProducts(string userRole)
{
return Products.Where(product => product.UserRole == userRole);
}
}
Update
To make it impossible for your MyDbContext to be abused, you could put all your database code and models into a separate project/assembly. Then make your DbContext an internal class (instead of public), then create a public class (FilteredDbContext) that wraps your MyDbContext and exposes methods that allow you to only grab the data your allowed to see. Then in your main assembly (your web project), you will only be able to use FilteredDbContext.
So, for example:
internal class Northwind : DbContext // note: internal class
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind // note: does not inherit from `Northwind`
{
private readonly _dbContext = new Northwind();
public IQueryable<Products> GetProducts(string userRole)
{
return _dbContext.Products.Where(product => product.UserRole == userRole);
}
}
If Northwind and FilteredNorthwind are in a separate assembly from your web app, you can instantiate only FilteredNorthwind from your web app.
Update 2
If you use a ViewModel, then your web app can't get back to the list of all products for a category because you extract out only the properties you need (and only the properties the user is allowed to see).
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public IEnumerable<Products> GetProducts(string userRole)
{
return _dbContext.Products
.Where(product => product.UserRole == userRole)
.Select(product => new ProductViewModel
{
Id = product.Id,
Name = product.Name,
Price = product.Price
};
}
You could make a layer above and hide the generated one and create a your own DbContext which derives from the generated MyDbContext. Just a wild guess but it seems logical to me and so you can implement your own argument set and still use the generated one.
I would do this:
public interface IUserContext {
string User { get; set; }
}
public class Database : DbContext {
public IDbSet<Product> Products { get; set; }
}
public class AuthorizedDatabase {
private readonly Database _database;
private readonly IUserContext _userContext;
public AuthorizedDatabase(Database database, IUserContext userContext) {
_database = database;
_userContext = userContext;
}
private bool Authorize<TEntity>(TEntity entity) {
// Some code here to look at the entity and the _userContext and decide if it should be accessible.
}
public IQueryable<Product> Products {
get {
return _database.Products.Where(Authorize);
}
}
}
This would allow me to cleanly abstract the actual logic around the authorization (and your IUserContext interface can be as simple or complex as required to suite your exact needs.)
To ensure that the user is unable is circumvert this protection using a navigation property (Product.Category.Products, for example.) you might need to turn off lazy loading and explicitly load the required related entities.
Have a look at this post from ADO.NET Team Blog for ideas: loading related entities