Are there advantages of using .NET Core's middleware "Health checks" over just a ping in a controller's route? - asp.net-core

I'm reading a "Asp.net Core 3 and Angular 9" book and there is an example usage of .NET Core Health check.
It's also described on Microsoft website: https://learn.microsoft.com/en-US/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.0
I can't find a reason to actually use it over just creating a route in some controller which will ping external addresses.
Code in a book goes like this:
Add this in Configure (Startup.cs) method:
app.UseHealthChecks("/hc", new CustomHealthCheckOptions());
ConfigureServices method:
services.AddHealthChecks()
.AddCheck("ICMP_01", new ICMPHealthCheck("www.ryadel.com", 100))
.AddCheck("ICMP_02", new ICMPHealthCheck("www.google.com", 100))
.AddCheck("ICMP_03", new ICMPHealthCheck("www.does-notexist.com", 100));
Create ICMPHealthCheck.cs file:
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
namespace HealthCheck
{
public class ICMPHealthCheck : IHealthCheck
{
private string Host { get; set; }
private int Timeout { get; set; }
public ICMPHealthCheck(string host, int timeout)
{
Host = host;
Timeout = timeout;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
using (var ping = new Ping())
{
var reply = await ping.SendPingAsync(Host);
switch (reply.Status)
{
case IPStatus.Success:
var msg = String.Format(
"IMCP to {0} took {1} ms.",
Host,
reply.RoundtripTime);
return (reply.RoundtripTime > Timeout)
? HealthCheckResult.Degraded(msg)
: HealthCheckResult.Healthy(msg);
default:
var err = String.Format(
"IMCP to {0} failed: {1}",
Host,
reply.Status);
return HealthCheckResult.Unhealthy(err);
}
}
}
catch (Exception e)
{
var err = String.Format(
"IMCP to {0} failed: {1}",
Host,
e.Message);
return HealthCheckResult.Unhealthy(err);
}
}
}
}
Create CustomHealthCheckOptions.cs file:
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.Net.Mime;
using System.Text.Json;
namespace HealthCheck
{
public class CustomHealthCheckOptions : HealthCheckOptions
{
public CustomHealthCheckOptions() : base()
{
var jsonSerializerOptions = new JsonSerializerOptions()
{
WriteIndented = true
};
ResponseWriter = async (c, r) =>
{
c.Response.ContentType =
MediaTypeNames.Application.Json;
c.Response.StatusCode = StatusCodes.Status200OK;
var result = JsonSerializer.Serialize(new
{
checks = r.Entries.Select(e => new
{
name = e.Key,
responseTime = e.Value.Duration.TotalMilliseconds,
status = e.Value.Status.ToString(),
description = e.Value.Description
}),
totalStatus = r.Status,
totalResponseTime =
r.TotalDuration.TotalMilliseconds,
}, jsonSerializerOptions);
await c.Response.WriteAsync(result);
};
}
}
}
So it just pings 3 addresses and I can't see advantages of using Microsoft.AspNetCore.Diagnostics.HealthChecks library. Is that wrong example?

Related

Receiving error while fetching mails using Aspose Email dll and Microsoft Graph Client API

