I am currently refactoring an application which uses exceptions for logic flow. The code is difficult to read and maintain and makes a S.O.L.I.D fanboy like myself cry when reading it (not to mention the longest catch block I've ever seen in my career).
My question is what pattern(s) could you use to make it easier to maintain or how would you go about refactoring?
public void CallExternalServices(ICriteria criteria)
{
try
{
someResult = ExternalService1.SomeMethod(criteria);
}
catch (Service1Exception service1Exception)
{
if (criteria.SomeValue == "1")
{
if (service1Exception.InnerException != null
&& service1Exception.InnerException.InnerException != null
&& service1Exception.InnerException.InnerException is TargetSystemException)
{
TargetSystemException targetSystemException = (TargetSystemException)service1Exception.InnerException.InnerException;
if (targetSystemException.ErrorStatus.IsServiceDownForMaintenance())
{
// Call internal method to perform some action
SendNotification("Service down for maintenance.")
}
}
}
else if (criteria.SomeValue == "2")
{
if (service1Exception.InnerException != null
&& service1Exception.InnerException.InnerException != null
&& service1Exception.InnerException.InnerException is TargetSystemException)
{
TargetSystemException tx = (TargetSystemException)service1Exception.InnerException.InnerException;
if (targetSystemException.ErrorStatus.IsBusy())
{
// Call to some internal method to perform an action
SendDifferentNotification()
criteria.SetSomeFlagToBe = true;
try
{
someResult = ExternalService2.SomeOtherMethod(criteria);
}
catch (Service2Exception service2Exception)
{
if (service2Exception.InnerException != null
&& service2Exception.InnerException.InnerException != null
&& service2Exception.InnerException.InnerException is TargetSystemException)
{
TargetSystemException tx = (TargetSystemException)service1Exception.InnerException.InnerException;
if (targetSystemException.ErrorStatus.HasNumberOfDailyTransactionsBeenExceeded())
{
// Call internal method to perform some action
SendNotification("Number of daily transactions exceeded.")
}
}
else if (service2Exception.InnerException != null
&& service2Exception.InnerException.InnerException != null
&& service2Exception.InnerException.InnerException is FaultException)
{
FaultException faultException = service2Exception.InnerException.InnerException as FaultException;
if (faultException.Detail != null
&& faultException.Detail.FaultMessage.Equals("SomeValueToCheckAgainst", StringComparison.OrdinalIgnoreCase))
{
return someResult;
}
else
{
throw service2Exception;
}
}
else
{
throw service2Exception;
}
}
if (someResult != null)
{
// perform another action
SomeActionInvoker.ExecuteAcitonAgainst(someResult);
}
}
}
else if (service1Exception.InnerException != null
&& service1Exception.InnerException.InnerException != null
&& service1Exception.InnerException.InnerException is FaultException)
{
FaultException faultException = service1Exception.InnerException.InnerException as FaultException;
if (faultException.Detail != null
&& faultException.Detail.FaultMessage.Equals("SomeOtherValueToCheckAgainst", StringComparison.OrdinalIgnoreCase))
{
return someResult;
}
else
{
throw service1Exception;
}
}
else
{
throw service1Exception;
}
}
}
}
All in all I think you would be helped by breaking up some stuff into some helper methods. For example, you could extract your checks that look like this
if (<exception-instance>.InnerException != null &&
<exception-instance>.InnerException.InnerException != null &&
<exception-instance>.InnerException.InnerException is <exception-type>)
Into a boolean method; you call code like this at least 3 times by my cursory glance.
Also, I would recommend extracting that second top-level case into an error-handling method; and perhaps its nested if statements.
Check out Working Effectively with Legacy Code by Michael Feathers, particularly Chapter 22 (I Need to Change a Monster Method and I Can't Write Tests for It). There are a lot of great techniques in there for situations like yours. Personally, in cases like this I usually end up extracting methods from sections of the longer methods, and getting rid of local variables that are used throughout the method; these are almost always trouble.
Start by refactoring the method into multiple methods. You've got waaaaaay too many levels of indentation going on.
After that, consider if some of the new methods could be extracted into other objects, and use an IoC-style approach rather than a procedural one.
That's kind of a high level answer, but I'm tired and don't have the energy to actually rework the code myself :)
Related
Ok so here I have this function that can return a null value in some cases. However, even when testing if it is before, it still says it might be null.
My code:
if (Account.FindAccount(usernameInput) == null) { return; }
if (Account.FindAccount(usernameInput).password == "1234") // warning CS8602: Dereference of a possibly null reference
{
//Do stuff
}
Is that FindAccount operation guaranteed to be idempotent? The compiler has no such guarantee. What it returns in one call it might not return in another.
More to the point... If you believe it will always return the same result for the same input, why are you invoking it twice? Just invoke it once and store the result:
var account = Account.FindAccount(usernameInput);
if (account == null) { return; }
if (account.password == "1234")
{
//Do stuff
}
How do you make multiple component references in a single if/then logic statements in Unity? I thought it would look something like this:
if(other.GetComponent<Component1>() != null || (GetComponent2>() != null)
{
//Run code
}
Such code returns this error:
error CS1026: ) expected
You are missing a get for the second component and a close )
if(other.GetComponent<Component1>() != null || other.GetComponent<Component2>() != null)
{
//Run code
}
With out knowing more, this is a start
I have this code snippet here:
public void ReDrawParallelLines(string lineName, string viewType)
{
var referenceLineOne = GetLineParams(viewType + ReferenceEnum.One.ToString() + linename);
var referenceLineTwo = GetLineParams(viewType + ReferenceEnum.Two.ToString() + linename);
if (lineName == referenceLineOne.lineParams.lineName)
{
//Do certain action with referencelineone
}
else if (lineName == referenceLineTwo.lineParams.lineName)
{
//Do same action but with referencelinetwo
}
}
I noticed that if referenceLineOne is null but I have referenceLineTwo, the else statement never gets executed. I'm not sure why? Doesn't it work such that if the bool fails the if then continue to the else and it should pass for the else. It just skips the inside if statement and the else condition entirely because the referenceLineOne is null. Why and how can I correct this check?
Basically, I am passing a line name and I want to check to see if it's equal to one of two lines I get from the GetLineParams function.
Since referenceLineOne is null, you will get an exception, which is why it bypasses the else if and jumps somewhere else.
You should do null checking like this
if (referenceLineOne != null && lineName == referenceLineOne.lineParams.lineName)
{
//Do certain action with referencelineone
}
or this if you use c#6
if (lineName == referenceLineOne?.lineParams.lineName)
{
//Do certain action with referencelineone
}
I'm currently developing an app for windows phone 8.1 with Azure. (I wouldn't really recommend this to you after my experiences). Anyway... I've got Multiple Controller classes in it who really do the same (Check if something in my database already exists and if not it creates it). The problem I have has to be in the Read() function which checks the database if an entry already exists:
public async void Read(Device device)
{
IMobileServiceTable mobileServiceTable = Connect();
MobileServiceCollection<Device, Device> devices = null;
try
{
devices = await mobileServiceTable.MobileServiceClient.GetTable<Device>().Where(d => d.Manufacturer == device.Manufacturer && d.Model == device.Model).ToCollectionAsync();
}
catch (Exception e)
{
if (_onDeviceControllerListener != null)
{
_onDeviceControllerListener.OnError(ControllerError.Error.ReadFromDatabase, e.ToString());
}
return;
}
if (_onDeviceControllerListener != null && devices != null)
{
_onDeviceControllerListener.OnRead(devices);
}
}
This one works perfectly how it should but the oder one which is basically just a copy throws a NullReferenceException by the line "apps = await mobileServiceTab...":
public async void Read(Model.App app)
{
IMobileServiceTable mobileServiceTable = Connect();
MobileServiceCollection<Model.App, Model.App> apps = null;
try {
apps = await mobileServiceTable.MobileServiceClient.GetTable<Model.App>().Where(a => a.HardwareId == app.HardwareId && a.PackageId == app.PackageId).ToCollectionAsync();
}
catch (Exception e)
{
if (_onAppControllerListener != null)
{
_onAppControllerListener.OnError(ControllerError.Error.ReadFromDatabase, e.ToString());
}
return;
}
if (_onAppControllerListener != null)
{
_onAppControllerListener.OnRead(apps);
}
}
Does somebody know what the problem is?
Thanks for helping
You can add a try...catch block and check for the "MobileServiceInvalidOperationException" exception. Also take a look at the callstack to find out what's happening.
A fiddler trace would also let you know if you made a successful call to the Mobile Service to retrieve the data. Then you can determine if the issue lies in how you handle the data in your code.
Take another look at your MobileServiceContext class and ensure all properties are accounted for especially the properties that use the "Model.App" class. E.g
public virtual DbSet<Model.App> SomeProperty { get; set; }
Hope this helps.
Code below is placed in page_Load. How I should handle this to bypass UrlReferrer when you enter page directly first time and there is no referrer?
What I am missing here?
if (HttpContext.Current.Request.UrlReferrer.AbsoluteUri != null)
{
urlReferer = HttpContext.Current.Request.UrlReferrer.AbsoluteUri.ToString();
}
else
{
urlReferer = "";
}
Just check UrlReferrerfor null:
if (HttpContext.Current.Request.UrlReferrer != null
&& HttpContext.Current.Request.UrlReferrer.AbsoluteUri != null)
{
urlReferer = HttpContext.Current.Request.UrlReferrer.AbsoluteUri.ToString();
}
else
{
urlReferer = "";
}
Who says the client passed by the referrer in the HTTP request?
Check if UrlReferrer is null first
if (HttpContext.Current.Request.UrlReferrer != null)
{
urlReferer = HttpContext.Current.Request.UrlReferrer.AbsoluteUri.ToString();
}
else
{
urlReferer = "";
}
Why not this way much cleaner than checking nulls
private void Page_Load()
{
if (!IsPostBack)
{
if (HttpContext.Current.Request.UrlReferrer != null)
{
urlReferer = HttpContext.Current.Request.UrlReferrer.AbsoluteUri.ToString();
}
else
{
urlReferer = "";
}
}
}
I believe you need to check if HttpContext.Current.Request.UrlReferrer != null.
If UrlReferrer is null, then the test to AbsolutUri will fail.
Try initially testing UrlReferrer for null, this will probably correct the issue.
Use your debugger. If you're running this out of visual studio, than you might be brought to a debugger window when the exception is thrown. There are multiple tabs at the bottom of the debugger including "Locals" and "Watch" you can use those to see what variables are being stored.
If the above code is indeed what's causing the problem than
HttpContext.Current.Request.UrlReferrer.AbsoluteUri
or
HttpContext.Current.Request.UrlReferrer
or
HttpContext.Current.Request
or
HttpContext.Current
or
HttpContext
is set to null