MVC Filter Database with IIdentity - asp.net-mvc

In my MVC Site a Teacher login and should view his classes and all books used in his classes.
The teacher should not be able to see someone else class or book.
Here's my model
public class Teacher
{
public int Id { get; set; }
public string Name {get; set;}
}
public class Books
{
public int Id {get; set;}
public string Title {get; set;}
public int CourseId {get; set;}
}
public class Course
{
public int Id {get; set;}
public int Name {get; set;}
public int TeacherId {get; set;}
}
I use a RepositoryPattern with UnitOfWork.
In order to get all classes tobe showned I should put in my Controller this kind of line :
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
Things will get worst with the BookControler since I need to check all classes of the currentTeacher:
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
var books = bookRepository.All.Where(x => classes.Contains(y => y.Id == x.CourseId)
It seems to me that this kind of approach may lead to some functional bug.
what I would like to do is that when a teacher login my repositories auto-filter in order to keep only datas concerning the currentTeacher, therefore in my controller I would have :
var classes = classRepository.All; // Already filtered on currentTeacher
or
var books = bookRepository.All; // Already filtered on currentTeacher
Is this possible? And how?

Create a new All(IPrincipal user) method in which you pass in the current authenticated user. This All method will lookup the Identity.Name value in your object context's Teacher collection to get the ID which is then returned.
i.e. .Where(x => x.TeacherId == currentTeacher.Id) is the return value from this new All method.

Related

How to set additional [Not Mapped] properties querying the database with a property in EF Core 6 resultset class

When I query a table in my database using EF Core 6, i get a IEnumberable or a List of items, like this
public class SqlService
{
private IDbContextFactory<WebContext> CtxFactory { get; set; }
public SqlService(IDbContextFactory<WebContext> dbContextFactory)
{
CtxFactory = dbContextFactory;
}
public List<Test> WithoutMail()
{
using var ctx = CtxFactory.CreateDbContext();
return ctx.Test.Where(p => p.EMail == null).ToList();
}
}
Now my results represent instances of my class "Test" like this:
private class Test
{
public string EMail {get; set;}
public string SomeValue {get; set;}
... more properties
}
What I want is a (not mapped) property, querying the dababase with a value from one of the proerties like:
private class Test
{
public string EMail {get; set;}
public string SomeValue {get; set;}
... more properties
[NotMapped]
public string ValueFromQuery => [CTX].SomeOtherTable.Where(p => p.Value == SomeValue);
}
But of cause, I have no context here and no database service. Is this possbile? Ist this the wrong way? Should I implement another method in SQLService to handle this task?
It would be nice to "extend" the class this way with some additional proerties that come from the db depending on a value in the table.

MVC varing relations between tables

I'm new to MVC and am used to using Forms. My question is suppose I have basic models setup for four tables with the following fields;
Branch
------
Branch_Nu
Branch_Address
Orders
-----
Order_Nu
Branch_Nu
Product_Nu
Customer_Nu
totcost
Products
---------
Product_Nu
Product
Price
Customer
----------
Customer_Nu
Name
Address
City
St
Zip
I'm interested in the following scenarios,
I want to see all orders for a branch;
Branch->orders
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
I want to see all orders for a customer regardless of branch they
purchased from;
customer->orders->branch
I want to see all branches that sold a particular product;
products->orders->Branch
I want to see which customers bought a particular product;
products->orders->customer
Question is can I use different controllers for the different scenarios using the same basic models that is submitted to different controller methods, or do I need different models for the different scenarios which is then submitted to different controller methods?
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
your entity framework models represent the relationships between your classes. In a code first approach, your models will dictate the design of your database. Take the following three classes for example:
public class Cat
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myCat.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class Dog
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public bool IsFluffy {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myDog.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class AnimalGroup
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Cat> Cats {get; set;}
public virtual ICollection<Dog> Dogs {get; set;
}
this represents two one-to-many relationships. A AnimalGroup can contain multiple Cats and an AnimalGroup can contain many Dogs. You can write queries to do CRUD operations. A very simplistic example is the following:
//create and save an animal group
AnimalGroup group = new AnimalGroup();
group.Name = "my animal group";
_dbContext.AnimalGroups.Add(group);
_dbContext.SaveChanges();
//create and save a cat associated with the animal group
Cat myCat = new Cat();
cat.Name = "kitty";
cat.AnimalGroupId = group.Id;
_dbContext.Cats.Add(myCat);
_dbContext.SaveChanges();
Question is can I use different controllers for the different scenarios using the same basic models
Yes
do I need different models for the different scenarios which is then submitted to different controller methods
You don't need them, but generally speaking using a model for a different logical function tends (not always) to break the Single Responsibility Principle.
In your instance it appears you are viewing the same data by different views (not logic). An order is an order, for viewing purposes anywhere on your site, generally I use the same model. Placing an order (different logic function) I would most likely have a new model.
Considering you are viewing the same data this is a classic example of the best use of MVC Templates.
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
I would probably design it like:
I want to see all orders for a branch;
Branch->orders
public class BranchController() { public ActionResult Orders() {}}
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
public class BranchController()
{
public ActionResult Orders() {}
public ActionResult CustomerOrders() {}
public ActionResult ProductOrders() {}
}
// ETC
A model example:
public class OrderVM
{
//I would get rid of hungarian notation
// https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation
public int OrderId { get; set; }
public int BranchId { get; set; }
public int ProductId { get; set; }
public int CustomerId { get; set; }
}
Then store how the html is rendered in /views/shared/templates/display/OrderVM.cshtml so it can be used throughout the application, allowing overrides per controller/area as well.

Usage of entities and DTOs when performing read and create/update on a database

What is the proper usage of entities and DTOs when performing database actions? My mindset is that it seems best to use DTOs when reading from a database and entities when creating/updating to a database. As a small example let's consider teh following:
Book Class
public class Book/Entity
{
public int Id {get; set;}
public string Title {get; set;}
public int AuthorId {get; set;}
public Author Author {get; set;}
}
Author Class/Entity
public class Author
{
public int Id {get; set;}
public string Name {get; set;}
public int BookId {get; set;}
public Book Book {get; set;}
}
BookAuthorDto Class
public class BookAuthorDto
{
public string Title {get; set;}
public string Name {get; set;}
}
Now, let's say we have a WebApi Book controller.
public class BookController : ApiController
{
public IHttpActionResult GetBook(int id)
{
var BADto = context.Book.Where(book => book.ID == id)
.Select(book => new BookAuthorDto
{
Title = book.Title,
Name = book.Author.Name
});
return Ok<BookAuthorDto>(BADto);
}
public IHttpActionResult PostBookEntity(Book book)
{
// Code for saving book to DB
}
public IHttpActionResult PostBookDto(BookAuthorDto BADto)
{
// Code for creating entities from DTO
// Code for saving the created entities to Database
}
}
Which method is considered more "proper" the PostBookEntity method, or the PostBookDto Method?
Actually it is a good idea to separate queries from data modifications (insert, update, delete) - this is called Command Query Responsibility Segregation pattern (CQRS).
Here are some great posts from experts
An introduction to CQRS by M.Fowler
Some good reasoning on why Entities + Dto's are better than just using Entities for all cases
Entities pretty much are DTOs. Use the entity for all database access, and use view models in your actions.

Nested query/Navigation Property collection

Assume the following models: (example taken from Breeze DocCode)
public class Customer {
public Guid CustomerID { get; internal set; }
public ICollection<Order> Orders { get; set; }
}
public class SomeDetail{
public string name{ get; set; }
}
public class Order {
public int OrderID {get; set;}
public Guid? CustomerID {get; set;}
public SomeDetail detail {get; set;}
}
Nested queries against single Navigation Properties are clear to me. How could this be done if the Navigation Property is a collection? Something like this:
var query = EntityQuery.from("Customers")
.where("Orders.detail.name", "==", someName);
As "Text":
Select all Customers where the name of the detail of any order this customer has equals someCondition?
I am running into errors here because
.where("Orders.detail.name, "=", someCondition)
is not possible due to the collection.
Is there a short way to check for this conditions without building up a number off collections and filtering per hand?
Any help much appreciated here.
As of Breeze 1.4.6, we have added support for two new query operators: "any" and "all"
This means that your query can now look something like this.
var query = EntityQuery.from("Customers")
.where("Orders", "any", "detail.name", "==", someName);
See: http://www.breezejs.com/documentation/query-examples

How to select just some fields from a table in EF

I have a table with 9 columns in database and I want to be able to load only some fields of it if I need.
How can I do this with Entity Framework 4 please?
e.g. My table has these fields:
ID, FirstName, LastName, FotherName, BirthDate, Mobile, Email
and I want to be able to fetch just these columns:
ID, FirstName, LastName
My project is an ASP.NET MVC 3 application, with SQLServer 2008 Express and EF 4.1.
Assume you have a table with this model:
public class User{
public int ID {get; set;}
public string NickName {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string FotherName {get; set;}
public DateTime BirthDate {get; set;}
public string Mobile {get; set;}
public string Email {get; set;}
public string Password {get; set;}
}
Now, you want fetch just ID, FirstName, LastName, and FotherName. You can do it in 2 way; The first way is fetch them as an anonymous object, look:
var user = entityContext.Users.Where(u => u.ID == id)
.Select(u => new {
ID = u.ID,
FirstName = u.FirstName,
LastName = u.LastName,
FotherName = u.FotherName
}).Single();
Now, your return-value-type is anonymous, you can work with it such as:
var i = user.ID;
// or
var s = user.FirstName;
In another way (for example when you want to pass the object as an Model to a View), you can define a new class, (i.e. UserViewModel), and when you select the object, select it as a UserViewModel. look:
public class UserViewModel{
public int ID {get; set;}
public string NickName {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string FotherName {get; set;}
}
and in query, take this:
var user = entityContext.Users.Where(u => u.ID == id)
.Select(u => new UserViewModel {
ID = u.ID,
FirstName = u.FirstName,
LastName = u.LastName,
FotherName = u.FotherName
}).Single();
Look that just ONE difference is between them, in labda expression, instead of u => new {}
we are using u => new UserViewModel{}. Good luck.
There can be many ways to do this job, but using Automapper NuGet package is the most simple one I have experienced.
First: Install Autmapper NuGet package for your project from NuGet package explorer.
Second: Make a simple ViewModel, which contains only required attributes:
public class UserViewModel {
public int ID {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
Third: Initialize your your mapper only for once in app_start class like:
namespace SampleProject.App_Start {
public class AutoMapperConfig {
public static void Initializer() {
AutoMapper.Mapper.Initialize(cfg => {
cfg.CreateMap<User, UserViewModel>()
});
}
}
}
Fourth: Add it's entry in Global.asax.cs:
namespace SampleProject
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// AutoMapper Initializer
App_Start.AutoMapperConfig.Initializer();
}
}
}
Fifth: Use it in your controller where you want like this:
namespace SampleProject.Controllers
{
public class UsersController : Controller
{
private DataContext db = new DataContext();
public ActionResult Index()(
var model = AutoMapper.Mapper.Map<List<UserViewModel>>(db.User.ToList());
return View(model);
}
}
}
Last: You can create as many maps as you want between your Models and ViewModels by initializing them once in the app_start's AutoMapperConfig class and use them where you want with a simple line of code.
Also you can find a lot of help about Automapper if you search about it. Its main website is here.
Important:
I am a developer of ASP.NET MVC 5. Automapper works fine for me every time. I cannot check it on MVC 3 or older than MVC 5.

Resources