Below is the code part along with error being received
Error received
Aspose.Email.AsposeBadServerResponceException: 'Server error Status: ResourceNotFound
Description: Resource could not be discovered.
Details:
GET: https://graph.microsoft.com/v1.0/users/1234outlook.onmicrosoft.com/mailFolders
Authorization: Bearer xxxxxx
Accept: application/json
Code 1
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Aspose.Email.Clients;
using Aspose.Email.Clients.Graph;
using Aspose.Email.Mapi;
using Azure.Identity;
using EASendMail;
using Microsoft.Graph;
namespace Code
{
internal class Graph_API
{
private static string _clientId = ConfigurationManager.AppSettings["ClientId"];
private static string _tenantId = ConfigurationManager.AppSettings["TenantId"];
private static string _secretValue = ConfigurationManager.AppSettings["SecretValue"];
static string _postString(string uri, string requestData)
{
HttpWebRequest httpRequest = WebRequest.Create(uri) as HttpWebRequest;
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
using (Stream requestStream = httpRequest.GetRequestStream())
{
byte[] requestBuffer = Encoding.UTF8.GetBytes(requestData);
requestStream.Write(requestBuffer, 0, requestBuffer.Length);
requestStream.Close();
}
try
{
HttpWebResponse httpResponse = httpRequest.GetResponse() as HttpWebResponse;
var responseText = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseText);
return responseText;
}
catch (WebException ep)
{
if (ep.Status == WebExceptionStatus.ProtocolError)
{
var responseText = new StreamReader(ep.Response.GetResponseStream()).ReadToEnd();
Console.WriteLine(responseText);
}
throw ep;
}
}
public string GenerateToken()
{
string client_id = _clientId;
string client_secret = _secretValue;
string tenant = _tenantId;
string requestData =
string.Format("client_id={0}&client_secret={1}" +
"&scope=https://graph.microsoft.com/.default&grant_type=client_credentials",
client_id, client_secret);
string tokenUri = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", tenant);
string responseText = _postString(tokenUri, requestData);
OAuthResponseParser parser = new OAuthResponseParser();
parser.Load(responseText);
var vv = parser.AccessToken;
return vv;
}
public void Generatemail()
{
interface_class bb = new interface_class();
IGraphClient client = GraphClient.GetClient(bb, _tenantId);
client.Resource = (ResourceType)1;
client.ResourceId = "1234outlook.onmicrosoft.com";
MapiMessage mm = new MapiMessage();
mm.Subject = "EMAILNET-39318 " + Guid.NewGuid().ToString();
mm.Body = "EMAILNET-39318 REST API v1.0 - Create Message";
mm.SetProperty(KnownPropertyList.DisplayTo, "1234outlook.onmicrosoft.com");
mm.SetProperty(KnownPropertyList.SenderName, "1234outlook.onmicrosoft.com");
mm.SetProperty(KnownPropertyList.SentRepresentingEmailAddress, "1234outlook.onmicrosoft.com");
// Create message in inbox folder
MapiMessage createdMessage = client.CreateMessage(Aspose.Email.Clients.Graph.KnownFolders.Inbox, mm);
}
public void FetchMail()
{
try
{
interface_class bb = new interface_class();
using (IGraphClient client = GraphClient.GetClient(bb, _tenantId))
{
client.Resource = (ResourceType)1;
client.ResourceId = "1234outlook.onmicrosoft.com";
FolderInfoCollection folderInfoCol1 = client.ListFolders();
FolderInfo inbox = null;
foreach (FolderInfo folderInfo in folderInfoCol1)
{
if (folderInfo.DisplayName.Equals("Inbox", StringComparison.InvariantCultureIgnoreCase))
{
inbox = folderInfo;
break;
}
}
MessageInfoCollection messageInfoCol = client.ListMessages(inbox.ItemId);
MessageInfo messageInfo = messageInfoCol[0];
MapiMessage message = client.FetchMessage(messageInfo.ItemId);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
--------------
Code file 2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Aspose.Email.Clients;
using Aspose.Email.Clients.Graph;
namespace Code
{
internal class interface_class : ITokenProvider
{
Graph_API obj = new Graph_API();
DateTime expirationDate = DateTime.Today.AddDays(1);
public void Dispose()
{
throw new NotImplementedException();
}
public OAuthToken GetAccessToken()
{
string token = obj.GenerateToken();
return new OAuthToken(token, expirationDate);
}
public OAuthToken GetAccessToken(bool ignoreExistingToken)
{
throw new NotImplementedException();
}
}
}

"Validation failed for one or more entities. ERROR

I am creating web API to save the uploaded file in my local storage. When I testing my code it gives an error as ExceptionMessage": "Validation failed for one or more entities. See EntityValidationErrors' property for more details."
Can anyone help to fix this issue. Thanks in advance.
Controller(FileUploadController)
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using System.Web.Http;
using VantageCore.BL;
namespace VantageCoreApi.Controllers.Api
{
public class FileUploadController : ApiController
{
[HttpPost]
[Route("api/FileUpload")]
public async Task<IHttpActionResult> UploadFile(string FileName, int Id)
{
try
{
List<string> ids = new List<string>();
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var referenceId = FileName.Split('_')[0];
foreach (var file in provider.Contents)
{
Guid guid;
ids.Add(Guid.TryParse(await new FileUploadMgt().ReceiveFile(file, FileName, Id), out guid) ? FileName : "Error");
}
return Ok(ids);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
public string SaveFile(byte[] File, string path)
{
string Result = "";
try
{
//LOCAL SERVER PATH
var fs = new BinaryWriter(new FileStream(#"F:\Testfolder" + path, FileMode.Append, FileAccess.Write));
fs.Write(File);
fs.Close();
Result = path;
}
catch (Exception ee)
{
Result = ee.ToString();
}
return Result;
}
}
}
BL (FileUplodMgt.cs)
using System;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Net.Http;
using VantageCore.Entity.Model;
using File = VantageCore.Entity.Model.File;
namespace VantageCore.BL
{
public class FileUploadMgt
{
public async Task<string> ReceiveFile(HttpContent receivedFile, string receivedFileName, int Id)
{
if (receivedFile != null)
{
var fileId = Guid.NewGuid();
using (var c = new DBEntities())
{
NameValueCollection appSettings = ConfigurationManager.AppSettings;
string folder = appSettings["TestPath"];
var fileName = fileId.ToString() + Path.GetExtension(receivedFileName).ToLower();
var file = Path.Combine(folder, fileName);
bool exists = Directory.Exists(folder);
if (!exists) Directory.CreateDirectory(folder);
using (var fs = new BinaryWriter(new FileStream(file, FileMode.Create, FileAccess.Write)))
{
fs.Write(await receivedFile.ReadAsByteArrayAsync());
}
string extention = Path.GetExtension(file);
receivedFileName = Path.GetFileNameWithoutExtension(receivedFileName).Length <= 32
? Path.GetFileNameWithoutExtension(receivedFileName)
: Path.GetFileNameWithoutExtension(receivedFileName).Substring(0, 31) + "~";
var newFile = new File
{
Uid = fileId,
FileExtention = extention,
FileName = receivedFileName,
FileSize = (int)(receivedFile.Headers.ContentLength / 1024),
CreatedDate = DateTime.UtcNow
};
c.Files.Add(newFile);
c.SaveChanges();
}
return fileId.ToString();
}
else
{
return "Error,Invalid file Or file size exceeded";
}
}
}
}
You could try as below to observe the error message when you debug and share it;
try
{
c.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
}
}

Unable to connect Azure Function App with Database (using .net core 2.1)

Please note (Environment):
Function App: Version 2,
Target Framework: .Net Core 2.1
I am developing a Function App, that will work like Web Api. This Function App will return the data from database tables, also it'll manipulate files in Azure storage(Blob). But I am stuck as I am unable to create ConnectionString from local.settings.json file. Ideally the connection string should be created by default as I followed some tutorials & no where mentioned any extra steps to create default connectionstring value, just need to create it in local.settings.json file.
following is my local.settings.json file content:-
{
"ConnectionStrings": {
"mycs": "data source=servername;initial catalog=dbname;user id=XXXX;password=XXXX;MultipleActiveResultSets=True;"
},
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"mycs": "data source=servername;initial catalog=dbname;user id=XXXX;password=XXXX;MultipleActiveResultSets=True;"
}
}
following is my HttpTrigger file:
namespace my_api
{
public class myDataContext : DbContext
{
public myDataContext() : base(GetConnectionString()) { }
private static string GetConnectionString()
{
const string providerName = "System.Data.SqlClient";
const string metadata = #"res://*/MYDB.csdl|res://*/MYDB.ssdl|res://*/MYDB.msl";
try
{
string connectString = ConfigurationManager.ConnectionStrings["mycs"].ToString();
// Initialize the connection string builder for the
// underlying provider.
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder(connectString);
// Set the properties for the data source.
//sqlBuilder.IntegratedSecurity = true;
sqlBuilder.MultipleActiveResultSets = true;
// Build the SqlConnection connection string.
string providerString = sqlBuilder.ToString();
// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
//Set the provider name.
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = metadata;
return entityBuilder.ConnectionString;
}
catch { }
var connectionstring = Environment.GetEnvironmentVariable("mycs");
return connectionstring;
}
public DbSet<flowerset> flowersets
{
get;
set;
}
}
}
Following is the code for :
namespace my_api
{
public static class helpService
{
[FunctionName("helpService_get")]
public static async Task> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
ILogger log, ExecutionContext context)
{
log.LogInformation("C# HTTP trigger function processed a request helpService_get).");
try {
int page = 0;
int pageSize = 20;
myDataContext entity = new myDataContext();
if (page == 0 && pageSize == 0)
{
return entity.helpsets.ToList();
}
if (pageSize <= 0) { pageSize = 20; }
entity.helpsets.OrderByDescending(x => x.id).Skip((page - 1) * pageSize).Take(pageSize).ToList();
}
catch (Exception exx) {
log.LogInformation("Exception changed (helpService_get): "+exx.Message);
}
return null;
}
}//End of Class
}//End of Namespace
I am getting following error on line entity.helpsets.OrderByDescending(x => x.id).Skip((page - 1) * pageSize).Take(pageSize).ToList();:
Unable to determine the provider name for provider factory of type 'System.Data.SqlClient.SqlClientFactory'. Make sure that the ADO.NET provider is installed or registered in the application config.
According to my test, we can use System.Data.SqlClient to connect Azure SQL in Azure function V2.0. For example
Create an Azure Function with Visual Studio 2019
Install System.Data.SqlClient package(the version I sue is 4.5.1)
Develop the function
local.settings.json file content
"ConnectionStrings": {
"mycs": "Data Source="";Initial Catalog=DotNetAppSqlDb20190826105048_db;User Id="";Password="" "
},
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
Code
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
try
{
var connectionstring = System.Environment.GetEnvironmentVariable($"ConnectionStrings:mycs"); ;
using (SqlConnection connection = new SqlConnection(connectionstring))
{
connection.Open();
log.LogInformation(" sql login success");
StringBuilder sb = new StringBuilder();
sb.Append("select * from dbo.Todoes");
String sql = sb.ToString();
using (SqlCommand command = new SqlCommand(sql, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
log.LogInformation("{0} {1}", reader.GetInt32(0), reader.GetString(1));
}
}
}
connection.Close();
}
}
catch (SqlException e)
{
Console.WriteLine(e.ToString());
}
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
For more details, please refer to the document

signalr with sqldepdency firing multiple times for each browser instance

I am having a asp.net mvc app with vs2013 and .net framwork 4.5.1 which should notify users when a certain field gets updated and for a certain recordID.
Everything works fine when I have a single instance of the browser open, but when i open another tab or browser either on the same machine or on the different machine it fires the sqldepdencychange event multiple times.
Below is my hub code
public class MessagesHub : Hub
{
private static string conString = ConfigurationManager.ConnectionStrings["FleetLink_DB"].ToString();
private static string hostName = "";
public static void SendMessages(string hName)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>();
hostName = hName;
context.Clients.Group(hostName).updateMessages(hName);
}
public Task leaveGroup(string hName)
{
return Groups.Remove(Context.ConnectionId, hName);
}
public Task joinGroup(string hName)
{
return Groups.Add(Context.ConnectionId, hName);
}
}
Below is my signalr script file
$(function () {
var dialog, form
// Declare a proxy to reference the hub.
var notifications = $.connection.messagesHub;
//debugger;
//Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function (hName) {
alert("testing");
getoneMessages(hName)
};
$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
var hostName = getUrlVars()["System_Name"];
notifications.server.joinGroup(hostName);
}).fail(function (e) {
alert(e);
});
});
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
function getoneMessages(hName) {
var tbl = $('#selectable');
//alert('mesgID=' + mesgID)
//var tbl = $('#selectable');
$.ajax({
url: '/controller/view',
cache: false,
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
//alert(result);
tbl.empty().append(result);
}).error(function (exception) {
//alert('failed= ' + exception);
});
}
window.onbeforeunload = function (e) {
var hostName = getUrlVars()["System_Name"];
notifications.server.joinGroup(hostName);
$.connection.hub.stop();
};
Below is my partialview code along with the definition for RegisterForNotification and depdendency_onchange event
public PartialViewResult SignalRTesterPartialView()
{
/...COde not included for brevity..../
RegisterForNotifications(ID);
}
public void RegisterForNotifications(int mID)
{
var efConnectionString = ConfigurationManager.ConnectionStrings["DB"].ConnectionString;
var builder = new EntityConnectionStringBuilder(efConnectionString);
var regularConnectionString = builder.ProviderConnectionString;
string commandText = null;
commandText = "select ID,Status,Name from tblABC where ID=" + strID;
using (SqlConnection connection = new SqlConnection(regularConnectionString))
{
using (SqlCommand command = new SqlCommand(commandText, connection))
{
connection.Open();
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
// NOTE: You have to execute the command, or the notification will never fire.
var reader = command.ExecuteReader();
}
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change && e.Info== SqlNotificationInfo.Update)
{
MessagesHub.SendMessages(hName);
}
RegisterForNotifications(1012);
}
Not sure why it is firing the sendmessages multiple times with each additional browser instance that I open. Any pointers would be helpful!
remove EventHandler when you done with it
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change && e.Info== SqlNotificationInfo.Update)
{
MessagesHub.SendMessages(hName);
}
//remove event handler
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= new OnChangeEventHandler(dependency_OnChange);
RegisterForNotifications(1012);
}

