Passing parameters into an Action? - c#

I am trying to fully understand how I can simplify the following:
public ActionResult Create(string ds) {
InitializeServices(ds, "0000");
vm.Account = new Account {
PartitionKey = "0000",
RowKey = "0000",
Created = DateTime.Now,
CreatedBy = User.Identity.Name
};
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e) {
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
return View("CreateEdit", vm);
}
I had a few great answers and the following was suggested:
private void HandleException(Action action) {
try {
action();
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
}
RunAndHandleExceptions(new Action(() =>
{
//Do some computing }
));
This looks like a really great solution but I still don't understand how I can pass in my
parameters into the action. What I need to do is to pass in the following:
string ds
System.Web.Mvc.ModelState ModelState (passed as a reference)

Just
HandleException(() => someFunction(ds, ModeState));
should do it
To get the return value, you need a Func<>, not Action<>:
private TR HandleException<TR>(Func<TR> action)
{
try
{
return action();
}
catch (ServiceException ex)
{
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
return default(TR); // null for reference types
}
You then would use it, e.g. without an existing function:
bool result = HandleException(() =>
{
if (string.IsNullOrEmpty(ds))
return false;
// do interesting stuff that throws many kinds of exceptions :)
// Note: freely use ds and ModelState from surrounding scope,
// no need to 'pass them'
return true;
});

Have you looked at RedirectToAction?
return this.RedirectToAction(c => c.SomeAction(MyParam));

You can define an action with up to 16 parameters (no discussion, if that number is useful, please). So, sour call could look like:
private void HandleException(Action<string, System.Web.Mvc.ModelState ModelState > action) {
Edit
Here is an example with an action having parameter:
private void RunHandleException(Action<int> action)
{
action(someIntValue);
}
...
RunAndHandleExceptions((someInt) =>
{
//Do some computing
});
And here is an example with a function having a return value:
private void RunHandleException(Func<bool, int> action)
{
bool returnValue = action(someIntValue);
}
...
RunAndHandleExceptions((someInt) =>
{
//Do some computing
return true;
});

Related

function that returns two different types

I should have a function that must return either a string of an error (through try / catch) or a different type T.
Example of such a function:
public T get()
{
T struttura;
try {
...
}
catch (Exception xcp) {
return xcp.Message;
}
...
return struttura;
}
There are ways to do this, but really consider if that's what you actually want. It is almost always better just to let the Exception bubble upwards into the calling code.
The first way is to use an out parameter.
public string get(out T result)
{
T struttura;
try{...}
catch (Exception xcp)
{
result = default(T);
return xcp.Message;
}
...
result = struttura;
return String.Empty;
}
The second way is to use a ValueTuple:
public (T, string) get()
{
T struttura;
try{...}
catch (Exception xcp){return (default(T), dexcp.Message);}
...
return (struttura, string.Empty);
}
The .net design guidelines https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/exception-throwing recommend never returning the exception as a return type. It’s always better design to throw the error and catch in the caller.
The guidelines also recommend that if you don’t want to throw the error that you can follow the TryParse pattern https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions-and-performance#try-parse-pattern. Typically you provide both methods, Get and TryGet. The presence of the Try method should indicate to callers that Get will throw exceptions but TryGet won’t. The TryGet also returns a Boolean if the operation was successful, allowing you to handle negative cases without using a try/catch block on the caller.
I suggest TryGet signature:
public bool TryGet(out T struttura) {
try {
...
struttura = ...
...
return true;
}
catch (Exception xcp){
struttura = default(T);
return false;
}
}
Usage:
if (TryGet(out var myStruct)) {
// succeeded, myStruct is returned struttura
}
else {
// failed
}
Or either do not catch exceptions at all or re-throw exception as custom one:
public T Get() {
try {
...
return struttura;
}
catch (Exception xcp) {
throw new MyException("My message", xcp);
}
}
Usage:
try {
myStruct = Get();
}
catch (MyException e) {
// Failed, e.Message for message
Console.WriteLine(e.Message);
}
Finally, you can mechanically combine value and message and return named tuple:
public (T value, string message) Get() {
try {
...
return (struttura, null);
}
catch (Exception xcp) {
return (default(T), xcp.message);
}
}
Usage:
var result = Get();
if (result.message == null) {
// succceded with result.value
}
else {
// failed with result.message
}

How to avoid code duplication inside two methods?

I have two identical methods, but one of them have return statement inside try catch
public void A(Guid agentId)
{
var agent = _agentsProvider.GetAgentById(agentId);
var updateCompletionSource = C(agentId);
try
{
var cacheEntry = UpdateAgentMetadataCacheEntry(agent, true, false);
updateCompletionSource.SetResult(cacheEntry);
}
catch (Exception e)
{
updateCompletionSource.SetException(e);
}
}
private Entry B(IAgent agent)
{
var updateCompletionSource = C(agent.Id);
try
{
var cacheEntry = UpdateAgentMetadataCacheEntry(agent, false, false);
updateCompletionSource.SetResult(cacheEntry);
return cacheEntry;
}
catch (Exception e)
{
updateCompletionSource.SetException(e);
return GetPreviousCacheEntry();
}
}
How to collect identical part and create new method with this part?
Unless GetPreviousCacheEntry could have problematic side-effects, it seems to me that you don't need method A at all.
Just call method B and ignore the return value if you're not interested in it.
As noted in comments, the methods aren't identical other than the return statements though - because they use a different second argument for UpdateAgentMetadataCacheEntry, and they have different parameters too (one has a Guid and one has an Agent). You could refactor this into:
private Entry B(IAgent agent, bool foo)
{
var updateCompletionSource = C(agent.Id);
try
{
var cacheEntry = UpdateAgentMetadataCacheEntry(agent, foo, false);
updateCompletionSource.SetResult(cacheEntry);
return cacheEntry;
}
catch (Exception e)
{
updateCompletionSource.SetException(e);
return GetPreviousCacheEntry();
}
}
... with a meaningful name for foo, obviously. I'll assume the difference in parameter type isn't a problem in reality.
Like Jon said, you don't need method A. Just add another parameter for boolean value.
public void A(Guid agentId)
{
var agent = _agentsProvider.GetAgentById(agentId);
AnotherA(agent, true);
}
private Entry B(IAgent agent)
{
return AnotherA(agent, false);
}
private Entry AnotherA(IAgent agent, bool a)
{
try
{
var cacheEntry = UpdateAgentMetadataCacheEntry(agent, a, false);
updateCompletionSource.SetResult(cacheEntry);
return cacheEntry;
}
catch (Exception e)
{
updateCompletionSource.SetException(e);
return GetPreviousCacheEntry();
}
}

how to differentiate among different exceptions of same class?

how to check whether its is login info exception or a connection lost exception if the the exceptions are form the same class?
private bool checkFileExists(string absoluteRemoteLocation)
{
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(absoluteRemoteLocation);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Timeout = timeOut;
request.Credentials = new NetworkCredential(this.username, this.password);
request.GetResponse();
return true;
}
catch(Exception e) //i want to check here
{
var g = e.ToString();
return false;
}
}
Use different catch block like this:
catch (System.TimeoutException e)
{
var g = e.ToString();
return false;
}
catch (System.Net.WebException e)
{
var g = e.ToString();
return false;
}
Use is keyword of C#.
<!-- language: C# -->
catch (Exception e) {
if (e is LoginInfoException) // do something
else if (e is ConnectionLostException) // do something else
}
For reference, check this link.
Isn't this what you want?
public class Program
{
public static void Main(string[] args)
{
try
{
throw new ConnectionLostException();
}
catch (Exception ex)
{
if (ex is LoginInfoException)
{
Console.WriteLine ("LoginInfoException");
}
else if (ex is ConnectionLostException)
{
Console.WriteLine ("ConnectionLostException");
}
}
}
}
public class LoginInfoException : WebException
{
public String Message { get; set; }
}
public class ConnectionLostException : WebException
{
public String Message { get; set; }
}
This is a simple example of a filter that will catch different exceptions. I don't know much about the hierarchy of exceptions you're dealing with, but this will allow you to filter what exceptions get caught where.
public class CatchExceptions
{
public void SomeMethod ()
{
try
{
//some stuff that throws exceptions
}
catch (WebException e) if (e is LoginInfoException)
{}
catch (WebException e) if (e is ConnectionLostException)
{}
}
}
Obviously you'll have to figure out what you can use to filter the exceptions like so; it appears that the two examples I used above are not concrete types. You may need to do some restructuring to figure out how to differentiate between the two.

Repeating code pattern

I have certain code pattern (which log performance and other variable for each function) which need to be present in every function, and I do not want to repeat the code over and over again. Here is what the code looks like:
public OutClass FUNC-X
{
if (IsDebugEnabled)
{
Logger("Start DataLibrary: FUNC-X");
}
try
{
CheckInitSucceeded();
GetAuthenticationTokens();
var dm = new Manager();
/**
* THIS SINGLE LINE IS THE VARIABLE PART
**/
var output = dm.FUNC-X(...);
if (IsDebugEnabled)
{
var data = Serialize(output);
Logger(output);
}
return output;
}
catch (WebFaultException)
{
throw;
}
catch (OtherException ex)
{
if (Logger.IsErrorEnabled)
{
Logger.LogError("Exception in FUNC-X", ex);
}
throw new OtherException("Some Message");
}
catch (Exception ex)
{
if (IsErrorEnabled)
{
Logger("Exception in FUNC-X", ex);
}
throw new Exception("Generic Exception");
}
finally
{
if (IsDebugEnabled)
{
Logger("End FUNC-X");
}
}
}
Essentially, I just need to replace FUNC-X with FUNC-Y or FUNC-Z everywhere this name occurs, is there some kind of design pattern which I can follow?
I'm sorry if the question is vague, I shall be happy to provide any details you ask.
Yeah, there are many ways to provide nice log code.
Use aspect-oriented programming. There is PostSharp and Spring.NET. You can use log4net library. All of them after config supports writing of method name.
You can use T4 and generate code for all funcs before compile.
You can write one global logging method that accepts Func<object> method and string methodName. Suppose you named your logging method LogAndExecute. Then to call you must write smth like:
LogAndExecute("func-x", () => dm.Func-X(/*your args*/))
If you got problems with different different return types of your funcs, use generics
You can use simple delegate to accept the variable part as parameter (if no. of parameters to function call in single line are same)
public void Function_X(Func<object,..> func)
{
if (IsDebugEnabled)
{
Logger("Start DataLibrary: FUNC-X");
}
try
{
CheckInitSucceeded();
GetAuthenticationTokens();
var dm = new Manager();
/**
* THIS SINGLE LINE IS THE VARIABLE PART
**/
// var output = dm.FUNC-X(...);
var output = func(...);
if (IsDebugEnabled)
{
var data = Serialize(output);
Logger(output);
}
return output;
}
catch (WebFaultException)
{
throw;
}
catch (OtherException ex)
{
if (Logger.IsErrorEnabled)
{
Logger.LogError("Exception in FUNC-X", ex);
}
throw new OtherException("Some Message");
}
catch (Exception ex)
{
if (IsErrorEnabled)
{
Logger("Exception in FUNC-X", ex);
}
throw new Exception("Generic Exception");
}
finally
{
if (IsDebugEnabled)
{
Logger("End FUNC-X");
}
}
}
You can create a common function that accepts a Func delegate:
static public TOutClass CommonFunc<TOutClass>(Func<Manager, TOutClass> func)
{
if (IsDebugEnabled)
{
Logger("Start DataLibrary: FUNC-X");
}
try
{
CheckInitSucceeded();
GetAuthenticationTokens();
var dm = new Manager();
TOutClass output = func(dm);
if (IsDebugEnabled)
{
var data = Serialize(output);
Logger(output);
}
return output;
}
catch
[...]
}
Your would write your functions as:
public OutClass FUNC-X(...)
{
return CommonFunc(dm=>dm.FUNC-X(...));
}

Passing parameters to an anonymous method using an action in C#

We are still having problems passing a parameter to an action. Here's what we have so far:
public ActionResult Create(string ds) {
HandleException(new Action<string, ref System.Web.Mvc.ModelState>(ds,ModelState) => {
InitializeServices(ds, "0000");
vm.Account = new Account {
PartitionKey = "0000",
RowKey = "0000",
Created = DateTime.Now,
CreatedBy = User.Identity.Name
};
});
return View("CreateEdit", vm);
}
private void HandleException(Action action) {
try {
action();
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
}
This gives 12 syntax errors and most are pointing to the line:
HandleException(new Action<string,
We have also have syntax errors saying that "ref" is not the correct syntax
Generic types expect types as their parameters... So Action requires actual types for each argument. "ref Type" is not a type. Ref is just a keyword.
As Jakub suggests, you should remove "ref" altogether and mutate your ModelState
The argument of HandleException is of type Action, but you call it with an object of type Action<string, ref System.Web.Mvc.ModelState>, which is a specialization of Action<T1, T2>. These two types must be the same, just choose one of them.
public ActionResult Create(string ds) {
HandleException(() => {
InitializeServices(ds, "0000");
vm.Account = new Account {
PartitionKey = "0000",
RowKey = "0000",
Created = DateTime.Now,
CreatedBy = User.Identity.Name
};
});
return View("CreateEdit", vm);
}
private void HandleException(Action action) {
try {
action();
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
}

Categories

Resources