Creating a Subclass of IdentityUser on Indentity framework .NET MVC 6, - asp.net

I have created this class to identify the users who work on a laboratory, with the lab they work on, the class extends from User, which also extends form IdentityUser.
namespace ASP6_SinAuth.Models
{
[Table("Laboratory_workers")]
public class LaboratoryWorker : User
{
[Column("id_lab")]
public Laboratory? laboratory { get; set; }
}
}
Then I created a controller wuth a creation function like for a normal model:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,laboratory")] LaboratoryWorker worker,int laboratory)
{
if (worker.Id != null)
{
worker.laboratory = _context.Laboratory.Find(laboratory);
_context.Add(worker);
_context.SaveChanges();
return RedirectToAction("Index");
} else
{
Console.WriteLine("Error creatring the worker ");
}
return View(worker);
}
But here comes the problem, the instance of LaboratoryWorker is from a new User and i dont know how to turn a normal User instance to a LaboratoryWorker so I can save it in the DB.

Related

How to create an object once and use it several times in Controller

I want to populate an object using methods in a Controller class. So I've created the object in the Controller class and then tried to populate it using methods in the class. This does not work though, because everytime a method in a controller is called, the entire Controller class is reinitiated. So I get a brand new object everytime I try to populate the object. However, I don't know how else I can create an object and populate it. I've added the code below. The object I'm talking about is ProcessModel. Ignore the other objects I created at the top of the class.
Controller:
public class HomeController : Controller
{
ProcessModel pm = new ProcessModel();
RetrievePatterns pt = new RetrievePatterns();
RetrieveModel rm = new RetrieveModel();
public IActionResult Index()
{
FindPatterns fp = new FindPatterns(rm.pm, pt.KpiPatterns);
ViewData["KPIs"] = fp.passdata;
return View();
}
[HttpPost]
public IActionResult AddEvent([FromBody] Event data)
{
data.ID = pm.EventObjects.Count + 1;
pm.EventObjects.Add(data);
return Json(pm.EventObjects.Count);
}
[HttpPost]
public IActionResult AddProcessName(string data)
{
pm.ID = 1;
pm.Name = data;
return Json(new { title = pm.Name });
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public ActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Object:
public class ProcessModel
{
public List<Event> EventObjects = new List<Event>();
public List<Entity> EntityObjects = new List<Entity>();
public int ID { get; set; }
public string Name { get; set; }
}
You could create a Singleton class which is responsible to create only one instance from Process model;
public class ProcessModelSingleton
{
private static ProcessModel _processModel = new ProcessModel();
private ProcessModelSingleton()
{
}
public static ProcessModel Instance
{
get { return _processModel; }
}
}
Then assign it a global variable in the controller;
ProcessModel pm = ProcessModelSingleton.Instance;
EDIT
Created instance by singleton class shared by all users. So, if you want to make it user specific, using Session is the best option.
public ProcessModel pm {
get
{
if (Session["ProcessModel"] == null)
{
Session["ProcessModel"] = new ProcessModel();
}
return (ProcessModel)Session["ProcessModel"];
}
}
Keep in mind static variables are shared across all users for that web server. So data populated into a singleton class by one user's request can be read/overwritten by a different user's request. This may be what you are after, but if not you should look into Session State.
ASP.NET Session State Overview
As HTTP is stateless by nature, usage of sessions are discouraged my Microsoft for Asp.NET. By default they are only visible to the web server that creates them so if you are using a farm, you either need to look at server affinity (also called sticky sessions) with a load balancer or out of proc state (such as SQL Server or NCache).
Ideally your application design should avoid the need for sessions and carry only the necessary data between requests via cookies and/or query strings. For more complex applications however this is usually not possible and so Session State is used.

When do we need data classes?

Im using asp.net core. Here is the basic way to use model with controller.
public class BookController : Controller
{
private readonly ApplicationDbContext _context { get; set; }
public BookController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Create(Book model)
{
// adding new model
}
public IActionResult Edit(Book model)
{
// modifying the model
}
public IActionResult Delete(Book model)
{
// removing the model
}
}
My question: when shall/should I implement the code inside the controller? When shall/should I implement it in another class?
Something like this:
public interface IBook
{
int Add(Book book);
int Update(Book book);
int Remove(Book book);
}
public class BookData : IBook
{
private readonly ApplicationDbContext _context { get; set; }
BookData(ApplicationDbContext context)
{
_context = context
}
public int Add(Book model)
{
// ...
return _context.SaveChanges();
}
// other implements...
}
Then, calling it inside controller:
public IActionResult Create(Book model)
{
var bookData = new BookData(_context);
int result = bookData.Add(model);
// ...
}
For the interface, I think it may be useful for the case: I have many controllers that require same action/method names.
Example: MessageController requires 3 actions/methods at least (Create/Add, Edit/Update, Delete/Remove). It's same to NotificationController class, CommentController class...
So, the interface can be improved to:
public interface IMyService<T> where T : class
{
int Add(T model);
int Update(T model);
int Remove(T model);
}
public class MyService<T> : IMyService<T> where T : class
{
private readonly ApplicationDbContext _context { get; set; }
public MyService(ApplicationDbContext context)
{
_context = context;
}
public int Add(T model)
{
Type type = typeof(model);
if (type == typeof(Book))
{
// adding new book model
}
else if (type == typeof(Comment))
{
// adding new comment model
}
// ...
return -1;
}
// other implements...
}
Do I misunderstand something?
If I read it correctly with data classes you actually means repository (which is an abstraction over the persistence layer). You should always encapsulate persistence logic behind a class (be it via repository pattern, command/query pattern or request handler) and use it instead of directly using the context in your service classes.
That being said, you can directly inject your BookData to your controller instead of the ApplicationDbContext. One thing you should consider you lose in your current implementation is the Unit of Work pattern. Right now, every add will instantly persist the data.
This may not be what you want, so you should move the _context.SaveChanges(); outside of the Add/Remove/Update methods and call it explicitly. This allows you to insert i.e. 10 records and if one of them fails, nothing will be persisted to the database.
But if you call _context.SaveChanges(); after each insert and you get an error in the 8th (of 10) records, then 7 get persisted and 3 will be missing and you get inconsistent data.
Controller shouldn't contain any logic at all, only do short validation of the input model (ModelState.IsValid check) and if its okay, call the services which do all the logic and report the result back to the user. Only in very simple tutorials and guides logic is put into the controller action for reasons of simplicity. In real world applications you should never do that. Controllers are much harder to unit test than service classes.

How to properly MVC this task?

My task is to upload file.
class FileUploadController : Controller {
public ActionResult Index(HttpPostedFileBase postedFile) {
// When and how to validate it and return appropriate view and model data
// How to store the file in database and appropriately return view and model data
}
}
Validation requires to check if filename already exists(database access) and if file extension(database access) is supported.
So far I architected it like this:
class FileUploadController : Controller {
public ActionResult Index(HttpPostedFileBase postedFile) {
FileUploadModel model=new FileUploadModel();
model.UploadedFile = postedFile;
FileUploadService service = new FileUploadService();
bool valid = service.Validate(postedFile);
if (valid) {
FileUploadViewModel viewModel = service.Save(postedFile);
return View("some_view", viewModel);
}
else {
return View("some_view", service.ViewModel);
}
}
}
public class FileUploadModel
{
public HttpPostedFileBase UploadedFile { get; set; }
}
class FileUploadViewModel {
public ModelState ModelState;
public String Filename;
}
public class FileUploadService
{
private FileUploadViewModel viewModel = new FileUploadViewModel();
public FileUploadViewModel Save(FileUploadModel fileUploadModel)
{
// here i will just save it to the database
// and return viewModel with valid state
}
public bool Validate(FileUploadModel fileUploadModel)
{
// I do the filename, size, etc validation here together with database validation if the file exists and appropriately attach errors to viewModel.ModelState so views can render the error
}
}
As you can see my validate method populates viewModel.ModelState and my Save method returns new FileUploadViewModel. I really can't make up my mind how to design this so it can grow.
My questions are:
- If suddenly update action is added and my service serve update method, I will need to return different data as ViewModel and the validation would be different, should I create new ViewModel class and new Validation..?
- Does my validation occur at valid place?

Asp.net mvc Role based and Rule based operations

I am a bit confused about role and rule with my application. My some users will be authorized edit data on my web site. For example I will create roles like this:
EDITOR
ADMIN
VIEWER
Asp.net mvc has an attribute named Authorize. I can specify roles for controller and actions .
[Authorize]
public class GeometryController : Controller
{
[Authorize(Roles = "VIEWER")]
public ActionResult Get(string id)
{
return Content("OK.");
}
[HttpGet]
[Authorize(Roles = "ADMIN, EDITOR")]
public ActionResult Edit(string id)
{
return Content("This operation is restricted for you.");
}
}
But I have another role that some users can edit data by working area. For example
user1 can only edit Arizona data and view All zone data.
user2 can edit and delete Texas data.
I've done something close to the following in my code.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _feature;
private readonly string _permission;
public BRTAuthorizeAttribute( string feature, string permission)
{
_feature = feature;
_permission = permission;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (!base.IsAuthorized(actionContext))
{
return false;
}
if(// check access rights)
{
return true
}
return false;
}
}
Then decorate controllers with [CustomAuthorize("feature", "permission")] this should be what you need.

ASP.NET MVC 5 Common actions for more controllers

I have some controllers (and will be more) which share some actions like those:
public ActionResult DeleteConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult RestoreConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = false;
db.SaveChanges();
return RedirectToAction("Index");
}
Those action are part of SuppliersController. What this does is that when I delete or restore an object, it marks the object in the database as true for deleted field (and false when it is restored).
The same behavior is shared by many other controllers like CurrenciesController, ProductsController, etc...
In the code I showed you should see that my database entity is clearly specified (Supplier) and also the repository (Suppliers).
I want to find a way to this in a generic way. I want to create a custom controller and all other controllers that shares the same behavior will extended it. In this case ProductsController will extend my DeleteRestoreController.
How can I do this in a "generic" way?
db is a DbContext
public partial class LE: DbContext
{
public LE()
: base("name=LE")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<CategoryText> CategoryTexts { get; set; }
...
}
Categories also share the same behavior.
To go one step further
public abstract class DeleteRestoreController<T> : Controller
{
public virtual Action DeleteConfirmed(int id)
{
var dbset = db.Set<T>();
var s = dbset.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
}
then when defining your controller add the entity type
public class ProductsController : DeleteRestoreController<Supplier>
{
////blah
}
You can implement your DeleteRestoreController as an abstract class.
public abstract class DeleteRestoreController : Controller
{
private IRepository : Repository;
public DeleteRestoreController() { ... }
public DeleteRestoreController(IRepository Repository) { ... }
public virtual Action DeleteConfirmed(int id)
{
Supplier s = db.Suppliers.Find(id);
s.Deleted = true;
db.SaveChanges();
return RedirectToAction("Index");
}
}
If you need to differ from that behaviour in your ProductsController you can simply override that method.
public class ProductsController : DeleteRestoreController
{
public override void DeleteConfirmed()
{
//override the logic
}
}
You could always go one step further and implement a generic repository as well, but I've never gone beyond 6-8 controllers in my applications and didn't create one once.
EDIT I've just read in the comments, that the entities would change from Suppliers in the controllers, so implementing a base controller wouldn't make much sense, if you do not implement a generic interface as well. Robert Harvey has made a great point in stating the complexity has to go somewhere.

Resources