Categories

Thursday, May 12, 2016

Announcing .NET Framework 4.6.2

Today we are excited to announce the availability of the .NET Framework 4.6.2! Many of the changes are based on your feedback, including those submitted on UserVoice and Connect. Thanks for your continued help and engagement!
The release is packed with lots of great improvements in the following areas:
You can see the full set of changes in the .NET Framework 4.6.2 change list and API diff.

Download Now

You can download the .NET Framework 4.6.2 now:

Base Class Library (BCL)

The following improvements have been made in the BCL.

Long Path Support (MAXPATH)

We fixed the 260 character (MAXPATH) file name length limitation in the System.IO APIs. Over 4500 of you voted for this issue on UserVoice!
This limitation doesn’t usually affect consumer applications (for example, loading files out of “My Documents”), but is more common on developer machines that build deeply nested source trees or use specialized tools that also run on Unix (where long paths are much more common).
This new capability is enabled for applications that target the .NET Framework 4.6.2 (or later). You can configure an application to target the .NET Framework 4.6.2 with the following app.config or web.config configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version=".NETFramework,Version=v4.6.2"/>
</startup>
</configuration>
You can opt applications that target an earlier version of the .NET Framework into using this functionality by setting an AppContext switch, as demonstrated in the following configuration file. The switch will only be honored when an application in running on the .NET Framework 4.6.2 (or later).
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version=".NETFramework,Version=v4.5.2"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false" />
</runtime>
</configuration>
The absence of targeting the .NET Framework 4.6.2 or setting the AppContext switch results in the existing behavior of being blocked from using paths longer than MAXPATH. The behavior is opt-in to maintain backwards compatibility for existing applications.
The following improvements were made to enable long paths:
  • Allow paths that are greater than 260 character (MAX_PATH). Paths that are longer than MAX_PATH are allowed by the BCL. The BCL APIs rely on the underlying Win32 file APIs for limitation checks.
  • Enable extended path syntax and file namespaces (\\?\, \\.\). Windows exposes multiple file namespaces that enable alternate path schemes, such as the extended path syntax, which allows paths to just over 32k characters. The BCL now supports these paths, such as the following: \\?\very long path. The .NET Framework now primarily relies on Windows for path normalization, treating it as the “source of truth”, to avoid inadvertently blocking legitimate paths. The extended path syntax is a good workaround for Windows versions that don’t support long paths using the regular form (for example, `C:\very long path’).
  • Performance Improvements. The adoption of Windows path normalization and the reduction of similar logic in the BCL has resulted in overall performance improvements for logic related to file paths. Other related performance improvements have also been made.
More details on these changes can be found on Jeremy Kuhne’s blog.

X509 Certificates Now Support FIPS 186-3 Digital Signature Algorithm

The .NET Framework 4.6.2 adds support for FIPS 186-3 Digital Signature Algorithm (DSA). This support enables X509 certificates with keys that exceed 1024-bit. It also enables computing signatures with the SHA-2 family of hash algorithms (SHA256, SHA384, and SHA512).
The .NET Framework 4.6.1 supports FIPS 186-2, which is limited to keys no greater than 1024-bit.
You can take advantage of FIPS 186-3 support by using the new DSACng class, as you can see in the example below.
The DSA base class has also been updated so you can you use FIPS 186-3 support without casting to the new DSACng class. This is the same as the approach that was used for updating RSA and ECDsa implementations, in the two previous .NET Framework releases.

Improved Usability of Elliptic Curve Diffie-Hellman Key Derivation Routines

The usability of the ECDiffieHellmanCng class has been improved. The .NET Framework Elliptic Curve Diffie-Hellman (ECDH) Key Agreement implementation includes three different Key Derivation Function (KDF) routines. These KDF routines are now represented and supported by three different methods, as you can see in the example below.
In previous .NET Framework versions, you had to know which subset of properties to set on the ECDiffieHellmanCng class for each of the three different routines.

Support for Persisted-Key Symmetric Encryption

The Windows Cryptography Library (CNG) supports storing persisted symmetric keys on software and hardware devices. The .NET Framework now exposes this CNG capability, as you can see demonstrated in the example below.
You need to use the concrete implementation classes, such as AesCng to use this new capability, as opposed to the more common factory approach, Aes.Create(). This requirement is due to key names and key providers being implementation-specific
Persisted-key symmetric encryption has been added for the AES and 3DES algorithms, in the AesCng TripleDESCng classes, respectively.

SignedXml Support for SHA-2 Hashing

The .NET Framework SignedXml implementation now supports the following SHA-2 Hashing algorithms:
You can see an example of signing XML with SHA-256 in the example below.
The new SignedXML URI constants have been added as new SignedXml fields. The new fields are shown below.
Any programs which have registered a custom SignatureDescription handler into CryptoConfig to add support for these algorithms will continue to function as they did in the past, but since there are now platform defaults the CryptoConfig registration should no longer be necessary.

Common Language Runtime (CLR)

The following improvements have been made in the CLR.

NullReferenceException Improvements

You have probably experienced and investigated the cause of a NullReferenceException. We are part-way through partnering with the Visual Studio team to provide a better debugging experience for null references in a future Visual Studio release.
The debugging experience in Visual Studio relies on the Common Language Runtime debugging APIs for low-level interaction with your code. Today, the NullReferenceException experience in Visual Studio looks like this:
null_ref
In this release, we extended the CLR debugging APIs to enable the debugger to request more information and perform additional analysis when a NullReferenceException occurs. Using this information, a debugger will be able to determine which reference is null and provide this information to you, making your job easier.

ClickOnce

The following improvements have been made in ClickOnce.

Transport Layer Security (TLS) 1.1 and 1.2 Support

We added support for TLS 1.1 and 1.2 protocols in ClickOnce for .NET Framework versions 4.6.2, 4.6.1, 4.6 and 4.5.2. We would like to thank those who voted for it on UserVoice! You do not need to do any extra steps to enable TLS 1.1 or 1.2 support as ClickOnce will automatically detect which TLS protocol is required at runtime.
Secure Sockets Layer (SSL) and TLS 1.0 are no longer recommended or supported by some organizations. For example, the Payment Card Industry Security Standards Council is in the process of requiring TLS 1.1 or higher for online transactions that meet their specifications.
ClickOnce continues to support TLS 1.0 for applications that do not or cannot upgrade, for compatibility. We recommend analyzing all of your uses of SSL and TLS 1.0. See the KB articles for links to download the hotfix .NET Framework versions 4.6, 4.6.1 and 4.5.2.

Client Certificate Support

ClickOnce applications can now be hosted in virtual directories with SSL enabled and with client certificates required. In that configuration, end users will be prompted to select their certificate when accessing an application. ClickOnce will not prompt for a certificate if the Client Certificates setting is set to “Ignore”.
In previous versions, ClickOnce deployments were terminated with an access denied error when applications where hosted this way.
clickonce_ssl

ASP.NET

The following improvements have been made in ASP.NET. See Announcing ASP.NET Core 1.0 to learn about improvements in ASP.NET Core.

DataAnnotation Localization

Localization is now much easier when using model binding and DataAnnotiation validation. ASP.NET has adopted a simple convention for resx resource files that contain DataAnnotation validation messages:
  • Located in the App_LocalResources folder.
  • Follow the DataAnnotation.Localization.{locale}.resx naming convention.
Using the .NET Framework 4.6.2, you specify DataAnnotation attributes in your model file just like you would in an un-localized application. For the ErrorMessage, you specify the name that you will use in resx file, as you can see in the example below:
public class ContactInfo
{
[Required(ErrorMessage = "Your email address is invalid")]
[Display(Name = "User Email")]
public int Email { get; set; }
[Required(ErrorMessage = "Your phone number is invalid")]
[Display(Name = "User Phone")]
public int Phone { get; set; }
}
asp.net_dataAnnotation_localization
You can see that localized resx files have been placed in the ‘App_LocalResources’ folder, following the new convention, in the example below:
asp.net_dataAnnotation
You can also plug in your own stringlocalizer provider to store the localized strings in another location or file type.
In previous .NET Framework versions, you would need to specify ErrorMessageResourceType and ErrorMessageResourceNamevalues, as you can see in the example below.
public class User
{
[Required(ErrorMessageResourceType = typeof(ModelResources),
ErrorMessageResourceName = "FirstName_Required")]
[StringLength(50, ErrorMessageResourceType = typeof(ModelResources),
ErrorMessageResourceName = "FirstName_StringLength")]
public sstring FirstName { get; set; }
[Required(ErrorMessageResourceType = typeof(ModelResources),
ErrorMessageResourceName = "LastName_Required")]
[StringLength(50, ErrorMessageResourceType = typeof(ModelResources),
ErrorMessageResourceName = "LastName_StringLength")]
public sstring LastName { get; set; }
}

Async Improvements

SessionStateModule and Output-Cache Module have been improved to enable async scenarios. The team is working on releasing async versions of both modules via NuGet, which will need to be imported into an existing project. Both NuGet packages are anticipated to release within the coming weeks. We will update this post when that happens.

SessionStateModule Interfaces

Session State allows you to store and retrieve user session data as a user navigates an ASP.NET site. You can now create your own async Session State Module implementation using the new ISessionStateModule interface, enabling you to store session data in your own way and use async methods.

Output-Cache Module

Output Caching can dramatically improve the performance of an ASP.NET application by caching the result returned from a controller action to avoid unnecessarily generating the same content for every request.
You can now use async APIs with Output Caching by implementing a new interface called OutputCacheProviderAsync. Doing so will reduce thread-blocking on a web server and improve scalability of an ASP.NET service.

SQL

The following improvements have been made in the SQL client.

Always Encrypted Enhancements

Always Encrypted is a feature designed to protect sensitive data, such as credit card numbers or national identification numbers that are stored in a database. It allows clients to encrypt sensitive data inside client applications, never revealing the encryption keys to the database engine. As a result, Always Encrypted provides a separation between those who own the data (and can view it) and those who manage the data (but should have no access).
The .NET Framework Data Provider for SQL Server (System.Data.SqlClient) introduces two important enhancements for Always Encrypted around performance and security.

Performance

To improve performance of parameterized queries against encrypted database columns, encryption metadata for query parameters is now cached. Database clients retrieve parameter metadata from the server only once when theSqlConnection::ColumnEncryptionQueryMetadataCacheEnabled property is set to true (the default), even if the same query is called multiple times.

Security

Column encryption key entries in the key cache are now evicted after a configurable time interval. The time interval can be set using the SqlConnection::ColumnEncryptionKeyCacheTtl property.

Windows Communication Foundation (WCF)

The following improvements have been made in WCF.

NetNamedPipeBinding Best Match

In .NET 4.6.2, NetNamedPipeBinding has been enhanced to support a new pipe lookup, known as “Best Match”. When using “Best Match”, the NetNamedPipeBinding service will force clients to search for the service listening at the best matching URI to their requested endpoint, rather than the first matching service found.
The “Best Match” pipe is particularly useful if a WCF client app tries to connect to the wrong URI when using the default “First Match” behavior. In certain situations, when there is more than one WCF Services listening on named pipes, WCF clients using “First Match” could be connected to a wrong service. This could happen if some of the services is hosted by an administrator account.
To enable this feature, you can add the following AppSetting to your client application’s App.config or Web.config file:
<configuration>
<appSettings>
<add key="wcf:useBestMatchNamedPipeUri" value="true" />
</appSettings>
</configuration>

DataContractJsonSerializer Improvements

The DataContractJsonSerializer has been improved to better support multiple daylight saving time adjustment rules. When enabled, DataContractJsonSerializer will use the TimeZoneInfo class instead of the TimeZone class. The TimeZoneInfo class supports multiple adjustment rules, which makes it possible to work with historic time zone data. This is useful when a time zone has different daylight saving time adjustment rules, such as (UTC+2) Istanbul.
You can enable this feature by adding the following AppSetting to the app.config file:
<runtime>
<AppContextSwitchOverrides value="Switch.System.Runtime.Serialization.DoNotUseTimeZoneInfo=false" />
</runtime>

TransportDefaults No Longer Supports SSL 3

The SSL 3 protocol is no longer a default protocol used for negotiating a secure connection when using NetTcp with transport security and a credential type of certificate. In most cases there should be no impact to existing applications, since TLS 1.0 has always been included in the default protocol list for NetTcp. All existing clients should be able to negotiate a connection using at least TLS 1.0.
SSL3 was removed as a default protocol since it is not longer considered secure. While not recommended, one of the following configuration mechanisms can be used to add SSL 3 back to the list of negotiated protocols if it is required for your deployment:

Transport Security for Windows Cryptography Library (CNG)

Transport Security now supports certificates stored using the Windows cryptography library (CNG). Currently, this support is limited to using certificates with a public key which has an exponent no more than 32bits in length.
This new capability is enabled for applications that target the .NET Framework 4.6.2 (or later). You can configure an application to target the .NET Framework 4.6.2 with the following app.config or web.config configuration file:
You can opt applications that target an earlier version of the .NET Framework into using this functionality by setting an AppContext switch, as demonstrated in the following configuration file. The switch will only be honored when an application in running on the .NET Framework 4.6.2 (or later).
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version=".NETFramework,Version=v4.5.2"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableCngCertificates=false" />
</runtime>
</configuration>
You can also enable this functionality programmatically:
private const string DisableCngCertificates = @"Switch.System.ServiceModel.DisableCngCertificates";
AppContext.SetSwitch(DisableCngCertificates, false);

OperationContext.Current Async Improvements

WCF now has the ability to include OperationContext.Current with ExecutionContext so that the OperationContext flows through asynchronous continuations. With this improvement, WCF allows CurrentContext to propagate from one thread to another thread. This means that even if there’s a context switch between calls to OperationContext.Current, it’s value will flow correctly throughout the execution of the method.
The following example demonstrates OperationContext.Current flowing correctly across a thread transition:
public async Task InvokeCallbackWithDelay(int delay)
{
int threadId1 = Thread.CurrentThread.ManagedThreadId;
OperationContext.Current.GetCallbackChannel<IServiceCallback>().TheCallback("One");
await Task.Delay(delay);
int threadId2 = Thread.CurrentThread.ManagedThreadId;
OperationContext.Current.GetCallbackChannel<IServiceCallback>().TheCallback("Two");
}
Previously, the internal implementation of OperationContext.Current was to store the CurrentContext using a ThreadStatic variable, which used the thread’s local storage to store the data associated with CurrentContext. If there was a change in the execution context of the method call (i.e. a thread change caused by awaiting another operation), any subsequent calls would be operating on a different thread without a reference to the original value. With the fix, the second call to OperationContext.Current will deliver the expected value even though threadId1 and threadId2 may be different.

Windows Presentation Foundation (WPF)

The following improvements have been made in WPF.

Group Sorting

An application that requests a CollectionView to group data can now explicitly declare how to sort the groups. This overcomes some unintuitive ordering that can arise when the application dynamically adds or removes groups, or when the application changes the value of item properties involved in grouping. It can also improve the performance of the group creation process, by moving comparisons of the grouping properties from the sort of the full collection to the sort of the groups.
The feature includes two new properties on the GroupDescription class: SortDescriptions and CustomSort. These properties describe how to sort the collection of groups produced by the GroupDescription, analogous to the way the properties onListCollectionView with the same names describe how to sort the data items. There are also two new static properties on the PropertyGroupDescription class for use in the most common cases: CompareNameAscending and CompareNameDescending.
For example, suppose an application wants to group data by Age, sorting the groups in ascending order and the items within each group by LastName.
With this new feature the application can now declare:
<GroupDescriptions>
<PropertyGroupDescription
PropertyName=”Age”
CustomSort=
”{x:Static PropertyGroupDescription.CompareNamesAscending}”/>
</PropertyGroupDescription>
</GroupDescriptions>
<SortDescriptions>
<SortDescription PropertyName=”LastName”/>
</SortDescriptions>
Prior to this feature, the application would declare:
<GroupDescriptions>
<PropertyGroupDescription PropertyName=”Age”/>
</GroupDescriptions>
<SortDescriptions>
<SortDescription PropertyName=”Age”/>
<SortDescription PropertyName=”LastName”/>
</SortDescriptions>

Per-Monitor DPI Support

WPF applications are now enabled for per-monitor DPI awareness. This improvement is critical for scenarios where multiple displays of varying DPI level are attached to a single machine. As all or part of a WPF application is transitioned between monitors, the expected behavior is for WPF to automatically match the DPI of the app to the screen. It now does.
You can learn more about how to enable your WPF application to become per-monitor DPI aware in the WPF samples and developer guide on GitHub.
In previous versions, you would have to write additional native code to enable per-monitor DPI awareness in WPF applications.

Soft Keyboard Support

Soft Keyboard support enables automatic invocation and dismissal of the touch keyboard in WPF applications without disabling WPF stylus/touch support on Windows 10.
In previous versions, WPF applications did not implicitly support the invocation or dismissal of the touch keyboard without disabling WPF stylus/touch support. This is due to a change in the way the touch keyboard tracks focus in applications starting in Windows 8.

softkeyboard

Tuesday, April 5, 2016

4 Reasons To Drop MVVM1

The MVVM design pattern has been around for quite a while now.  It has a lot of strengths when done correctly. But, I believe the time has come to recognize that MVVM has a lot of shortcomings that point to its demise.  Since I primarily develop web applications, I will keep this discussion centered on the use of MVVM in web applications.  The use of MVVM for desktop may or may not have these same issues.
I realize that for some of you, the very suggestion of dropping MVVM will invoke a negative emotional response.  Some very smart people have quit their job at the suggestion that MVVM and its close cousin two-way data-binding, be abandoned in favor of another way.  But just for a few minutes, I would like for you to stop treating programming as a religion and consider the possibility that there may be a better way.
image

History of MVVM

MVVM was originally created by John Gossman to support the XAML syntax used to create Windows™ desktop applications and Silver Light applications.  Its main advantage has always been that it provides an easy way to decouple the View code from any business logic that might need to run.  Because of this decoupling, our applications become much easier to unit test.
The next major implementation of MVVM that I can remember is Knockout.  It is the knockout framework that introduced me to MVVM and I have to say it is also the only one I feel like actually got it right.  By that I mean that it actually did what it was advertised to do.  Maybe that’s because of all the implementations I’ve used, Knockout is the only one that ONLY implemented MVVM rather than making it part of a larger framework.

Definition of MVVM

I’ve written about MVVM before where I’ve explained more completely what MVVM is.  But just so we have a working definition of what I mean when I talk about MVVM, let’s define it this way.
MVVM is a design pattern that uses two-way data-binding to get data in and out of the presentation layer, referred to as the View1, without the programmer needing to do any more than specifying that this should happen in the view.  MVVM is also able to have data elements and functions in the “ViewModel” track changes so that anything that is dependent on other data is automatically recalculated without the programmer having to write a lot of code to make this happen.  This creates a model that is able to respond to events in the view, but is primarily data centric rather than event centric has we have often reasoned about our applications in the past.
It sounds great.  And when it works it is.  But that’s the main problem, it hardly ever works well.

MVVM Done Right is Slow

If you’ve had any experience or paid any attention to the implementation of MVVM using JavaScript, you will realize that the number one problem with MVVM is that it is a memory hog and performs poorly for all but the most trivial of applications.  In fact, for all of the popularity of Angular JS, the biggest complaint has been around the implementation of the data-binding.  In a large application, you might need to loop through the data multiple times to make sure it has all recalculated correctly.  If you just use the framework and let the framework deal with your sloppy code, this can make the system incredibly slow.  If you actually pay attention to what you are doing, it takes longer to implement than if you had chosen some other design pattern.
But doing that means we have not moved on to…

MVVM is Hard to Implement

Recognizing that looping through the data until it stabilizes may not be a good idea, the framework designers have developed rules such as, “We’ll only run the digest cycle once.”  and “We’ll only run it when some user interaction has occurred.”  Well, OK.  That sounds good.  At least now it will be obvious that I have a problem.  But this is where the trouble begins.  If I can’t rely on my data, and ultimately my view, responding to changes in my data correctly, I am left with having to only partially implementing MVVM so that I can work around these limitations and using other means to make sure my view is updated correctly.
This is to say nothing of many frameworks just not working as you would expect them to.

MVVM is Hard to Reason About

Again, in all but the most trivial of applications, and because of the optimizations that various frameworks have tried to implement, MVVM becomes difficult to implement.  As I’ve tried to explain MVVM to others and even as I’ve tried to implement it myself, I’ve found that the simple act of keeping the view stuff in the view layer and the data stuff in the data layer and making sure it all updates appropriately has me, at times tearing my hair out.  Many times this is caused by incomplete implementations.  But if being hard to implement means it is hard to reason about, maybe we shouldn’t be using it to begin with.

MVVM is Overkill

But it does work sometimes.  In really simple CRUD applications, it works great.  None of the problems I’ve mentioned.  And this is the great seduction of MVVM.  You try it on some small application and you get excited.  Like a gateway drug, it lures you in.  And when you finally go to implement it on some larger application, you find out that it really doesn’t scale all that well.  And on that small app you tried it on, couldn’t you have done that just as easily using another design pattern?

Where to Go from Here

As I was reviewing these arguments with a co-worker this week, he asked, “Are you saying we shouldn’t be using MVVM?”  And my answer might surprise you.
I said, “Given the two models we have to work within the framework we are currently using, MVVM is the best choice.”
However, what we might want to consider is moving to another framework that provides a better design pattern.  There are several “One-way” design patterns that intrigue me.  The first is the basic Flux pattern that React tends to use.  Done correctly, this uses events to achieve the decoupling we all should be striving for.  At its core, it is basic MVC.
The second one, which is very flux like, is RxJS.  I’m still wrapping my head around how I to use it in an application and honestly don’t know enough about it at this point to say any more than that it looks interesting.
And even if we decided to move away from MVVM, I think using two way data-binding between the view and the ViewModel is good.  I just think the ViewModel shouldn’t try to re-compute the values as part of what it does. Leave that to the developer to control.  The problem is, trying to get existing systems that implement two-way data-binding to only work at that level would not work correctly.

Saturday, March 12, 2016

C# BAD PRACTICES: Learn how to make a good code by bad example

A purpose of this article is to show how to write a clean, extendable and maintainable code by showing example of badly written class. I will explain what troubles could it bring and present a way how to replace it by a better solution – using good practices and design patterns.

First part is for every developer who knows C# language basics - it will show some basic mistakes and techniques how to make code readable like a book. Advanced part is for developers who have at least basic understanding of design patterns – it will show completely clean, unit testable code.

To understand this article you need to have at least basic knowledge of:

    C# language
    dependency injection, factory method and strategy design patterns

Example described in this article is a concrete, real world feature – I won't show examples like build pizza using decorator pattern or implement calculator using strategy pattern Smile | :)

                                                          

As those theoretical examples are very good for explanation I found extremely difficult to use it in a real production applications.

We hear many times don't use this and use that instead. But why? I will try to explain it and prove that all the good practices and design patterns are really saving our lives!


Note:

    I will not explain C# language features and design patterns (it would make this article too long), there are so many good theoretical examples in the network.  I will concentrate to show how to use it in our ever day work

    Example is extremely simplified to highlight only described issues – I’ve found difficulties in understanding a general idea of article when I was learning from examples which were containing tones of code.

    I’m not saying that shown by me solutions for described below problems are the only one solutions, but for sure are working and making from your code high quality solution.

    I don't care in below code about error handling, logging etc. Code is written only to show solution for common programming problems.



Let’s go to concretes...
So badly written class...

Our real world example will be below class:
Hide   Copy Code

public class Class1
{
  public decimal Calculate(decimal ammount, int type, int years)
  {
    decimal result = 0;
    decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100;
    if (type == 1)
    {
      result = ammount;
    }
    else if (type == 2)
    {
      result = (ammount - (0.1m * ammount)) - disc * (ammount - (0.1m * ammount));
    }
    else if (type == 3)
    {
      result = (0.7m * ammount) - disc * (0.7m * ammount);
    }
    else if (type == 4)
    {
      result = (ammount - (0.5m * ammount)) - disc * (ammount - (0.5m * ammount));
    }
    return result;
  }
}

It is a really bad guy. Can we imagine what is the role of the above class? It is doing some wired calculating? That's all we can say about it for now…

Now imagine that it is a DiscountManager class which is responsible for calculating a discount for the customer while he is buying some product in online shop.

- Come on? Really?
- Unfortunately Yes!

It is completely unreadable, unmaintainable, unextendable and it is using many bad practices and anti-patterns.

What exact issues do we have here?

    Naming – we can only guess what does this method calculate and what exactly is the input for these calculations. It is really hard to extract calculation algorithm from this class.
    Risks:
    the most important thing in this case is – time wasting
                                                              
    , if we will get enquiry from business to present them algorithm details or we will have a need to modify this piece of code it will take us ages to understand logic of our Calculate method. And if we won’t document it or refactor the code, next time we/other developer will spend the same time to figure out what exactly is happening there. We can make very easily mistake while modifying it as well.

    Magic numbers
                                                   
    In our example the type variable means - status of the customer account. Could you guess that? If-else if statements make a choice how to calculate price of the product after discount.
    Now we don’t have an idea what kind of account is 1,2,3 or 4. Let’s imagine now that you have to change algorithm of giving discount for ValuableCustomer account You can try to figure out it from the rest of the code – what will take you a long time but even though we can very easily make a mistake and modify algorithm of BasicCustomer account – numbers like 2 or 3 aren’t very descriptive. After our mistake customers will be very happy because they will be getting a discount of valuable customers Smile | :)

    Not obvious bug
    Because our code is very dirty and unreadable we can easily miss very important thing. Imagine that there is a new customer account status added to our system - GoldenCustomer. Now our method will return 0 as a final price for every product which will be bought from a new kind of an account. Why? Because if none of our if-else if conditions will be satisfied (there will be unhandled account status) method will always return 0. Our boss is not happy – he sold many products for free before somebody realized that something is wrong.

    Unreadable
    We all have to agree that our code is extremely unreadable.
    Unreadable = more time for understanding the code + increasing risk of mistake.

    Magic numbers - again
    Do we know what numbers like 0.1, 0.7, 0.5 mean? No we don't, but we should if we are owners of the code.
    Let’s imagine that you have to change this line:
    result = (ammount - (0.5m * ammount)) - disc * (ammount - (0.5m * ammount));

    as the method is completely unreadable you change only first 0.5 to 0.4 and leave second 0.5 as is. It can be a bug but it also can be a fully proper modification. It’s because 0.5 does not tell us anything.
    The same story we have in case of converting years variable to disc variable:
    decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100;
    It is calculating discount for time of having account in our system in percentage. Ok, but what the hell is 5? It is a maximum discount in percentage which customer can get for loyalty. Could you guess that?

    DRY – Don’t repeat yourself
    It is not visible for the first look but there are many places in our method where the code is repeated.
    For example:
    disc * (ammount - (0.1m * ammount));

    is the same logic as:
    disc * (ammount - (0.5m * ammount))

    there is only static variable which is making a difference – we can easily parametrize this variable.
    If we won’t get rid of duplicated code we will come across situations where we will only do a part of task because we will not see that we have to change in the same way for example 5 places in our code. Above logic is calculating a discount for years of being a customer in our system. So if we will change this logic in 2 of 3 places our system will become inconsistent.

    Multiple responsibilities per class
    Our method has at least 3 responsibilities:
    1. Choosing calculation algorithm,
    2. Calculate a discount for account status,
    3. Calculate a discount for years of being our customer.
    It violates Single Responsibility Principle. What risk does it bring? If we will need to modify one of those 3 features it will affect also 2 other. It means that it could break something in features which we didn’t want to touch. So we will have to test all class again – waste of time.


Refactoring...

In 9 below steps I will show you how we can avoid all described above risks and bad practices to achieve a clean, maintainable and unit testable code which will be readable like a book.


I STEP – Naming, naming, naming

It's IMHO one of the most important aspect of a good code. We've only changed names of method, parameters and variables and now we exactly know what below class is responsible for.
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, int accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
    if (accountStatus == 1)
    {
      priceAfterDiscount = price;
    }
    else if (accountStatus == 2)
    {
      priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
    }
    else if (accountStatus == 3)
    {
      priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
    }
    else if (accountStatus == 4)
    {
      priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
    }

    return priceAfterDiscount;
  }
}

However we still don't know what 1, 2, 3, 4 mean, let's do something with it!
II STEP – Magic numbers

One of techniques of avoiding magic numbers in C# is replacing it by enums. I prepared AccountStatuses enum to replace our magic numbers in if-else if statements:
Hide   Copy Code

public enum AccountStatuses
{
  NotRegistered = 1,
  SimpleCustomer = 2,
  ValuableCustomer = 3,
  MostValuableCustomer = 4
}

Now look at our refactored class, we can easily say which algorithm of calculating discount is used for which account status. Risk of mixing up Account Statuses decreased rapidly.
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;

    if (accountStatus == AccountStatuses.NotRegistered)
    {
      priceAfterDiscount = price;
    }
    else if (accountStatus == AccountStatuses.SimpleCustomer)
    {
      priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
    }
    else if (accountStatus == AccountStatuses.ValuableCustomer)
    {
      priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
    }
    else if (accountStatus == AccountStatuses.MostValuableCustomer)
    {
      priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
    }
    return priceAfterDiscount;
  }
}

III STEP – more readable

In this step we will improve readability of our class by replacing if-else if statement with switch-case statement.

I also divided long one-line algorithms into two separate lines. We have now separated “calculation of discount for account status” from “calculation of discount for years of having customer account”.

For example, line:
priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));

was replaced by:
priceAfterDiscount = (price - (0.5m * price));
priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage ​​​​​​​* priceAfterDiscount);

Below code presents described changes:
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = (price - (0.1m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = (0.7m * price);
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = (price - (0.5m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
    }
    return priceAfterDiscount;
  }
}

IV STEP - Not obvious bug

We finally got to our hidden bug!
As I mentioned before our method AddDiscount will return 0 as final price for every product which will be bought from new kind of account. Sad but true..

How can we fix it? By throwing NotImplementedException!

                                                      

You will think - Isn't it exception driven development? No, it isn't!

When our method will get as parameter value of AccountStatus which we didn't support we want to be noticed immediately about this fact and stop program flow not to make any unpredictable operations in our system.

This situation SHOULD NOT HAPPEN NEVER so we have to throw exception if it will occur.

Below code was modified to throw an NotImplementedException if none conditions are satisfied – the default section of switch-case statement:
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = (price - (0.1m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = (0.7m * price);
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = (price - (0.5m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      default:
        throw new NotImplementedException();
    }
    return priceAfterDiscount;
  }
}

V STEP - Lets analyse calculations

In our example we have two criteria of giving a discount for our customers:

    Account status
    Time in years of having an account in our system.

All algorithms look similar in case of discount for time of being a customer:
(discountForLoyaltyInPercentage​​​​​​​ * priceAfterDiscount)


,but there is one exception in case of calculating constant discount for account status:
0.7m * price

so let's change it to look the same as in other cases:
price - (0.3m * price)
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = (price - (0.1m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = (price - (0.3m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = (price - (0.5m * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      default:
        throw new NotImplementedException();
    }
    return priceAfterDiscount;
  }
}

Now we have all the rules which are calculating a discount according to account status in one format:
price - ((static_discount_in_percentages/100) * price)
VI STEP - Get rid of magic numbers – another technique

Let's look at static variable which is a part of discount algorithm for account status:(static_discount_in_percentages/100)

and concrete instances of it:
0.1m
0.3m
0.5m

those numbers are very magic as well – they are not telling us anything about themselves.

We have the same situation in case of converting 'time in years of having an account' to discount a 'discount for loyalty:
decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;

number 5 is making our code very mysterious.

We have to do something with it to make it more descriptive!

I will use another technique of avoiding magic strings – which are constants (const keyword in C#). I strongly recommend to create one static class for constants to have it in one place of our application.

For our example, I've created below class:
Hide   Copy Code

public static class Constants
{
  public const int MAXIMUM_DISCOUNT_FOR_LOYALTY = 5;
  public const decimal DISCOUNT_FOR_SIMPLE_CUSTOMERS = 0.1m;
  public const decimal DISCOUNT_FOR_VALUABLE_CUSTOMERS = 0.3m;
  public const decimal DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS = 0.5m;
}

and after modification our DiscountManager class will look as below:
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
        priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
        break;
      default:
        throw new NotImplementedException();
    }
    return priceAfterDiscount;
  }
}

I hope you will agree that our method is now more self explanatory Smile | :)
STEP VII – Don't repeat yourself!

                                        

Just to not duplicate our code we will move parts of our algorithms to separate methods.

We will use extension methods to do that.

Firstly we have to create 2 extension methods:
Hide   Copy Code

public static class PriceExtensions
{
  public static decimal AddDiscountForAccountStatus(this decimal price, decimal discountSize)
  {
    return price - (discountSize * price);
  }

  public static decimal AddDiscountForTimeOfHavingAccount(this decimal price, int timeOfHavingAccountInYears)
  {
     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
    return price - (discountForLoyaltyInPercentage * price);
  }
}

As names of our methods are very descriptive I don't have to explain what they are responsible for, now let's use the new code in our example:
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
          .AddDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
          .AddDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
          .AddDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
        break;
      default:
        throw new NotImplementedException();
    }
    return priceAfterDiscount;
  }
}

Extension methods are very nice and can make your code simpler but at the end of the day are still static classes and can make your unit testing very hard or even impossible. Because of that we will get rid of it in the last step. I've used it just to present you how they can make our live easier, but I'm not a big fan of them.

Anyway, will you agree that our code looks a lot better now?

So let's jump to the next step!
STEP VIII – Remove few unnecessary lines...

We should write as short and simple code as it is possible. Shorter code = less possible bugs, shorter time of understanding the business logic.
Let's simplify more our example then.

We can easily notice that we have the same mathod call for 3 kinds of customer account:
.AddDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

Can't we do it once? No, we have exception for NotRegistered users because discount for years of being a registered customer does not make any sense for unregistered customer. True, but what time of having account has unregistered user?

- 0 years

Discount in this case will always be 0, so we can safely add this discount also for unregistered users, let's do it!
Hide   Shrink   Copy Code

public class DiscountManager
{
  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        priceAfterDiscount = price;
        break;
      case AccountStatuses.SimpleCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS);
        break;
      case AccountStatuses.ValuableCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS);
        break;
      case AccountStatuses.MostValuableCustomer:
        priceAfterDiscount = price.AddDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS);
        break;
      default:
        throw new NotImplementedException();
    }
    priceAfterDiscount = priceAfterDiscount.AddDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    return priceAfterDiscount;
  }
}

We were able to move this line outside the switch-case statement. Benefit – less code!
STEP IX – Advanced – Finally get clean code

All right! Now we can read our class like a book, but it isn't enough for us! We want super clean code now!

                                                               

Ok, so let's do some changes to finally achieve this goal. We will use dependency injection and strategy with a factory method design patterns!



That's how our code will look at the end of the day:
Hide   Copy Code

public class DiscountManager
{
  private readonly IAccountDiscountCalculatorFactory _factory;
  private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;

  public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
  {
    _factory = factory;
    _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
  }

  public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
  {
    decimal priceAfterDiscount = 0;
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).AddDiscount(price);
    priceAfterDiscount = _loyaltyDiscountCalculator.AddDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
    return priceAfterDiscount;
  }
}

Hide   Copy Code

public interface ILoyaltyDiscountCalculator
{
  decimal AddDiscount(decimal price, int timeOfHavingAccountInYears);
}

public class DefaultLoyaltyDiscountCalculator : ILoyaltyDiscountCalculator
{
  public decimal AddDiscount(decimal price, int timeOfHavingAccountInYears)
  {
    decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
    return price - (discountForLoyaltyInPercentage * price);
  }
}

Hide   Shrink   Copy Code

public interface IAccountDiscountCalculatorFactory
{
  IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatuses accountStatus);
}

public class DefaultAccountDiscountCalculatorFactory : IAccountDiscountCalculatorFactory
{
  public IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatuses accountStatus)
  {
    IAccountDiscountCalculator calculator;
    switch (accountStatus)
    {
      case AccountStatuses.NotRegistered:
        calculator = new NotRegisteredDiscountCalculator();
        break;
      case AccountStatuses.SimpleCustomer:
        calculator = new SimpleCustomerDiscountCalculator();
        break;
      case AccountStatuses.ValuableCustomer:
        calculator = new ValuableCustomerDiscountCalculator();
        break;
      case AccountStatuses.MostValuableCustomer:
        calculator = new MostValuableCustomerDiscountCalculator();
        break;
      default:
        throw new NotImplementedException();
    }

    return calculator;
  }
}

Hide   Shrink   Copy Code

public interface IAccountDiscountCalculator
{
  decimal AddDiscount(decimal price);
}

public class NotRegisteredDiscountCalculator : IAccountDiscountCalculator
{
  public decimal AddDiscount(decimal price)
  {
    return price;
  }
}

public class SimpleCustomerDiscountCalculator : IAccountDiscountCalculator
{
  public decimal AddDiscount(decimal price)
  {
    return price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price);
  }
}

public class ValuableCustomerDiscountCalculator : IAccountDiscountCalculator
{
  public decimal AddDiscount(decimal price)
  {
    return price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price);
  }
}

public class MostValuableCustomerDiscountCalculator : IAccountDiscountCalculator
{
  public decimal AddDiscount(decimal price)
  {
    return price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price);
  }
}



First of all we got rid of extension methods (read: static classes) because using them made caller class (DiscountManager) tightly coupled with discount algorithms inside extension methods. If we would want to unit test our AddDiscount method it would not be possible because we would be also testing PriceExtensions class.



To avoid it I've created DefaultLoyaltyDiscountCalculator class which contains logic of AddDiscountForTimeOfHavingAccount extension method and hide it’s implementation behind abstraction (read: interface) ILoyaltyDiscountCalculator. Now when we want to test our DiscountManager class we will be able to inject mock/fake object which implements ILoyaltyDiscountCalculator into our DiscountManager class via constructor to test only DiscountManager implementation. We are using here Dependency Injection Design Pattern.
                                                          


By doing it we have also moved responsibility of calculating a discount for loyalty to a different class, so if we will need to modify this logic we will have to change only DefaultLoyaltyDiscountCalculator class and all other code will stay untouched – lower risk of breaking something, less time for testing.

Below use of divided to separate class logic in our DiscountManager class:
priceAfterDiscount = _loyaltyDiscountCalculator.AddDiscount(priceAfterDiscount, timeOfHavingAccountInYears);

In case of calculation discount for account status logic, I had to create something more complex. We had 2 responsibilities which we wanted to move out from DiscountManager:

    Which algorithm to use according to account status
    Details of particular algorithm calculation

To move out first responsibility I’ve created a factory class (DefaultAccountDiscountCalculatorFactory) which is an implementation on Factory Method Design Pattern and hid it behind abstraction - IAccountDiscountCalculatorFactory.

                                                      

Our factory will decide which discount algorithm to choose. Finally we are injecting our factory into a DiscountManager class via constructor using Dependency Injection Design Pattern.

Below use of the factory in our DiscountManager class:
priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).AddDiscount(price);

Above line will return proper strategy for particular account status and will invoke AddDiscount method on it.

First responsibility divided so let’s talk about the second one.

Let's talk about strategies then…

                                                        

As a discount algorithm could be different for every account status we will have to use different strategies to implement it. It’s great opportunity to use Strategy Design Pattern!

In our example we have now 3 strategies:
NotRegisteredDiscountCalculator
SimpleCustomerDiscountCalculator
MostValuableCustomerDiscountCalculator

They contain implementation of particular discount algorithms and are hidden behind abstraction:
IAccountDiscountCalculator.

It will allow our DiscountManager class to use proper strategy without knowledge of its implementation. DiscountManager only knows that returned object implements IAccountDiscountCalculator interface which contains method AddDiscount.

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator, MostValuableCustomerDiscountCalculator classes contain implementation of proper algorithm according to account status. As our 3 strategies look similar the only thing we could do more would be to create one method for all 3 algorithms and call it from each strategy class with a different parameter. As it would make our example to big I didn't decide to do that.



All right, so to sum up now we have a clean readable code and all our classes have only one responsibility – only one reason to change:
1. DiscountManager – manage code flow
2. DefaultLoyaltyDiscountCalculator – calculation of discount for loyalty
3. DefaultAccountDiscountCalculatorFactory – deciding which strategy of calculation account status discount to choose
4. NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator, MostValuableCustomerDiscountCalculator – calculation of discount for account status



Now compare method from beginning:
Hide   Copy Code

public class Class1
{
    public decimal Calculate(decimal ammount, int type, int years)
    {
        decimal result = 0;
        decimal disc = (years > 5) ? (decimal)5 / 100 : (decimal)years / 100;
        if (type == 1)
        {
            result = ammount;
        }
        else if (type == 2)
        {
            result = (ammount - (0.1m * ammount)) - disc * (ammount - (0.1m * ammount));
        }
        else if (type == 3)
        {
            result = (0.7m * ammount) - disc * (0.7m * ammount);
        }
        else if (type == 4)
        {
            result = (ammount - (0.5m * ammount)) - disc * (ammount - (0.5m * ammount));
        }
        return result;
    }
}

to our new, refactored code:
Hide   Copy Code

public decimal AddDiscount(decimal price, AccountStatuses accountStatus, int timeOfHavingAccountInYears)
{
  decimal priceAfterDiscount = 0;
  priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).AddDiscount(price);
  priceAfterDiscount = _loyaltyDiscountCalculator.AddDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
  return priceAfterDiscount;
}


Source: http://www.codeproject.com/Articles/1083348/Csharp-BAD-PRACTICES-Learn-how-to-make-a-good-code

Experimental .NET Core Debugging in VS Code

oday we are releasing our first experimental preview of debugging for the new ASP.NET Core CLI toolset in Visual Studio Code. Before I continue it’s important to note a few things:

    This support is for the new prerelease .NET Core CLI toolset; it does not work for the existing DNX experience (see the ASP.NET teams Q&A blog post to understand the changes).
    VS Code’s IntelliSense does not yet work with the .NET CLI tools. So if you want to try out debugging, IntelliSense won’t be available for the CLI project type.

With this first release you get breakpoints, stepping, variable inspection, and call stacks.

vsc

However, given the very early stage of .NET Core and the debugging experience there will be some features you might be used to in the Visual Studio IDE that we don’t have in VS Code yet.  These include:

    No separate console window is created when you start debugging, all program output appears in the Debug Console of VS Code.  This means that to use Console.ReadLine in a console application during debugging you have to start the application with dotnet run outside of VS Code and then attach the debugger.
    No Edit & Continue (the ability to modify code and have the changes applied while debugging)
    Cannot edit the value of variables during debugging
    No TracePoints or Conditional breakpoints
    Set Next Statement is not supported

We are very interested in your feedback to help us prioritize addressing these as covered at the end of this post.
Getting started

To get started you will need to do a few things (see our GitHub page for complete instructions)

    Install (or upgrade to) Visual Studio Code version 0.10.10 or greater
    Install the .NET CLI tools
    Install the C# extension
    Open any .cs file in VS Code to load the extension and complete the installation (view progress in the Output pane)
    Install mono

Things you get while debugging

    Breakpoints – set breakpoints with one click (or via F9)from the editor
    Watches – define expressions that you want to keep an eye on
    Locals – see the value of local variables automatically
    Call Stack – find information on method calls on the stack
    Debug Console – shows you all debug messages (show and hide this pane using Ctrl+Shift+Y or Cmd+Shift+Y on a Mac)
    Current values –  hover over variables to see the value
    All panes show coordinated values – whenever you click an entry in the Call Stack you automatically jump to the corresponding line in the editor and watches and locals are updated

What else can you configure for debugging?

Once you install and configure the extension you will have the basic debugging experience describe above, but there is much more you can configure using the launch.json file.
Stop at Entry

Setting the stopAtEntry flag will cause the debugger to break immediately on the entry point to the application. This allows you to start stepping through your code without setting breakpoints.
Configure debugging for ASP.NET applications

Besides the default debug configuration there’s a configuration called .NET Core Launch (web) which is a good starting point for debugging ASP.NET applications. It contains an additional section “launchBrowser” that is used to open up a WebBrowser on your development machine whenever you start debugging. Per default it is configured to use the default browser but you can specify alternative browsers if you modify the commands in file accordingly. To get started and to give ASP.NET a try just download the ASP.NET CLI samples from Github .

Here’s what a configuration of launch.json would look like if you configure it to open a page with Opera.


"osx": {
    "command" : "open",
    "args" : "-a opera ${auto-detect-url}"
}


Modify launch URL

The URL the browser navigates to is configurable as well. If you stick to the defaults the debugger figures out the URL automatically and targets to the default URL the server listens to. ${auto-detect-url} serves as placeholder to find the URL automatically and you can use it anywhere in the configuration. For a scenario where you’d like to always debug a certain URL path like http://localhost:5000/api/customers/42 , you can overwrite the default value to avoid the manual navigation process.
Attach scenarios

For attach scenarios a third configuration section is integrated. All you have to do here is specify the process name of the application you want to debug. In our case this would be HelloWorld. If you spin up your application from the command line with dotnet run make sure you select the .NET Core Attach configuration first before starting the debugger.

If you have multiple processes running with the same name you can also specify processId instead of the processName. Just make sure you don’t use both properties at the same time.

top

processid
Hidden configuration properties

There are currently three additional properties that don’t show up in the default configuration. If you wonder how to figure out those properties aside from reading the documentation, here’s a tip: Start typing “o in launch.json (make sure you also type the leading quotation mark) and you’ll get an auto-completion that shows you a list of properties containing an “o”.

typeo

While this is very handy when you’re already editing, you might want always up to date documentation. As always the truth is in the code, and if you want to dig a little deeper you can take a look at the installation folder of the extension where you will find a file called package.json . Consider this file as the master template for valid configurations. It gives you a very good overview about configurations possible on your machine for your current version of the extension in easy to understand and human-readable json.

pathtopackagejson

json
symbolPath

You can use “symbolPath” to specify an array of paths to your debugging symbols. This can be very helpful if your symbols are located on a central server and when you are working across multiple operating systems. Symbols generated on one specific operation system also work cross platform.

A valid configuration of the symbol path is shown below.

    “symbolPath”:”[ \”/Volumes/symbols\” , \”/OtherVolume/otherSymbols\” ]”
justMyCode

When you are in a debug session you might not be interested in stepping into framework code or into code of components you haven’t written. This is the reason why we came up with justMyCode. This feature is enabled per default as it seems to be the preferred setting for most debugging situations. However, if you want to debug framework code or external components you can enable this by setting it explicitly to false.

“justMyCode”:false
sourceFileMap

If you want to point the debugger to the corresponding source files for your debugging session you can specify a mapping table using the sourceFileMap property. This property can have any number of entries and will make sure the debugger works with the source code files you want it to work with. A configuration could look like this.

“sourceFileMap”: {
    “C:\foo\bar”:”/home/me/foo/bar”
}


Source: https://blogs.msdn.microsoft.com/visualstudioalm/2016/03/10/experimental-net-core-debugging-in-vs-code/