Using view model in MVC 6 - asp.net-core-mvc

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

Related

How can I LINQ select from two EF DbSets and group join - but using AutoMapper

Note: These classes are related, but not part of the same Aggregate (like PurchaseOrder and OrderLine) - so I do not have a navigation property from "One" to "Many".
=== Entities ===
public class One
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Many
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid One { get; set; }
}
=== Contracts ===
public class OneWithMany
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<Many>? ManyRelatedObjects { get; set; }
}
I want to select all One objects and any related Many objects from DbSet/DbSet into OneWithMany.
To ensure I don't miss properties added in future I am using ProjectTo in AutoMapper - but I can't work out how to fit it into the equation.
Unfortunately, it seems Entity Framework does not support GroupJoin.
The solution is to do the projection and as much filtering as possible as two separate queries, and then combine them into a result in memory.
If you find EF related answers on the web related to GroupJoin make sure you check the example code to see if they are actually showing code working on arrays instead of DbSet.

Can't add controller of model that inherits from other class

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.

How to access an association in view from one POCO with several references to another

Sorry about the title; couldn't think of a better one.
Any way, I'm accessing an associated property in my view like so:
#Model.Company.CompanyName // No problems here...
The model is a viewmodel mapped to an EF POCO. The Model has several properties associated to the Company table. Only one of the properties in the model share the same name as the PK in the Company table. All the other properties reference the same table:
public class MyModelClass
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AnotherCompanyId { get; set; } // References CompanyId
public int AndAnotherCompanyId { get; set; } // References CompanyId
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public string Address { get; set; }
}
I'm obviously missing something here.
How can I get the names of the other companies in my Model?
Any help is greatly appreciated.
The model is a viewmodel mapped to an EF POCO
I think you are confusing the notion of a view model. A view model is a class that is specifically designed to meet the requirements of your view. So if in your view you need to display the company name and not the company id then your view model should directly contain a CompanyName property. Or a reference to another view model (CompanyViewModel) which contains the name directly. It is then the responsibility of your controller action to query your domain models (EF entities) and aggregate them into a single view model tat will contain all the necessary information that the view requires.
Here's how a typical view model might look like:
public class MyViewModel
{
public CompanyViewModel Company { get; set; }
public CompanyViewModel AnotherCompany { get; set; }
public CompanyViewModel AndAnotherCompany { get; set; }
}
public class CompanyViewModel
{
public string Name { get; set; }
}
Where the data comes from in this view model is not important. You could have the Company property populated from your EF stuff, the AnotherCompany property populated from a XML file and AndAnotherCompany from WCF.

Entity Framework, two Many to Many relationship to same object using Fluent API

I am trying to define two many to many relationship to same object using fluent api.
Here is the simplified model:
public class PurchaseRequisition
{
[Key, ForeignKey("Transaction")]
public int TransactionId { get; set; }
public virtual ICollection<People> RequisitionedBys { get; set; }
public virtual ICollection<People> AuthorizedSignatures { get; set; }
}
public class People
{
[Key]
public string Id{ get; set; }
public string FullName { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForRequisitionedBys { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForAuthorizedSignatures { get; set; }
}
Here is the fluent api code:
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.RequisitionedBys)
.WithMany(b => b.PurchaseRequisitionsForRequisitionedBys)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("RequisitionedById");
x.ToTable("PurchaseRequisitionRequisitionedBy");
});
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.AuthorizedSignatures)
.WithMany(b =>b.PurchaseRequisitionsForAuthorizedSignatures)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("AuthorizedSignatureId");
x.ToTable("PurchaseRequisitionAuthorizedSignature");
});
What I want is to generate two separate linking tables, but what EF generates is two foreign key columns to PurchaseRequisition in People table and 1 foreign key column to People in PurchaseRequisition field.
Can anyone tell me what might be wrong?
The problem was fixed.
I mistakenly thought that my database initializer code would drop and recreate the database since I made changes to the model classes and my Initializer class extended DropCreateDatabaseIfModelChanges.
As Slauma suggested, fluent api code was not being reached even though the model has been changed. I was setting the initializer using SetInitializer() method and this code only ran when I used a context instance for the first time to access the DB.

Code first DbContext with current user filter

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

Resources