How to add Health Checks to Swagger - c#

after looking through many articles and not finding a clear answer, I would like to start one more time a topic about adding Health Checks to the swagger in ASP .Net Core.
Firstly, I would like to ask you if it is good idea to do that and how to do it in the easiest way.
Thanks in advance for all answers.

First question, Why do we need Health Check?
When we create Health Checks, we can create very granular, specific checks for certain services, which helps us greatly when diagnosing issues with our application infrastructure, as we can easily see which service/dependency is performing poorly. Our application may still be up and running, but in a degraded state that we can’t easily see by simply using the application, so having Health Checks in place give us a better understanding of what a healthy state of our application looks like.
Instead of relying on our users reporting an issue with the application, we can monitor our application health constantly and be proactive in understanding where our application isn’t functioning correctly and make adjustments as needed.
Here is simple demo about database Health check
First, Write a controller and Inject HealthCheckService in it.
[Route("[controller]")]
[ApiController]
[AllowAnonymous]
public class HealthController : ControllerBase
{
private readonly HealthCheckService healthCheckService;
public HealthController(HealthCheckService healthCheckService)
{
this.healthCheckService = healthCheckService;
}
[HttpGet]
public async Task<ActionResult> Get()
{
HealthReport report = await this.healthCheckService.CheckHealthAsync();
var result = new
{
status = report.Status.ToString(),
errors = report.Entries.Select(e => new { name = e.Key, status = e.Value.Status.ToString(), description = e.Value.Description.ToString() })
};
return report.Status == HealthStatus.Healthy ? this.Ok(result) : this.StatusCode((int)HttpStatusCode.ServiceUnavailable, result);
}
}
Then, In Program.cs(.Net 6), Configure the health check to test whether the query function of the database is normal
//.....
string connectionString = builder.Configuration.GetConnectionString("default");
builder.Services.AddHealthChecks().AddCheck("sql", () =>
{
string sqlHealthCheckDescription = "Tests that we can connect and select from the database.";
string sqlHealthCheckUnHealthDescription = "There is something wrong in database.";
using (SqlConnection connection = new SqlConnection(connectionString))
{
try
{
connection.Open();
//You can specify the table to test or test other function in database
SqlCommand command = new SqlCommand("SELECT TOP(1) id from dbo.students", connection);
command.ExecuteNonQuery();
}
catch (Exception ex)
{
//Log.Error(ex, "Exception in sql health check");
return HealthCheckResult.Unhealthy(sqlHealthCheckUnHealthDescription );
}
}
return HealthCheckResult.Healthy(sqlHealthCheckDescription);
});
//......
Result:
Swagger will expose this health check endpoint
When the query function works fine in database,It will return 200
When there is something wrong in database, It will return 503

Related

How to create unit test for testing data insertion methods with Entity Framework?

I'm trying to create unit tests for methods that insert data into a SQL Server database in ASP.NET MVC. I've got several ActionResult methods that write data into my SQL Server database. One of these methods is this the following:
[HttpPost]
public ActionResult AddApi(ApiRedirect model)
{
try
{
List<ApiRedirect> list = dbProducts.ApiRedirects.ToList();
int companyID = dbProducts.Companies.Where(x => x.CompanyName == model.Company.CompanyName).FirstOrDefault().CompanyID;
int mappingID = dbProducts.MappingNames.Where(x => x.Name == model.MappingName.Name).FirstOrDefault().MappingID;
ApiRedirect api = new ApiRedirect();
api.ApiName = model.ApiName;
api.CompanyID = companyID;
api.ApiURL2 = model.ApiURL2;
api.MappingID = mappingID;
api.ResponseType = model.ResponseType;
dbProducts.ApiRedirects.Add(api);
dbProducts.SaveChanges();
return View ();
}
catch (Exception ex){
throw ex;
}
}
However when I try to this in my test project like this:
[TestClass]
public class ApiRedirectTests
{
[TestMethod]
public void AddApiRedirect()
{
//Arrange
var controller = new ApiBrokerController();
ApiRedirect model = new ApiRedirect();
model.ApiName = "UnitTest";
model.CompanyID = 1;
model.ApiURL2 = "www.UnitTest.com/API";
model.MappingID = 1;
model.ResponseType = "json";
//Act
controller.AddApi(model);
}
}
I'm getting the following error:
I would like some test that writes code, checks if code is inserted in database and delete afterwards. What is the best way to do that?
UPDATE
I've added MVC reference to my project and now I get the following error:
Thanks in advance!
what you are talking about is an integration test not a unit test.
you will have:
a call which creates some data
another which does the actual test / asserts against your criteria
a final one which deletes the test data created in step one.
You have a number of ways of doing this, one in code, you write code which talks to endpoints only, you don't need to instantiate a controller at all.
What you must ensure you have is a way to create / delete data in an API kind of way, so you need endpoints which do this.
If you don't want to write code, you can also use something like Postman which can quite nicely orchestrate all this. This gives you the certainty that everything works, from the endpoints which clients will use, all the way down to database.
Error number 1
To fix your error, add the System.web.mvc framework to your unit test
PM> Install-Package Microsoft.AspNet.Mvc – gh9 20 mins ago
Error Number 2
You now need to upgrade the System.web version to 5.2 from 4.0
Try adding this Nuget Package

