Magic is a Super DYR starter kit for your ASP.NET Core web APIs, that brings the ideas of DRY ("Don't Repeat Yourself") to a new level. If you follow a small recipe as you create your controller endpoints, services, view models, and database models, you will literally start out with 80% of your job done, and you can wrap any CRUD operations into your HTTP REST endpoints extremely rapidly. The project's purpose is to be a starter kit for your own projects, allowing you to "hit the ground running". Magic is best suited for database applications.
- Download Magic
- Unzip and open "magic.sln"
- Start your debugger
The whole idea is that Magic allows you to create all CRUD operations on your web APIs, without having to code. This is possible due to that HTTP and SQL are basically just fundamentally CRUD operations. Hence, your controller endpoints will end up looking like the following.
[Route("api/todo")]
public class TodoController : CrudController<www.Todo, db.Todo>
{
public TodoController(ITodoService service)
: base(service)
{ }
}
While your service implementation will resemble the following.
public class TodoService : CrudService<Todo>, ITodoService
{
public TodoService([Named("default")] ISession session)
: base(session, LogManager.GetLogger(typeof(TodoService)))
{ }
}
Even your service interface ends up empty.
public interface ITodoService : ICrudService<Todo>
{ }
At which point all you need to do, is to model your database model, such as the following illustrates.
public class Todo : Model
{
public virtual string Header { get; set; }
public virtual string Description { get; set; }
public virtual bool Done { get; set; }
}
And map it to your database using something resembling the following.
public class TodoMap : ClassMap<Todo>
{
public TodoMap()
{
Table("todos");
Id(x => x.Id);
Map(x => x.Header).Not.Nullable().Length(256);
Map(x => x.Description).Not.Nullable().Length(4096);
Map(x => x.Done).Not.Nullable();
}
}
For then to create a view model looking like the following.
public class Todo
{
public Guid? Id { get; set; }
public string Header { get; set; }
public string Description { get; set; }
public bool Done { get; set; }
}
And then make sure Ninject is using your service implementation, by creating something resembling the following.
public class Initializer : IInitialize
{
public void Initialize(IKernel kernel)
{
kernel.Bind<ITodoService>().To<TodoService>();
}
}
See the "/modules/magic.todo/" folder for more details about how to create your own CRUD modules.
Notice, without adding more than one line of actual "code", we were still able to create all CRUD HTTP REST endpoints for our domain type, arguably "magically", without actually adding any code per se. This is possible due to intelligent use of polymorphism and C# generics, which allows our code to become "Super DRY".
Magic contains a basic "signal" implementation, that allows you to create a "signal" from one module, that you can subscribe to using a "slot" in another module, without bringing in dependencies between your two different modules at all. The basic way to implement this is as follows.
using System;
using magic.signals.contracts;
[Slot(Name = "foo.bar")]
public class FooBarSlot : ISlot
{
public void Signal(JObject input)
{
var foo = input["foo"].Value<Guid>();
/* ... do stuff with foo here ... /*
}
}
Then somewhere else in your application, in a completely different module, you can do the following.
using System;
using magic.signals.contracts;
public class FooBarSignaler
{
readonly ISignaler _signaler;
public FooBarSignaler(ISignaler signaler)
{
_signaler = signaler;
}
public void DoFooBar(Guid foo)
{
_signaler.Signal("foo.bar", new JObject
{
["foo"] = foo,
});
}
}
Once you invoke the ISignaler.Signal
method, the FooBarSlot.Signal
method will be invoked, because its Slot.Name
happens to be the name
of the "signal" we raise inside of DoFooBar
. No dependencies are required between the two different modules, and this works for all practical
concerns as an event mechanism across modules, while still keeping a loosely coupled implementation, without dependencies between the two different
modules. This allows any module of yours to communicate with any other module in your app, without having to bring in dependencies between the two,
as long as they communicate exclusively using signals and slots, only passing data in through their JObject
input. Data as in strings, date objects,
integers, etc ...
You can see this implemented in the "magic.auth" module, which raises the "user.deleted" signal, which other modules can attach themselves to. The "magic.email" module has a slot for this particular signal.
This repository also includes a .NET Core CLI template which can be installed by:
- Download and unzip the latest version
- Open a terminal in the parent folder of the repo
- i.e. one level above the root of the repo
- if you unzipped to
~/code/magic
, then open the terminal at~/code/
- Run the following command:
dotnet new --install ./magic
This will install the contents of this repo as a template, which you can use alongside the .NET Core CLI in the following manner:
dotnet new magic --name foo
After the scaffolding process completes, you will find a copy of the code base in
the ./foo/
folder. The --name
switch will take care of full namespace replacement.
Magic supports MySQL, MSSQL and SQLIte out of the box, but adding support for your own relational database type, can be done with three lines of code. This is possible due to the usage of Fluent nHibernate. Magic is built upon the following open source projects.
You will probably benefit from understanding these projects as you proceed to create your own projects.
- Create a folder in "modules" to contain your module's components
- Create your database model class(es). See
magic.todo.model
for an example. - Create your contract (service interface). See the
magic.todo.contracts
for an example. - Create your service implementation. See
magic.todo.services
for an example. - Create your view model. See the
magic.todo.web.model
project for an example. - Create your controller. See the
magic.todo.web.controller
project for an example. - Add a reference to your controller and service into the
magic.backend
. - Make sure you somehow configure Ninject to use your service implementation
To change database provider is easy. Open up "appsettings.config" in your "magic.backend" project, and change the database
section to whatever
database provider you want to use, and the connection string to your database. Notice, Magic will automatically create the database
schema itself accoding to your ClassMap
definition(s). Supported database drivers are as follows.
MySQL
- Make sure you use a connection string to an existing MySQL database instanceMSSQL
- Microsoft SQL ServerSQLIte
- SQLIte database (this is the default)
- Auth module - Add authentication and authorization features to your app
- IO module - Helps you handle files and folders in your installation
- Email module - Giving you a webmail backend for retrieving emails from POP3 accounts and sending emails over SMTP
- Affiliate cookie module - Gives you the ability to easily create affiliate tracking cookies
The above modules comes in addition to the example TODO module, and the common modules, allowing you to easily create any CRUD modules, and create signals and slots across module boundaries. Below is an example of how to use Magic to create a file
upload/download HTTP REST web API, that securely gives you access to most of the important functionality from
within System.IO
.
Thanks to the following contributors.
- Jamie Taylor, who also have an awesome podcast and blog about .Net Core who contributed the CLI template.
Magic is licensed as Affero GPL, which implies that you can only use it to create Open Source software - However, a proprietary enabling license can be obtained for €50 by following this PayPal link and pay me €50 - At which point you are free to create one closed source web app. If you want to create multiple closed source web APIs using Magic, you'll have to purchase one license for each web API project you want to create.
Notice, without a closed source license, your code automatically becomes Open Source, and you'll have to provide a link to your own source code from any website(s), and/or application(s) from where you are consuming your Magic web API. With a closed source license, you can create closed source code, and you don't have to provide a link to neither me, nor your own source code.
Send more Champagne
Quote by Karl Marx