VCR for ServiceStack's JsonServiceClient

The Ruby VCR library enables you to "Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests."
I'd like to create something similar using ServiceStack's JsonServiceClient, but I can't get it to work. My most recent failed attempt follows. I'd like to either make my current attempt work, or suggestions on another approach that will work.
public static class Memoization
{
public static Func<T, TResult> AsCached<T, TResult>(this Func<T, TResult> function)
{
var cachedResults = new Dictionary<T, TResult>();
string filename = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + (typeof(TResult)).Name + ".jsv";
var serializer = MessagePackSerializer.Create<Dictionary<T, TResult>>();
if (cachedResults.Count == 0)
{
////// load cache from file
using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
cachedResults = serializer.Unpack(fs);
}
}
return (argument) =>
{
TResult result;
lock (cachedResults)
{
if (!cachedResults.TryGetValue(argument, out result))
{
result = function(argument);
cachedResults.Add(argument, result);
////// update cache file
using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
serializer.Pack(fs, cachedResults);
}
}
}
return result;
};
}
}
class MemoizeJsonClient<TResponse> : JsonServiceClient, IServiceClient, IRestClient
{
private Func<IReturn<TResponse>, TResponse> _getCached;
private JsonServiceClient client;
public TResponse Get(IReturn<TResponse> request)
{
if (_getCached == null)
{
Func<IReturn<TResponse>, TResponse> func = GetImpl;
_getCached = func.AsCached();
}
return _getCached(request);
}
private TResponse GetImpl(IReturn<TResponse> request)
{
return client.Get(request);
}
public MemoizeJsonClient(string BaseUri) {
client = new JsonServiceClient(BaseUri);
}
}
Called like this:
[Test]
public void TestReports2()
{
string Host = "http://localhost:1337";
string BaseUri = Host + "/";
List<Options> testcases = new List<Options>();
testcases.Add(new Options("Name", "20130815", "20130815"));
foreach (Options options in testcases)
{
TransactionsReq transRequest = new TransactionsReq();
transRequest.source = "Source";
transRequest.name = new List<String>(new string[] { options.Name });
transRequest.startDate = options.StartDate;
transRequest.endDate = options.EndDate;
MemoizeJsonClient<TransactionsReqResponse> client = new MemoizeJsonClient<TransactionsReqResponse>(BaseUri);
List<Transaction> transactions;
TransactionsReqResponse transResponse = client.Get(transRequest);
transactions = transResponse.data;
}
}
But I get the following error:
System.Runtime.Serialization.SerializationException occurred
HResult=-2146233076
Message=Cannot serialize type 'ServiceStack.ServiceHost.IReturn`1[ImagineServerWrapper.DTO.TransactionsReqResponse]' because it does not have any serializable fields nor properties.
Source=MsgPack
StackTrace:
at MsgPack.Serialization.SerializerBuilder`1.CreateSerializer()
InnerException: