Validation, repository classes and code structure in Entity Framework - c#

I already asked a similar question to this but i can't even look the code i write, it looks horrible. There is something wrong.
I'm trying to create a simple web application with Visual Studio, ASP.Net Web Pages and Entity Framework. People are not familiar with Web Pages, it's basically a development environment like classic ASP and PHP.
I have two tables, one is workers and other one is overhours. I created models for both, they are related so every overhour record has one worker.
Basically i'm using this code:
if (IsPost)
{
try
{
Worker curWorker = new Worker();
try
{
curWorker = m.Workers.Find(decimal.Parse(Request.Form["WorkerId"]));
}
catch (Exception)
{
errors += "Please select a worker.";
}
try
{
overhour.OverhourAmount = decimal.Parse(Request.Form["OverhourAmount"]);
if (overhour.OverhourAmount == 0)
{
throw new Exception();
}
}
catch (Exception)
{
errors += "Hour field should be numerical and non-zero.";
}
overhour.Worker = curWorker;
overhour.OverhourDate = DateTime.Today;
curWorker.Overhours.Add(overhour);
if (errors != "")
{
throw new WrongValueException(errors);
}
m.SaveChanges();
Response.Redirect(Page.ParentPage);
}
catch (DbEntityValidationException ex)
{
errors = kStatic.getValidationErrors(ex.EntityValidationErrors, "<br />");
}
catch (WrongValueException ex)
{
errors = ex.Message.ToString();
}
catch (Exception ex)
{
errors = "Critical error, technical details: " + ex.Message;
}
}
Form has a combobox with all workers named WorkerId. It works but i have a few problems.
I hate validation method. I need to validate if user has selected a valid worker from the combobox because it has an option named "Select a worker" and it's value is empty string, so i need to check if it's numerical. I can include a [Regex..] code to my model class but it doesn't matter because there will be an error when i try to assign string to decimal field (decimal WorkerId). I can catch the exception but it will be likely a mismatch exception. I need more details.
Same thing with OverhourAmount, it should be numerical and non-zero too.
I don't like putting this code into the page code itself. I can create a repository class with methods like r.addOverhour but people say it's unnecesary. Is it unnecesary for MVC or if you're using Entity Framework, you shouldn't use an extra repository class.
I want to check the database for some validation before saving changes. For example, an user (user who has username and password, not worker) shouldn't be able to create a record about a worker if they are not in same building. For example, user A works in building X, and worker H works in building Y, user A shouldn't be able to create any data related to worker H. So i need to check if they work in same building before adding the record. I have BranchId field in both user and worker tables, i can check that easily but where?
Basically i don't know how to structure my code. I think i'm missing something big here because everybody validates their data and filter their inputs.
Thanks

Decorate your dtos with validation. Modelbinding will verify your data automatically and if failed will return the property that fails validation and the reason which you can customize. You business rules need to happen in a logic layer which will basically operate on your data models and return dtos. Your logic layer will accept dtos and this will provide separation from the data layer from the web API/mvc

Related

How do I Display a Message across projects?

