How do I Display a Message across projects? - c#

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.

Related

Why no developer exception page in asp.net 5 razor pages?

I have a simple asp.net 5 razor pages app which does not show developer exception page but shows this in the browser developer tools
The character encoding of the plain text document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the file needs to be declared in the transfer protocol or file needs to use a byte order mark as an encoding signature.
After many iterations and debugging it turns out there was a simple typo in the sql query and instead of showing the developer error page, it was showing blank with the aforementioned error in the browser console !
Questions -
Is this normal/expected ?
any way to turn on "more" debugging to identify such errors rather than trial and error ?
environment -
Visual studio 2019, .net 5
db access using dapper v2.0.78
configure excerpts below !
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logg)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseDeveloperExceptionPage();
//app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
...
there are no try/catch handlers, the code is rather basic
in the razor page
public IEnumerable<gEmployee> ListRows { get; private set; }
DAL mDAL;
public string Message;
public void OnGet(int dID)
{
ListRows = mDAL.GetEmployees(dID);
Message = $"Got {ListRows.Count()} Rows";
}
this is how i figured out the error when the OnGet() would get called but 2nd line with Message = ListRows.Count would not get executed !!
in GetEmployees
public List<gEmployee> GetEmployees(int dID)
{
using (var conn = new SqlConnection(cx.DefaultConnection))
{
var sql = #"SELECT * from gEmployee ";
if (dID > 0)
sql += " WHERE dID = #dID ";
var ListRows = conn.Query<gEmployee>(sql, new { dID = dID}).ToList();
return ListRows;
}
}
Usually in these cases, the first approach should be trying to reproduce the behaviour on a small/clean project so you can rule out various scenarios.
As you saw, in your case it's the
services.AddDatabaseDeveloperPageExceptionFilter()
that is causing the problem.
As Microsoft says in the documentation
This should only be enabled in the Development environment.
The developer error page will display in the event of an exception being raised in your server-side code. Based on the console message, it is likely that your SQL typo did not cause an exception to be raised. Or, if it did, you might be hiding it in a try - catch block. You haven't actually shown the relevant code so this is pure speculation. Either way, it resulted in your view page trying to render something that the browser did not understand, so the browser let you know in the console.
If you are writing SQL, it is always useful to test it in SQL Server Management Studio prior to handing it over to your code to execute.
Is this normal/expected
You're working with an advanced framework (.net) for the web development, so, debugging is not as simple as other platforms (like PHP frameworks) unless you know enough about the the project dependencies and underlying details/behaviors. In this way, you may sometimes see none relevant or ambiguous errors. In my opinion, working with high level frameworks requires a greater level of knowledge and experience. In your case, primarily double check methods and attributes to be used deliberately as they can disable/disturb the built-in Exception Handler behavior where you said instead of showing the developer error page, it was showing blank with the aforementioned error in the browser console
any way to turn on "more" debugging to identify such errors rather
than trial and error ?
You can write a HandleError method containing one of the approaches available at Handle errors in ASP.NET Core to consume where you need it like this :
private void HandleError()
{
...
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
....
}
public void OnGet(string code)
{
//do stuff
HandleError();
//return
}
Please read the entire of MS Doc page in order to find out which one well fits in your case to unravel the main problem. After clearing the issue, you no more need to use above technique.

Using a "shared" type on a WCF/MVC project with two services ("cannot convert from...")

I've two WCF services connected to my client. I want to use a User-object, retrieved from service #1, and use this as paramter for service #2. Here is my MVC-Controller TournamentController.cs code:
private readonly GCTournamentServiceClient _tournamentClient = new GCTournamentServiceClient();
public ActionResult Join(int id)
{
GCUserServiceClient userClient = new GCUserServiceClient();
// Get current user
var currentUser = userClient.GetUser(0);
if (currentUser != null)
{
// Get selected tournament
var selectedTournament = _tournamentClient.GetTournament(id);
// Check if there are available seats in the tournament
if (selectedTournament.Seats > selectedTournament.RegistredUsers.Count)
{
// Check if user exist amoung registred users
if (!selectedTournament.RegistredUsers.Contains(currentUser))
{
selectedTournament?.RegistredUsers.Add(currentUser);
}
}
}
}
The error Visual Studio prompt me with:
Argument 1: cannot convert from 'GConnect.Clients.WebClient.GCUserService.User' to 'GConnect.Clients.WebClient.GCTournamentService.User'
So the problem is currentUser, which has the type GCUserService.User. I'm unable to use this as parameter for RegistredUsers
The error itself makes perfect sense, however, I'm not quite sure how I'm suppose to convert this type (properly). Some articles states, that a "shared"-service has to be created, which holds the User-type. I just can't believe, that a solution like that, should be necessary.
I might have misunderstood some basic stuff here (working with WCF and MVC), but please enlighten me, if that's the case.
So the problem is currentUser, which has the type GCUserService.User.
I'm unable to use this as parameter for RegistredUsers
There are 2 approaches to solve this problem:
1)
Create a class library project (Visual Studio) and add the User class in that project, compile it and add its assembly's (.dll) reference to both services and the client (your MVC application). Next retrieve that user object as you are already doing it
var currentUser = userClient.GetUser(0);
GetUser will return the type of User that is defined in a separate assembly which is added as reference as suggested above. The TournamentService will also reference the same assembly and the RegistredUsers.Add(User userToadd) method will take the same User object and WCF runtime should be able to serialise/desterilise it.
2)
In your MVC client application, new up the User object that is acceptable by the TournamentService.RegistredUsers.Add method. Populate its properties from the currentUser and pass in that object as parameter to RegistredUsers.Add method.
Best Practice
Ideally, I would recommend the first approach which is more work but a better practice and that your User class is maintained centrally and code is reused.
Hope this helps!

SQL write to ASP.NET user table doesn't save