Avoid fast post on webapi c#

I have problem in when user post the data. Some times the post run so fast and this make problem in my website.
The user want to register a form about 100$ and have 120$ balance.
When the post (save) button pressed sometimes two post come to server very fast like:
2018-01-31 19:34:43.660 Register Form 5760$
2018-01-31 19:34:43.663 Register Form 5760$
Therefore my client balance become negative.
I use If in my code to check balance but the code run many fast and I think both if happen together and I missed them.
Therefore I made Lock Controll class to avoid concurrency per user but not work well.
I made global Action Filter to control the users this is my code:
public void OnActionExecuting(ActionExecutingContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
bool jobDone = false;
int delay = 0;
int counter = 0;
do
{
delay = LockControllers.IsRequested(controller.User.Identity.Name);
if (delay == 0)
{
LockControllers.AddUser(controller.User.Identity.Name);
jobDone = true;
}
else
{
counter++;
System.Threading.Thread.Sleep(delay);
}
if (counter >= 10000)
{
context.HttpContext.Response.StatusCode = 400;
jobDone = true;
context.Result = new ContentResult()
{
Content = "Attack Detected"
};
}
} while (!jobDone);
}
}
catch (System.Exception)
{
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
LockControllers.RemoveUser(controller.User.Identity.Name);
}
}
catch (System.Exception)
{
}
}
I made list static list of user and sleep their thread until previous task happen.
Is there any better way to manage this problem?
So the original question has been edited so this answer is invalid.
so the issue isn't that the code runs too fast. Fast is always good :) The issue is that the account is going into negative funds. If the client decides to post a form twice that is the clients fault. It maybe that you only want the client to pay only once which is an other problem.
So for the first problem, I would recommend a using transactions (https://en.wikipedia.org/wiki/Database_transaction) to lock your table. Which means that the add update/add a change (or set of changes) and you force other calls to that table to wait until those operations have been done. You can always begin your transaction and check that the account has the correct amount of funds.
If the case is that they are only ever meant to pay once then.. then have a separate table that records if the user has payed (again within a transaction), before processing the update/add.
http://www.entityframeworktutorial.net/entityframework6/transaction-in-entity-framework.aspx
(Edit: fixing link)
You have a few options here
You implement ETag functionality in your app which you can use for optimistic concurrency. This works well, when you are working with records, i.e. you have a database with a data record, return that to the user and then the user changes it.
You could add an required field with a guid to your view model which you pass to your app and add it to in memory cache and check it on each request.
public class RegisterViewModel
{
[Required]
public Guid Id { get; set; }
/* other properties here */
...
}
and then use IMemoryCache or IDistributedMemoryCache (see ASP.NET Core Docs) to put this Id into the memory cache and validate it on request
public Task<IActioNResult> Register(RegisterViewModel register)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);
var userId = ...; /* get userId */
if(_cache.TryGetValue($"Registration-{userId}", register.Id))
{
return BadRequest(new { ErrorMessage = "Command already recieved by this user" });
}
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for 5 minutes, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
// when we're here, the command wasn't executed before, so we save the key in the cache
_cache.Set($"Registration-{userId}", register.Id, cacheEntryOptions );
// call your service here to process it
registrationService.Register(...);
}
When the second request arrives, the value will already be in the (distributed) memory cache and the operation will fail.
If the caller do not sets the Id, validation will fail.
Of course all that Jonathan Hickey listed in his answer below applies to, you should always validate that there is enough balance and use EF-Cores optimistic or pessimistic concurrency