We have one Visual Studio 2019 ASP.NET solution with 22 projects, one of which is called BusinessLogic and one which is called Web.
In BusinessLogic, we have code to log errors, and it used by all projects in the solution.
public static Guid? StartDebugLog(String module, String method, String message)
{
if (ApplicationSettings.IsDebugLogActive)
{
using (BPIContext context = SessionManager.CreateNewBPIContext)
{
DebugLog debugLogObject = new DebugLog();
debugLogObject.ID = Guid.NewGuid();
debugLogObject.Module = module;
debugLogObject.Method = method;
debugLogObject.StartTime = DateTime.Now;
if (message == null)
debugLogObject.Message = "(NULL)";
else
debugLogObject.Message = message;
context.AddToDebugLog(debugLogObject);
context.SaveChanges();
return debugLogObject.ID;
}
}
return null;
}
The problem is that this routine is hiding important errors from our customers. Yes, they can go into the application logs, but simply starting the application creates over 100 entries in the application log. Finding errors would be a daunting task to ask of our customers.
I need a way for this routine to be able to display a basic message so that customers do not continue on, thinking everything is OK. Something like alert("An error has occurred."), but the *BusinessLogic project does not have access to the Web project.
Is there a recommended way to create a CALLBACK or something? Most of my background is in Windows Forms where this would be simple. Something like this:
...
context.AddToDebugLog(debugLogObject);
context.SaveChanges();
if (WebCallback != null)
{
WebCallback(debugLogObject);
}
return debugLogObject.ID;
...
I'm not even sure if this is possible in a Web environment. If it is not possible, that's an answer too.
For a "Part 2", some of our newer modules are displayed by dropping them into an iFrame. Is there a way to create something that will allow information from the iFrame to pass to the parent? Currently, all messages that are sent in the iFrame appear to get lost.

Update entity framework entity with multiple relationships to other entities

I am using web api with entity framework core. I have a Worker entity with relationships to Company (via CompanyId), Status (via StatusId) and Position (via PositionId). Using a request through the API I want to be able to update a worker.
I want to pass a request of WorkerId and PositionId only. In the data layer I check to find the PositiionId exists from the request, if not the return back to the controller with message of Position not found. Below is a sample of the code without the Company and Status checks.
Public void Update(WorkerEntity worker)
{
var workerRecord = _context.WorkerEntity.SingleOrDefault(w => w.Id == worker.Id);
if (workerRecord == null)
{
Log.Logger("Cannot find the worker with Id " + worker.Id);
return;
}
var positionRecord = _context.PositionEntity.SingleOrDefault(w => w.Id == worker.PositionId);
if (positionRecord == null)
{
Log.Logger("Cannot find the position with Id " + worker.PositionId);
return;
}
workerRecord.Position = positionRecord;
_context.SaveChanges();
}
I have also tried a simple approach using .Single to catch an error if the record does exist but the catch only output the generic "Sequence contains no elements" which isn't helpful in knowing with entity failed.
Is there an easier way to check the relationship Ids the user has added in the request are not bogus without having lines and code checking the Id exists? This table may have 3 relationships but I have another table that has 5 which would have 5 if null checks.
To get a meaningful message you would have to check each field manually. It can be simplified little bit using Any() method.
if (!context.PositionEntity.Any(w => w.Id == worker.PositionId)
{
Log.Logger("Cannot find the position with Id " + worker.PositionId);
return;
}
From the EF exception you wont be able to get a descriptive error message.
But you do you really want to do this? I assume you have these lookup values (Positions, statuses etc) as dropdowns in front-end where you load them with values from your database. So in the normal scenario an incorrect value cannot be selected and send to the API. However somehow if it does, you have the EF core default error to avoid screwing up things.
Cheers,

How do I secure my web service apart from checking session variable for NULL?

I have written a webservice that basically inserts data into the central database and is expected to be consumed by multiple clients from multiple systems.
It works but the problem is that it can be accessed by anyone hence making it vulnerable to be used by anybody i.e. anybody can spam by entering data or anything.
One way is to check for the Session variable but how would I know the name of the session variable of the client consuming the system or may be he's not authenticating that way?
So what should I do to make it secure?
[WebMethod(EnableSession= true)]
public int InsertEngineeringData(string FunctionalLocation, string EqptType, string WINFileNo, string ComponentTagNo)
{
try
{
if (Session["User"] != null)
{
}
int EngineeringDataID = 0;
EngineeringDataDAL EngineeringDataDAL = new Vail_PlantWebApi.EngineeringDataDAL();
EngineeringDataID = EngineeringDataDAL.InsertEngineeringData(FunctionalLocation, EqptType, WINFileNo, ComponentTagNo);
return EngineeringDataID;
}
catch (Exception ex)
{
throw ex;
}
}
If it is an asmx webservice, then use the link Crocoder posted or another quick way if it works is you can try the [Authorize] attribute although I'm not sure if that will work with an inline webmethod you're using, I've only seen it used in WebAPI. Authorize attribute in ASP.NET MVC
A more robust way that would definitely work is you add a column to the Users table called 'CurrentSessionID' and another one that says 'LastLoginDateStamp' For each login request if you have a valid user you update their session there and a datestamp. Then when the user hits the api, you compare the session and make sure it hasn't exceeded what you decide is a valid threshold for the last login, maybe 24 hours for example.
There would be a lot more work to do after that, but that's the basic idea.

Try catch does not work in Azure server

In the config of my application I deleted the customErrors to make a test of an error that is difficult to replicate.
And where the error should be happening I put a try:
if (ModelState.IsValid)
{
db.TarefaHoras.Add(tarefaHora);
try
{
db.SaveChanges();
}
catch (DbUpdateException ex)
{
ErroDetalhe erro = new ErroDetalhe();
erro.Data = tarefaHora.Data;
erro.UsuarioId = tarefaHora.ApplicationUserId;
erro.JSON = JsonConvert.SerializeObject(tarefaHora);
erro.Tipo = "TarefaHora";
erro.Controller = "TarefaHoras";
erro.Action = "Create Post";
erro.Exception = ex.GetType().FullName;
db.ErroDetalhes.Add(erro);
db.SaveChanges();
return RedirectToAction("ErroNaAtualizacaoDaBase", "Erros", new { id = erro.ID });
}
return RedirectToAction("Index", "Home");
}
return View(tarefaHora);
}
This error only happens at runtime and only on Azure server, so my attempt was to get a better description of the error and for this I created a table where the information is deposited.
But....
The problem is that the View error that I'm getting is not the View "ErrorInputUpdate" that I created and that is redirected in Catch.
I am getting the default AspNET error view.
So I went into Shared Views and deleted the default page and removed customErrors from web.config.
And even then Azure still brings the default error view.
Anyone have any idea what might be happening?
So calling db.SaveChanges(); generates an exception. That means that the context db is invalid and it should be resolved or discarded.
However, what you are doing is creating an error entry and add using the same db context which is still in a faulty state. So the call to db.SaveChanges(); in your catch block will still generate an exception, but this time it won't be catched.
The solution is either to write the error to something else than the database, like a file or whatever or to use another EF context instance. Other options are to removed or correct the invalid entries before calling db.SaveChanges(); again or do not store the error in the database but include all the details in the redirect view.

