Page 30 - MSDN Magazine, June 2019
P. 30
We’ve become super-DRY magicians, through intelligent use of OOP, generics and the principles of DRY. So the question begs: How can you use this approach to make your code better?
The answer: Start out with your database model and create your own model class, which can be done either by adding a new project inside your model folder, or by adding a new class to the existing magic.todo.model project. Then create your service interface in the contracts folder. Now implement your service in your services folder, and create your view model and controller. Make sure you bind between your service interface and your service implementa- tion. Then if you choose to create new projects, you’ll have to make sure ASP.NET Core loads your assembly by adding a reference to it in your magic.backend project. Notice that only the service project and the controller need to be referenced by your back end.
If you chose to use the existing projects, the last part isn’t even necessary. One simple line of actual code to bind between your ser- vice implementation and your service interface, and you’ve created an entire ASP.NET Core Web API solution. That’s one mighty line of code if you ask me. You can see me go through the entire pro- cess for an earlier version of the code in my “Super DRY Magic for ASP.NET Core” video at youtu.be/M3uKdPAvS1I.
Then imagine what occurs when you realize that you can scaffold this code, and automatically generate it according to your database schema. At this point, your computer scaffolding software sys- tem is arguably doing your coding, producing a perfectly valid Domain-Driven Design (DDD) architecture in the process.
No Code, No Bugs, No Problem
Most scaffolding frameworks apply shortcuts, or prevent you from extending and modifying their resulting code, such that using them for real-world applications becomes impossible. With Magic, this shortcoming is simply not true. It creates a service layer for you, and it uses dependency injection to inject a service interface to your controller. It also produces perfectly valid DDD patterns for you. And as you’ve created your initial code, every single part of your
Figure 1 Overriding the Deletion of an EmailAccount
solution can be extended and modified as you see fit. Your project perfectly validates to every single letter in SOLID.
For instance, in one of my own solutions, I have a POP3 server fetcher thread in my service, declared for an EmailAccount domain model type. This POP3 service stores emails into my database from my POP3 server, running on a background thread. When an email is deleted, I want to make sure I also physically delete its attachments in storage, and if the user deletes an EmailAccount, I obviously want to delete its associated emails.
We’ve become super-DRY Magicians, through intelligent use of OOP, generics and the principles of DRY. So the question begs: How can you use this approach to make your code better?
The code in Figure 1 shows how I’ve overridden the deletion of an EmailAccount, which also should delete all emails and attach- ments. For the record, it uses Hibernate Query Language (HQL) to communicate with the database. This ensures that NHibernate will automatically create the correct SQL syntax, depending on to which database it’s physically connected.
Doing the Math
Once you start philosophizing around these ideas, inspiration strikes. For instance, imagine a scaffolding framework built around Magic. From a mathematical point of view, if you have a database with 100 tables, each with an average of 10 columns, you’ll find that the cost in terms of total lines of code can add up fast. For instance, to wrap all of these tables into an HTTP REST API requires seven lines of code per service interface, while 14 lines of code are needed per table for each service and 19 lines are needed per table for each controller. Figure 2 runs down the elements involved and the lines of code required.
Figure 2 Adding Up the Cost in Code
public sealed class EmailAccountService : CrudService<EmailAccount>, IEmailAccountService
{
public EmailAccountService(ISession session)
: base(session, LogManager.GetLogger(typeof(EmailAccountService))) {}
public override void Delete(Guid id) {
var attachments = Session.CreateQuery(
"select Path from EmailAttachment where Email.EmailAccount.Id = :id");
attachments.SetParameter("id", id);
foreach (var idx in attachments.Enumerable<string>()) {
if (File.Exists(idx)) File.Delete(idx);
}
var deleteEmails = Session.CreateQuery(
"delete from Email where EmailAccount.Id = :id");
deleteEmails.SetParameter("id", id); deleteEmails.ExecuteUpdate();
base.Delete(id); }
}
Component
Contracts
Average Lines of Code
Total Lines of Code
Service interfaces
100
7
700
Services
100
14
1,400
Controllers
100
19
1,900
Service interface and implementation
100
1
100
View models
100
17
1,700
Database models
100
17
1,700
Database mappings
100
20
2,000
Total Lines of Code:
9,500
26 msdn magazine
Patterns and Practices