Design your software in Interfaces and Repository Services Architecture, this way your code will be testable, and when it comes time to migrate to a newer framework you can package your code in Library classes and build as a DLL.
- Your code will be adaptable, you can plug in the Class Libraries into a Console App and your application will be agnostic to every design/template.
Do not let other ServiceRespository Use/Consume another Service Repository, only utilize the Service Repository in the Controller unless really needed.
- Failing to follow this principle might result in a Circular Reference Error (there is an exception to this, you can get away with it if the Service being called does not call another Service that is being called from the Service calling this Service), meaning that other Services are consuming other services (code calling becomes circular somehow creating a loop ). - A good example of this is when you reference a project by itself or have two projects that reference each other.
- This is important because if there is a Service missing, the application will still function. Look at it from a micro-service architecture point of view. A service should not use another service but the user should go through a controller, only a controller should utilize the ServiceRepository class that inherits from the Interface.
- When utilizing the ServiceRepository class from a Controller make sure that you inject the Interface that the ServiceRepository class inherits from, that way all the functions defined in the Interface class will be accessible from the Controller and it will be lightweight.
Easy To Migrate to New Framework - Designing your Software Application like this will allow you to easily migrate your code to the latest framework or package your code into DLL that you can use across multiple applications.
Use Dependency Injection in your application, in Asp.Net Core the Dependency Injection Container manages the Services and the Interfaces
Use SOLID Principle in your design ( Single Responsibility, Open-Closed Principle-Software classes should be closed for modification but open for extension, Liskov Substitution Principle - Objects should be replaceable with instances of their subtypes without altering the correctness of that program, Interface Segregation Principal - many interfaces are better than one giant interface. Dependency Invasion Principle - One should depend on abstraction than concretion.
)
Tools
Download Linqpad to run C# code without loading or creating a Program in Visual Studio - Sometimes, you just want to see if a certain code will execute or will bring the results as desired, therefore you don't need to create the whole application just to test the code. In this case, use Linqpad, select a language, and type the code in the working areas.
Make sure that you are not leaving hard-coded values throughout the application
- Look at it as, when your Boss comes back asking you to change Color: blue to Color: Yellow, then you will have to go to every class or page that has that value hard-coded. Do Not Do This, instead define that Color as a variable or property so that it is prone to change. If your boss asks you to change, you will be able to change in one place.
- User enum to define integer-based values and use the name where needed.
[NB] Ever wondered how you can update the UI control when a variable changes on the Server Side? Leave a comment below if you want me to get into it. - One way to achieve this is by recompiling MVC or View onLoad, which means everytime you access the View it will be recompiled therefore showing the value that has been changed on the Server Side. Other methods will be to use App States but you will have problem notifying the UI control when there is a change on the Server Side.
Watch Out [Do Not Do This]
Watch out when you start duplicating code, ask yourself if there is a better way to implement that code without duplicating it. For example, if you want to code execute on every View/Page then just put that code in _ViewStart.cshtml, the _ViewStart will be available for every View and the code in it will execute on every View inheriting it. Look at it like a Master Page for All Views inheriting it.
Do not make Actions Async if they only return a View, doing this will throttle the response time, it does not return to the user in a timely manner. It makes the application run (specifically Views) run slower.
Make Post Actions Async, this will help the response time, as the request will be run in the background for other transactions.
Technique
If you want data to be available to the Http Context (During the time the user is going to use the app) then use a static class to store data temporarily or create a Singleton Service Class and register it with the Startup.cs Class.
The Singleton Variables will be accessed for the Application Life Cycle. If you want Data to be available to the Session then create a Service Class and register it as a Scopped Service.
Scopped Services are available to Http Context during the whole Session.
If you want to run some code operation in the Background without blocking the UI-Thread in C# (asp.net core) wrap the code or function in a Task.Run(()=>{_someInterface.SomeFunction();}) - the function will run in the background, let the function call SignalR function to notify other function when the Task is complete - The function is executed in a Task.Run() should return a Task.
Use Dynamic Modal Data Binding in Asp.net 5
You can use dynamic data = System.Dynamic.ExpandoObject() - You can then pack your/the entire List of Object inside the data object like so: data.YouNameIt = TheObjectWithListOfObject data.AnotherInstance = "someValues"; - In the View do not explicitly define the ModalType. Just bind dynamically.
Different Database Contexts - Did you know that you could register different other Database Context and use them in the Application? - Picture this: you have a multi ternant application and one requires a database schema to be slightly different. You could ague that you could use the different table schema to only use one Database Context, but here is the catch: Mapping two different (New Schema and Old Table Schema) will throw an error, you should establish relationship between those two. What if you don't want, you just want couple columns in New Table without breaking dependecies in other application that don't require those column in a table.
- What if you you had other Class Library accross muiltiple application that uses one concrete database Schema? this way, when you change the Database Context other application will be broken.
Solution: If you want to use slightly different Database Schema then create a new DatabaseContext.cs file and create your DbSet<Class> in there. You can still register your old Database Context in Startup.cs and still use the Old Database Context in other ServiceRepositories. Keep in mind that you will have to use the New Database Context class in other Services that needs to use a new Database Schema, that way you have it both ways.