I know there are lots of delegate/func examples but I can't find any examples that will work for me, or I just don't understand them.
I'm using asp.net MVC for a website, and the website needs some web service calls for an outside application to interact with my app. These all need a function to execute (going to db and whatnot), and return a similar data model every time. I want to wrap each call in a try/catch and populate the model.
Here is the generic code that happens every call.
var model = new ResponseDataModel();
try
{
//execute different code here
}
catch (Exception ex)
{
model.Error = true;
model.Message = ex.ToString();
}
return View(model); // will return JSON or XML depending on what the caller specifies
This is one of the controller methods/ functions that I am using
public ActionResult MillRequestCoil()
{
var model = new ResponseDataModel();
try
{
/* edit */
//specific code
string coilId = "CC12345";
//additional code
model.Data = dataRepository.doSomethingToCoil(coilId);
//replaced code
//model.Data = new { Coil = coilId, M3 = "m3 message", M5 = "m5 message" };
model.Message = string.Format("Coil {0} sent successfully", coilId);
}
catch (Exception ex)
{
model.Error = true;
model.Message = ex.ToString();
}
return View(model);
}
I would like to be able to somehow convert the specific function to a variable to pass into the generic code. I've looked at delegates and anonymous funcs but it's pretty confusing until you do it yourself.
Put the following somewhere accessible:
public static ActionResult SafeViewFromModel(
Action<ResponseDataModel> setUpModel)
{
var model = new ResponseDataModel();
try
{
setUpModel(model);
}
catch (Exception ex)
{
model.Error = true;
model.Message = ex.ToString();
}
return View(model);
}
and then call it like:
public ActionResult MillRequestCoil()
{
return MyHelperClass.SafeViewFromModel(model =>
{
string coilId = "CC12345";
model.Data = new {
Coil = coilId,
M3 = "m3 message",
M5 = "m5 message" };
model.Message = string.Format("Coil {0} sent successfully", coilId);
});
}
public interface IAction{
public void doAction(ResponseDataModel model);
}
public class Action1 : IAction
{
public void doAction(ResponseDataModel model)
{
string coilId = "CC12345";
model.Data = new { Coil = coilId, M3 = "m3 message", M5 = "m5 message" };
model.Message = string.Format("Coil {0} sent successfully", coilId);
}
}
class Class1
{
public ActionResult MillRequestCoil(IAction action)
{
var model = new ResponseDataModel();
try
{
//specific code
action.doAction(model);
}
catch (Exception ex)
{
model.Error = true;
model.Message = ex.ToString();
}
return View(model);
}
}
Use:
var result = MillRequestCoil(new Action1());
or execute other code
var result = MillRequestCoil(new ActionXY());
This is a variation of Rawling's answer, that I believe has better readability:
public ActionResult MillRequestCoil()
{
var model = CreateResponseDataModel(RunSpecificCode);
return View(model);
}
public ActionResult MillDoSomethingElse ()
{
var model = CreateResponseDataModel(RunOtherCode);
return View(model);
}
private ResponseDataModel CreateResponseDataModel(Action<ResponseDataModel> action)
{
var model = new ResponseDataModel();
try
{
action(model);
}
catch (Exception ex)
{
model.Error = true;
model.Message = ex.ToString();
}
return model;
}
private void RunSpecificCode(ResponseDataModel model)
{
/* edit */
//specific code
const string coilId = "CC12345";
//additional code
model.Data = _dataRepository.DoSomethingToCoil(coilId);
//replaced code
//model.Data = new { Coil = coilId, M3 = "m3 message", M5 = "m5 message" };
model.Message = string.Format("Coil {0} sent successfully", coilId);
}
private void RunOtherCode(ResponseDataModel obj)
{
// some other piece of code
}
No lambdas, and nice separation of concerns between your specific code and the template code for the try/catch and model building stuff.
Related
I need to call from create method delete for cleanup. I want to call DeleteDevice like lazy cleanup and do not care if it will succeed or not.
People suggested to use this way:
DeleteDevice(param).ConfigureAwait(false);
Is it safe?
public async Task<ActionResult<Device>> CreateDevice([FromBody] CreateDeviceRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var registeredDevice = await RegisterDevice(request.DisplayName);
bool isCreatePrinterSucceed = false;
try
{
var updatedDevice = await UpdateDevice(registeredDevice.Id);
isCreatePrinterSucceed = true;
return Ok(new DeviceReference
{
DisplayName = request.DisplayName,
Id = updatedDevice.Id
});
}
finally
{
if (!isCreatePrinterSucceed)
{
var param = new DeleteDeviceRequest()
{
Id = registeredDevice.Id,
AzureUserBearerToken = request.AzureUserBearerToken
};
DeleteDevice(param).ConfigureAwait(false); ;
}
}
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
}
}
My Api Service is in .NET and my client side is in React.js. I use axios.post to send parameters and retrieve datas from .NET. I want to see error details on react.js side when something happened in service side. Example codes are below;
[HttpPost]
public ConcreteAccrument CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
ConcreteApplication application = depositAmountParameters.application;
int multiplier = depositAmountParameters.multiplier;
bool forceCalculation = depositAmountParameters.forceCalculation;
long registryInfoOid = depositAmountParameters.registryInfoOid;
long subscriberRegistryOid = depositAmountParameters.subscriberRegistryOid;
try
{
Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage wssService = new Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage();
return wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
}
catch (BSException e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = new BSCommunicationException();
commException.Id = e.Id;
commException.ExceptionMessage = e.ExceptionMessage;
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
catch (Exception e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = PrepareCommunicationException(e);
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
}
There are some details in throw new FaultException at first catch(BSException e). It's not a system error. For example, data is null or some value are missing when first catch works. And second catch is system error. But in that code all catches return 500 error in React.Js side. All I want is to see all detail in first catch on React.js side. When I use "return error" in catch then I get convert error because my class return an object.
Here my react.js code;
export const CalculateDepositAmount = (APPLICATION,MULTIPLIER,FORCE_CALCULATION,REGISTRY_INFO_OID, SUBSCRIBER_REGISTRY_OID, SuccessOperation, FailedOperation) => {
return () => {
const body = { application:APPLICATION,multiplier:MULTIPLIER,forceCalculation:FORCE_CALCULATION,registryInfoOid:REGISTRY_INFO_OID, subscriberRegistryOid:SUBSCRIBER_REGISTRY_OID};
console.log("bodyFormData",body)
axios.post('https://localhost:44396/api/CalculateDepositAmount', body)
.then( async response => {
SuccessOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: await response.data });
})
.catch(() => {
FailedOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: null })
});
}
}
I am assuming that this is not asp.net core / 5 / 6, but vanilla 4.x
One thing you can do is change the method signature to IHttpActionResult, so you can return different status codes, with varying payloads back to the client:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
try
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
return Ok(result);
}
catch (BSException e)
{
return BadRequest(e.Message)
//or
//return StatusCode(418)
}
catch (Exception e)
{
}
}
You can tailor the response to the client much better to your needs, instead of return either the object or an exception. You can find the full list of here:
https://learn.microsoft.com/en-us/previous-versions/aspnet/dn314678(v=vs.118)?redirectedfrom=MSDN
Another approach that will require some more refactoring, is to change the return type of your service to some sort of Result object, that indicates, whether it is a successfull operation or if a problem occured.
For example take this CommandResult example:
public class CommandResult<T>
{
private CommandResult(T payload) => Payload = payload;
private CommandResult(string failureReason)
{
FailureReason = failureReason;
}
public string FailureReason { get; }
public string Message { get; }
public bool IsSuccess => string.IsNullOrEmpty(FailureReason);
public T Payload { get; }
public static implicit operator bool(CommandResult<T> result) => result.IsSuccess;
public static CommandResult<T> Success(T payload)
=> new(payload);
public static CommandResult<T> Fail(string reason)
=> new(reason);
}
In your service you can now do the following:
public Commandresult<ConcreteAccrument> CalculateDepositAmount(DepositAmountParameters depositAmountParameters)
{
try
{
var result = // do the calculation
return CommandResult<ConcreteAccrument>.Success(result);
}
catch (BSException e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
catch (Exception e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
}
Now your controller simply has to decide, if it was successfull or not:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
if(result.IsSuccess) // or simply if (result)
{
return Ok(result.Payload);
}
return Exception(result.FailureReason); //or whatever suits best.
}
I want to perform a unit test on my following method:
public HttpResponseMessage PostUser(User obj)
{
try
{
bool error = false;
string errorMessage = "";
if (obj.FullName == "")
{
error = true;
errorMessage += "Please do not leave name area empty. ";
}
//Several other if statements to check everything is filled as expected by the user
if(!error)
{
db.Users.Add(obj);
db.SaveChanges();
var response = Request.CreateResponse(HttpStatusCode.Created, obj);
response.Headers.Location = new Uri(Request.RequestUri + obj.ID.ToString());
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
I know that method performs well as it inserts data to database without popping any error. So, I watch some videos and check other questions related with UnitTesting, but unfortunatelly cannot make test response equal to expected response. Below is my test case:
[TestClass]
public class ProcessControllerTests
{
private readonly ProcessController _processController;
public ProcessControllerTests()
{
_processController = new ProcessController
{
Request = new System.Net.Http.HttpRequestMessage(),
Configuration = new System.Web.Http.HttpConfiguration()
};
}
[TestMethod]
public void PostUser()
{
User testUser = new User
{
ID = 10,
FullName = "Test User",
Email = "test#unit.com",
UserName = "TestU",
Password = "teus123456"
};
var testResponse = _processController.PostUser(testUser);
Assert.AreEqual(HttpStatusCode.Created, testResponse.StatusCode);
}
}
I'm not sure where I am making the mistake.
Thanks in advance!
I have a wcf api and wish to wrap all requests inside a transaction
Currently my code looks like this in each endpoint
public MyCompleteList ReadOrganisations()
{
MyCompleteList resp = new MyCompleteList ();
try
{
using (TransactionScope scope = new TransactionScope())
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
DC_Base browser_request = new DC_Base(PROJECT);
browser_request.cmd_user_id = coreDb.GetUserIDFromLoginName(PROJECT,
HttpContext.Current.User.Identity.Name);
resp =
new MyCompleteList (coreSc.User_Read_All_Organisations(browser_request, utils,
validation, coreSc, coreDb));
scope.Complete();
}
else
{
resp.SetResponseNotLoggedIn();
}
}
}
catch (TransactionAbortedException ex)
{
resp.SetResponseServerError();
}
catch (ApplicationException ex)
{
resp.SetResponseServerError();
}
return resp;
}
As you can see if I am to use the "using" transaction scope part in every endpoint (approx 300) its going to be a lot of duplicated code.
is there anyway to reduce the amount of duplication?
You can write a helper method, that handles the transaction logic while calling your actual code as a lambda.
public static T Execute<T>(Func<T> func, TransactionExecutionOptions options = null)
{
options = options ?? TransactionExecutionOptions.Default;
T res;
using (var tx = new TransactionScope(options))
{
res = func();
tx.Complete();
}
return res;
}
Depending on your needs you can provide additional arguments to the Func argument; for example, the Execute method could also open a database connection and pass that to the func (then having Func<IDbConnection, T> as parameter type). YMMV.
For your example:
public MyCompleteList ReadOrganisations()
{
MyCompleteList resp = new MyCompleteList ();
try
{
resp = Execute(() => {
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
DC_Base browser_request = new DC_Base(PROJECT);
browser_request.cmd_user_id = coreDb.GetUserIDFromLoginName(PROJECT,
HttpContext.Current.User.Identity.Name);
resp =
new MyCompleteList (coreSc.User_Read_All_Organisations(browser_request, utils,
validation, coreSc, coreDb));
scope.Complete();
}
else
{
resp.SetResponseNotLoggedIn();
}
});
}
catch (TransactionAbortedException ex)
{
resp.SetResponseServerError();
}
catch (ApplicationException ex)
{
resp.SetResponseServerError();
}
return resp;
}
If possible, you can also factor the SetResponse*() methods out into a base class or interface (say IMyResponse), thus making it possible to handle this aspect inside the Execute method as well.
public static T Execute<T>(Func<T> func, TransactionExecutionOptions options = null) where T : IMyResponse
{
options = options ?? TransactionExecutionOptions.Default;
T res;
try
{
using (var tx = new TransactionScope(options))
{
res = func();
tx.Complete();
}
}
catch (TransactionAbortedException ex)
{
res.SetResponseServerError();
}
catch (ApplicationException ex)
{
res.SetResponseServerError();
}
return res;
}
1- Create a ServiceBase class as follows
public class ServiceBase
{
protected void ExecuteOperation(Action codetoExecute)
{
try
{
using (TransactionScope scope = new TransactionScope())
{
codetoExecute.Invoke();
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
// handle exception
}
catch (ApplicationException ex)
{
// handle exception
}
}
}
2- Each new service must inherits from ServiceBase and call ExecuteOperation instead. Code as follows:
ExecuteOperation(() =>
{
// Custom code here
});
3- Atomic transactions are useful when executing operations that dont expect results in return.
Having an Action that uses a base method that expect a Func
public class HomeController: BaseController
{
public JsonResult HomeController()
{
var model = ExecuteHandledJTableJsonOperation(() =>
{
//do some stuff
}, LocalResources.CommonErrorMessage);
return Json(model);
}
}
And the base method that expect Func
public class BaseController : Controller
{
public T ExecuteHandledJTableJsonOperation<T>(Func<T> actionToExecute, string errorMessage)
{
try
{
return actionToExecute.Invoke();
}
catch (Exception ex)
{
LogEntry entry = new LogEntry();
entry.AddErrorMessage(ex.Message);
entry.AddErrorMessage(String.Format("Inner Exception:", ex.InnerException.Message));
//entry.Message = ex.Message;
entry.Priority = 1;
entry.EventId = 432;
entry.Severity = System.Diagnostics.TraceEventType.Error;
writer.Write(entry);
return Json(new { Result = "ERROR", Message = errorMessage });
}
}
}
It retrieves me an error when I trying to return Json(new { Result = "ERROR", Message = errorMessage });
Cannot implicitly convert type 'System.Web.Mvc.JsonResult' to 'T'
I know that is better if I create an override of ExecuteHandledJTableJsonOperation that expect two action, one to execute/return normally and the second to execute when the operation has an excetion.
Something like this:
return ExecuteHandledJTableJsonOperation(() =>
{
//do something
return Json(new { Result = "OK", Records = excepciones, TotalRecordCount = excepciones.Count() });
}, () =>
{
return Json(new { Result = "ERROR", Message = Properties.Resources.CommonErrorMessage });
});
But I want to know how to solve the first case:
Cannot implicitly convert type 'System.Web.Mvc.JsonResult' to 'T'
Thanks.
It is not entirely clear why your method is generic as you seem to want to always return a JsonResult as such simply change your method to this.
public class BaseController : Controller
{
public JsonResult ExecuteHandledJTableJsonOperation<T>(Func<JsonResult> actionToExecute, string errorMessage)
{
try
{
return actionToExecute.Invoke();
}
catch (Exception ex)
{
LogEntry entry = new LogEntry();
entry.AddErrorMessage(ex.Message);
entry.AddErrorMessage(String.Format("Inner Exception:", ex.InnerException.Message));
//entry.Message = ex.Message;
entry.Priority = 1;
entry.EventId = 432;
entry.Severity = System.Diagnostics.TraceEventType.Error;
writer.Write(entry);
return Json(new { Result = "ERROR", Message = errorMessage });
}
}
}
I guess you don't need that to be soo generic. Providing that you will use that only in your actions you can return ActionResult as JsonResult derived from it. Think adding constraint of ActionResult will be sufficient in your case:
public T ExecuteHandledJTableJsonOperation<T>(Func<T> actionToExecute, string errorMessage)
where T: ActionResult
{
//code
}