Reduce number of query in ASP.NET controller for polling data

I'm trying to make a request every 10 seconds to a webApi controller in ASP.NET. The controller has to check in the database (SQL Server) if there are new data available (Using enitity framework). Here is the controller side
public class RemoteCommandController : ApiController
{
public Command Get(String id)
{
Command command = null;
ProxyCommand proxycommand = null;
Device deviceSearch;
using (var systemDB = new DB())
{
deviceSearch = systemDB.Devices.Include("CommandList").Where(d => d.Name.Equals(id)).SingleOrDefault();
command = deviceSearch.CommandList.LastOrDefault();
}
if (command.IsExecuted == false)
{
proxycommand = ConvertingObjects.FromCommandToProxy(command);
}
return proxycommand;
}
}
I want to avoid to query the DB everytime I call this controller. Is there a way to improve this controller in order to reduce the number of querys?
I was thinking of using Session, but I don't think is a good idea...
Just use output caching. Add this attribute to your controller:
[OutputCache(Duration=10, VaryByParam="none")]
public class RemoteCommandController : ApiController
{
//etc.....
Increase or decrease the duration as needed. If the command list never changes, you can set the duration absurdly high and the controller will only get called once.

Best Practices for SQL Statements/Connections in Get() Request

For simple lookups, I need to perform some SQL statements on a DB2 machine. I'm not able to use an ORM at the moment. I have a working example through this code, however I'm wondering if it can be optimized more as this would essentially create a connection on each request. And that just seems like bad programming.
Is there a way I can optimize this Get() request to leave a connection open? Nesting using statements seems dirty, as well. How should I handle the fact that Get() really wants to return an object of User no matter what; even in error? Can I put this connection in the start of the program so that I can use it over and over again? What are some of the best practices for this?
public class UsersController : ApiController
{
String constr = WebConfigurationManager.ConnectionStrings["DB2Connection"].ConnectionString;
public User Get([FromUri] User cst)
{
if (cst == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
else
{
using (OdbcConnection DB2Conn = new OdbcConnection(constr))
{
DB2Conn.Open();
using (OdbcCommand com = new OdbcCommand(
// Generic SQL Statement
"SELECT * FROM [TABLE] WHERE customerNumber = ?", DB2Conn))
{
com.Parameters.AddWithValue("#var", cst.customerNumber);
using (OdbcDataReader reader = com.ExecuteReader())
{
try
{
while (reader.Read())
{
cst.name = (string)reader["name"];
return cst;
}
}
catch
{
throw;
}
}
}
}
return cst;
}
}
}
I found a great question that doesn't really have detailed answers, I feel like similar solutions exist for both of these questions...
And that just seems like bad programming.
Why do you think that?
The underlying system should be maintaining connections in a connection pool for you. Creating a connection should be very optimized already.
From a logical perspective, what you're doing now is exactly what you want to be doing. Create the connection, use it, and dispose of it immediately. This allows other threads/processes/etc. to use it from the connection pool now that you're done with it.
This also avoids the myriad of problems which arise from manually maintaining your open connections outside of the code that uses them.
Is there a way I can optimize this Get() request to leave a connection open?
Have you measured an actual performance problem? If not, there's nothing to optimize.
And there's a very good chance that hanging on to open connections in a static context in your web application is going to have drastic performance implications.
In short... You're already doing this correctly. (Well, except for that unnecessary try/catch. You can remove that.)
Edit: If you're just looking to improve the readability of the code (which itself is a matter of personal preference), this seems readable to me:
public User Get([FromUri] User cst)
{
if (cst == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
using (var DB2Conn = new OdbcConnection(constr))
using (var com = new OdbcCommand("SELECT * FROM [TABLE] WHERE customerNumber = ?", DB2Conn))
{
com.Parameters.AddWithValue("#var", cst.customerNumber);
DB2Conn.Open();
using (OdbcDataReader reader = com.ExecuteReader())
while (reader.Read())
{
cst.name = (string)reader["name"]
return cst;
}
}
return cst;
}
Note that you can further improve it by re-addressing the logic of that SQL query. Since you're fetching one value from one record then you don't need to loop over a data reader. Just fetch a single literal and return it. Note that this is free-hand and untested, but it might look something like this:
public User Get([FromUri] User cst)
{
if (cst == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
using (var DB2Conn = new OdbcConnection(constr))
using (var com = new OdbcCommand("SELECT name FROM [TABLE] WHERE customerNumber = ? FETCH FIRST 1 ROWS ONLY", DB2Conn))
{
com.Parameters.AddWithValue("#var", cst.customerNumber);
DB2Conn.Open();
cst.name = (string)com.ExecuteScalar();
}
return cst;
}
#David's answer addresses your actual questions perfectly but here's some other observations that may make your code a little more pallatable to you:
remove the try/catch block - all you're doing is re-throwing the exception which is what will happen if you don't use a try/catch at all. Don't catch the exception unless you can do something about it. (I see now that #David's answer addresses that - either it was added after I read it or I missed it - my apologies for the overlap but it's worth reinforcing)
Change your query to just pull name and use ExecuteScalar instead of ExecuteReader. You are taking the name value from the first record and exiting the while loop. ExecuteScalar returns the value from the first column in the first record, so you can eliminate the while loop and the using there.

Unit testing methods with void type

I've got some code as shown below with multiple methods which call a web service to get some data from a database. This will produce a set of fields which are then added to another database from the web app. This all works great but I have no idea how to unit test any of it due to it mostly outputting voids, and the data coming from a database which changes every time the button is clicked. Is there a way to unit test to just if the methods work or not? Sorry I am very new to unit testing but I know how important it is so any help would be appreciated.
//Get webservice service
private Service1 GetService()
{
return new TestProjectService.Service1();
}
//Choose which webservice we want to use based on radio button selection
private TestProjectService.CommandMessages GetCommand(Service1 service)
{
var command = new TestProjectService.CommandMessages();
switch (WebServiceRadio.SelectedIndex)
{
case 0:
command = service.GetData();
break;
case 1:
command = service.GetDataLINQ();
break;
}
return command;
}
//Display the results in a label on screen
private void DisplayResult(string text)
{
LatestCommandLabel.Text = text;
}
//Get the current username of the user logged in
public string GetUsername()
{
return System.Security.Principal.WindowsIdentity.GetCurrent().Name;
}
//Submit the data to the database using Linq
private void SubmitData(string username, TestProjectService.CommandMessages command)
{
var dc = new TestProjectLinqSQLDataContext();
var msg = new TestProjectCommandMessage
{
Command_Type = command.CommandType,
Command = command.Command,
DateTimeSent = command.DateTimeSent,
DateTimeCreated = command.DateTimeCreated,
Created_User = username,
Created_Dttm = DateTime.Now
};
dc.TestProjectCommandMessages.InsertOnSubmit(msg);
dc.SubmitChanges();
}
//Return the value and submit data to database
private void ReturnValue()
{
var service = GetService();
var command = GetCommand(service);
var username = GetUsername();
if (command != null)
{
DisplayResult(String.Format("Last Command Called (Using {0}) : {1}", WebServiceRadio.SelectedItem.ToString(), command.Command));
string userName = GetUsername();
SubmitData(username, command);
}
else
{
DisplayResult("No Commands Available");
}
}
//Onlick return value
protected void GetCommandButton_Click(object sender, EventArgs e)
{
ReturnValue();
}
Behavior verification is the approach used to test methods that don't return any values.
In a nutshell, since the method doesn't return any results, the only thing a test can do is make sure the method causes the appropriate actions to take place. This is typically accomplished by using a mock object, which keeps track of whether or not its methods have been called.
In order for your tests to use test doubles, you need to include seams in the design of your system.
I strongly recommend reading Dependency Injection in .Net, by Mark Seeman. Since you're new to unit testing, you undoubtedly have many questions on the mechanics involved in unit testing (and this answer probably sparked even more questions) - this book goes into great detail answering those questions.

Categories