My setup:
ASP.NET 4.5 web api (on Azure) saving data to SQL db (also on Azure)
AngularJS web front end (another Azure web site)
When a user first signs up, I show them a "getting started intro". The intro is only supposed to run once - I log the timestamp of the intro launch date as a custom field in the ASP.NET user table.
Imagine my surprise when I log in (as a user would) and see the intro TWICE.
The AngularJS front end is properly sending the "intro viewed" message to the ASP.NET api, and the api responds with a success message. However, when I look at the raw data in the db, the timestamp is most definitely NOT updated. Consequently, the user will see the intro a second time (at which point the timestamp gets recorded in the db properly).
I have a crappy workaround. After the client requests an OAuth Bearer token from my server, the client then requests user information (to decide whether or not to show the tour). Waiting 100ms and then sending the "tour viewed" message back to the server masks the issue.
I've not seen ANY other issues storing data at any point. Because our db is on Azure, I can't hook up Profiler and the built in auditing doesn't give me any clues.
Is there something about requesting the token that leaves ASP.NET identity in a funny state? And it takes a brief wait before you can write to the table? Are custom fields that extend the base Identity setup prone to problems like this? Is the UserManager possibly doing something weird in its black box?
Does anyone have suggestions for how to continue debugging this problem? Or ever hear of anything like it?
Here's the relevant code that should be updating the "tour viewed" timestamp in the db:
[HttpPost, Route("UserInfo")]
public async Task<IHttpActionResult> UpdateUserInfo(UpdateBindingModel model)
{
var currentUser = UserManager.FindById(User.Identity.GetUserId());
if (model.FirstName != null)
{
currentUser.FirstName = model.FirstName;
}
if (model.LastName != null)
{
currentUser.LastName = model.LastName;
}
if (model.SetIntroViewCompleteDate)
{
currentUser.IntroViewCompleteDate = DateTime.UtcNow;
}
if (model.SetIntroViewLaunchDate)
{
currentUser.IntroViewLaunchDate = DateTime.UtcNow;
}
if (model.SetTipTourCompleteDate)
{
currentUser.TipTourCompleteDate = DateTime.UtcNow;
}
if (model.SetTipTourLaunchDate)
{
currentUser.TipTourLaunchDate = DateTime.UtcNow;
}
IdentityResult result = await UserManager.UpdateAsync(currentUser);
if (result.Succeeded)
{
var data = new UserInfoViewModel
{
FirstName = currentUser.FirstName,
LastName = currentUser.LastName,
IntroViewLaunchDate = currentUser.IntroViewLaunchDate
};
return Ok(data);
}
return InternalServerError();
}
UPDATE ********* 4/18
I've also tried to move completely away from UserManager stuff. I've tried the following modifications (pulling the user data from a table like I would access any other data), but it still behaves the same. I'm starting to think that putting custom fields on the ApplicationUser object is a bad idea...
New db retrieve and save looks like this:
ApplicationDbContext newContext = new ApplicationDbContext();
var currentUser = await (from c in newContext.Users
where c.Email == User.Identity.Name
select c).SingleOrDefaultAsync();
//update some values
await newContext.SaveChangesAsync();
Basically the problem might be with initialization of the `UserManager' and the fact that this class works on the db context so you need to persist changes to that context. Here is an example:
var userStore = new UserStore<ApplicationUser>(new MyDbContext());
var userManager = new UserManager(userStore);
That way you remember both manager and context. Then in your method you would normally call:
IdentityResult result = await userManager.UpdateAsync(currentUser);
followed by persisting this change to db context:
var dbContext = userStore.context;
dbContext.saveChanges();
Based on your comment that waiting 100ms masks the issue, I think you may have a problem with the multiple async await calls. Try running the calls synchronously and see if you still have the same issue. My guess is that the problem might go away. My experience has been that using async await can be tricky when you have calls to asynchronous methods that call other asynchronous methods. You may have code that is executing without the proper results returned.
Well, here's what I did to solve the problem. I totally de-coupled my custom user data from the built in ASP.NET identity stuff. I've now got a separate object (and therefore separate SQL table) that stores things like FirstName, LastName, LastActiveDate, etc, etc.
This has solved my problem entirely, though it has introduced another call to the database in certain situations. I've deemed it to be not a big enough performance issue to worry about. I'm left thinking that this was some sort of weird race condition involving the generation of a token for an ASP.NET identity user then quickly writing to an Azure SQL database - lord knows what it was exactly in my code that caused the problem.
If you've got a problem that's hard to solve, often the best plan is to change the problem.
Now I need to find a meta thread discussing what to do with bounty points when you've blown up the problem...

Validation, repository classes and code structure in Entity Framework

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

Kendo UI Scheduler connection to database

I'm currently developing a web app using the open source Kendo UI Scheduler with ASP.NET MVC Visual Studio 2012. But i'm experiencing some trouble while trying to connect my scheduler with a local database to store the bookings made by users of my application.
I've been looking for documentation to help me with this but I haven't been able to set this up entirely...
I followed instructions about this on: http://docs.telerik.com/kendo-ui/getting-started/using-kendo-with/aspnet-mvc/helpers/scheduler/ajax-editing
I've added the Entity Framework Data Model and necessary code in Models like TaskViewModel but the problem occurs in my Controllers.
public ActionResult Tasks_Read([DataSourceRequest]DataSourceRequest request)
{
using (var sampleDB = new SchedulerEntities())
{
IQueryable<TaskViewModel> tasks = sampleDB.Tasks.ToList().Select(task => new TaskViewModel()
{
TaskID = task.TaskID,
Title = task.Title,
//Specify the DateTimeKind to be UTC
Start = Convert.ToDateTime(task.Start),
End = Convert.ToDateTime(task.End),
Description = task.Deschription,
}).AsQueryable();
return Json(tasks.ToDataSourceResult(request));
}
}
I get an error on the DataSourceRequest: the type or namespace DataSourceRequest could not be found. But I can't find to narrow down which module I'm missing or what else I'm doing wrong...
Besides that I also get the following error System.LinqIQueryable doest not contain a definition for ToDataSourceResult. on the code:
return Json(tasks.ToDataSourceResult(request));
Anyone who can help me here or has an other/better solution to make a connection to a local database using the open source Kendo UI Scheduler?
Any help would be really appreciated!
You need to specify the allow get behavior.
return Json(tasks.ToDataSourceResult(request), JsonRequestBehavior.AllowGet));

Categories

Resources