Insert-update-delete web GUI

I find myself creating Add/Edit/Delete/List GUI's so often that I'm sick and tired of it.
There must be some free package that solves this, right?
What I would like is something like this:
{
MyApplicationUser user = MyApplication.GetUserByID(1234);
EditForm form = new EditForm("Title: Edit User"); //this is the magic object
form.addFieldsFromObject(user);
}
function onFormSubmit(eventArgs e){
MyApplicationUser user = form.GetSubmittedData();
MyApplication.SaveUser(user);
}
AddFieldsFromObject would automatically create a html form with fields mathing the datatype of the public properties of the object I feed it with.
There are a number of frameworks that try to solve this problem. ASP.NET Dynamic Data may be a good place to start. It uses a template-based system to provide basic CRUD (Create, Retrieve, Update, Delete) user interfaces with very minimal custom code.
ASP.NET MVC also does a pretty good job with its editor models:
// View code
#using(Html.BeginForm(...)) {
#Html.EditorForModel()
}
// Action code
public ActionResult ShowForm(int userId)
{
var model = // get model from user ID;
return View(model);
}
public ActionResult SaveForm(Model model)
{
if(ModelState.IsValid)
{
// Save model
}
}
LightSwitch tries to solve this same problem by auto-generating basic scaffolding code for you to produce an experience similar to Microsoft Access. But since it's using actual C# code, you can alter the code to provide more functionality if you find that your needs have grown beyond the original scope of the project.

Categories

Resources