MagnaDB is a fast and reliable ORM that makes you worry less about databases
and lets you code peacefully.
This framework packs Code-First capabilities, as well as Database-First easy
integration. It'll go with you, the path you decide to take. No need for
complicated migrations, nor strict database access/modification; you do
you; just make sure your models and tables match and everything will be
just fine.
You're on the No-SQL train? This is for you.
You still love SQL like the first time? This is for you.
MagnaDB is designed to be a Hybrid ORM, letting you manipulate databases
objects directly from the code, but also letting the window open databases
commands to get in. All this while keeping you safe.
In order to get started we first need to install MagnaDB to one of our
projects.
For the present example let's create a new Console Application Project
for .NET Framework. Let's name it TestMagnaDB
(or whatever you wish to name it. It's up to you).
If you're using Visual Studio, or Visual Studio Code (with the NuGet
Package Manager extension) you just need to execute the following command
in the NuGet Package Manager Console.
PM> Install-Package MagnaDB.SQLite
After you have successfully installed MagnaDB to your project, the next step is to create
some models.
We'll start with a Table Model.
A TableModel is a class that resembles a Table definition in a database,
and objects from this class are mapped to a row from this table in the database
through their properties.
Let's create a Cat model (you
can do this by creating a new Class file in your project; name it Cat); it will have
the following properties: Id, Name, Race, DateOfBirth.
It would look something like this:
using System;
using MagnaDB.SQLite;
namespace TestMagnaDB
{
public class Cat
{
public long Id { get; set; }
public string Name { get; set; }
public string Breed { get; set; }
public DateTime DateOfBirth { get; set; }
}
}
using System;
using MagnaDB.SQLite;
namespace TestMagnaDB
{
public class Cat : TableModel<Cat>
{
protected override string ConnectionString => "Data Source=testmagna.db;New=True;Version=3;";
protected override string TableName => "Cats";
protected override MagnaKey Key => this.MakeKey(cat => cat.Id);
[Identity]
public long Id { get; set; }
public string Name { get; set; }
public string Breed { get; set; }
public DateTime DateOfBirth { get; set; }
}
}
using System;
namespace TestMagnaDB
{
class Program
{
static void Main(string[] args)
{
// First things, first, let's create our Cat Table
// If a table already exists in the database, we'll
// have an exception thrown, so we'll use try/catch
try
{
Cat.CreateTable();
}
catch
{
// The table already exists.
}
// Next, we'll create a new Cat object
Cat milo = new Cat()
{
Name = "Milo",
Breed = "Persian",
DateOfBirth = new DateTime(2019, 11, 7)
};
// Let's insert Milo to the DB
milo.Insert();
// After a TableModel object is successfully inserted to the database
// it will have its Identity decorated property (if any) automatically updated
Console.WriteLine("{0}, the {1} cat's Id is {2}", milo.Name, milo.Breed, milo.Id);
// Turns out Milo's not a Persian, but an American Shorthair
// We'll fix this and update Milo's breed in the database
milo.Breed = "American Shorthair";
milo.Update();
// If we want to retrieve Milo's information from the database
// we can do so by using the Get() static method, and providing Milo's Id
// as an argument. This works for classes with Identity decorated properties.
Cat miloFromTheDB = Cat.Get(milo.Id);
Console.WriteLine("This kitten's name is {0}", miloFromTheDB.Name);
Console.WriteLine("It's a(n) {0}", miloFromTheDB.Breed);
Console.WriteLine("It was born on {0:MMM dd yyyy}", miloFromTheDB.DateOfBirth);
Console.WriteLine("{0}Press any key to continue...", Environment.NewLine);
Console.ReadKey();
}
}
}
MagnaDB View Models are models that are only used to retrieve information from
the database, meaning they are SELECT Only.
Even though Views are created from existing tables (in engines like SQL Server,
or MySql), which means they derive from Tables; in MagnaDB, TableModel is a ViewModel's
subclass (which is useful to know), and therefore
all operations done with ViewModel can be done with TableModel.
ViewModels DO NOT have to point to a VIEW in order to work.
They can perfectly point at a Table, and query one or more of
its columns with no issues.
Let's create a new Class file in our project which we previously created (sections
2., 3.) which we'll name
CatView.cs
using System;
using MagnaDB.SQLite;
namespace TestMagnaDB
{
public class CatView : ViewModel<CatView>
{
protected override string ConnectionString => "Data Source=testmagna.db;New=True;Version=3;";
protected override string TableName => "Cats";
protected override MagnaKey Key => this.MakeKey(cat => cat.Id);
[Identity]
public long Id { get; set; }
public string Name { get; set; }
public string Breed { get; set; }
}
}
...
// We'll retrieve all cats from the database using the ToList() static
// method in our CatView class; this can also be done by using table
// models (like the Cat class we previously created), but we're
// using the CatView class just for demonstration.
var aLotOfMilos = CatView.ToList();
foreach(var item in aLotOfMilos)
{
Console.WriteLine("Id: {0}, Name: {1}, Breed: {2}", item.Id, item.Name, item.Breed);
}
Console.WriteLine("{0}Press any key to continue...", Environment.NewLine);
Console.ReadKey();
}
}
}
**DISCLAIMER: The documentation provided for this section contains information
for the different MagnaDB classes, properties, methods, events, that are
considered to be relevant for its use, and not the whole project.**
Method | Description |
---|---|
CreateActiveTransaction() static async |
Uses this class' connection string property to create an active transaction
to the database (which needs to be commited/backrolled, and disposed of
after being used). Returns the newly created transaction. Will throw an exception if the connection to the database fails. |
CreateOpenConnection() static async |
Uses this class' connection string property to create an open connection.
to the database (which needs to be closed/disposed of after being used). Returns the newly created database connection. Will throw an exception if the connection to the database fails. |
Get() static async |
Finds and retrieves an object in the database
matching the argument-provided key. Returns true if a table row is successfully inserted. Otherwise, false. This Methods triggers the GetSucceeded event when a matching result is found. This Methods triggers the GetFailed event when a matching entity is not found. Will throw an exception if the database connection fails or if the class definition doesn't correctly match to the table's definition in the database. |
GetKey() | Returns the object's corresponding Key. |
GetTableName() static | Returns this object's class' table name. |
GetTableName() static | Returns this object's class' table name. |
LoadRelationships() static async | Retrieves and assigns for an object or collection all specified foreign relations see attribute. |
TableToIEnumerable() static async |
Maps a DataTable object's columns to a set of objects' properties and
returns an IEnumerable collection with the resulting objects. Will throw an exception if the database connection fails. |
ToDataTable() static async |
Performs a SELECT to the Database selecting this class' properties that
match to columns of the database table. Only columns decorated with the
[DataDisplayable] will be selected by default
(this behavior can vary).SQL Clauses may be included as
arguments. A DataTable object with the results will be returned. Will throw an exception if the connection to the database fails, or if the class' definition doesn't correctly match that of the database table. |
ToIEnumerable() static async |
Performs a SELECT to the Database selecting this class' properties that
match to columns of the database table. SQL Clauses may be included as
arguments. An IEnumerable<T> object with the results will be returned. Will throw an exception if the connection to the database fails, or if the class' definition doesn't correctly match that of the database table. |
ToList() static async |
Performs a SELECT to the Database selecting this class' properties that
match to columns of the database table. SQL Clauses may be included as
arguments. A List<T> object with the results will be returned. Will throw an exception if the connection to the database fails, or if the class' definition doesn't correctly match that of the database table. |
Property | Description |
---|---|
ConnectionString | This property returns this class' specific connection string. |
Key | Returns this object's corresponding Key. |
TableName | Returns the name of the table this class is mapped to in the database. |
Event | Description |
---|---|
GetFailed | This event is raised when the Get method doesn't retrieve an entity from the database or runs into an exception of some sort. |
GetSucceeded | This event is raised when the Get method succeeds in retrieving an entity from the DB. |
SelectFailed | This event is raised when the ToList, ToIEnumerable, ToDataTable methods don't retrieve an entity from the DB or run into an exception of some sort. |
SelectSucceeded | This event is raised when the ToList or ToIEnumerable, ToDataTable methods properly retrieve an Entity from the DB. |
Method | Description |
---|---|
CreateTable() static |
Uses this class' properties to create a table in the database specified
by the class' connection string or the argument-provided connection/transaction. Returns true if the table is sucessfully created. Otherwise, false. Will throw an Exception if the table already exists, or the connection to the database fails. |
Delete() async |
Deletes the database row with a matching key to the invoking object. Returns true if the matching table row is deleted. Otherwise, false. This Methods triggers the BeforeDelete before executing the DELETE command. This Methods triggers the DeleteSucceeded when a database row is successfully deleted. Will throw an exception if the database connection fails or if the provided Key property is not valid. |
GroupInsert() static async |
Inserts an IEnumerable<T> collection of entities to the the database
in a single command. This is specially useful when inserting large GroupInsert
of entities. Returns true if all entities are successfully inserted. Otherwise, false. Will throw an exception if the database connection fails. |
Insert() async |
Inserts a new row to the class' specified table by mapping the invoking
object's properties to columns in the table. Returns true if a table row is successfully inserted. Otherwise, false. This Methods triggers the BeforeInsert before executing the INSERT command. This Methods triggers the InsertSucceeded when a database row is successfully inserted. Will throw an exception if the database connection fails or if the class definition doesn't correctly match to the table's definition in the database. |
IsDuplicated() async |
Verifies if the object is a duplicated entry by evaluating properties
decorated with the [DuplicationColumn] attribute. Returns true if a duplicate is found. Otherwise, false. Will throw an exception if the database connection fails. |
Update() async |
Updates the database row matching this object's Key property using
each one of its non-key properties. Returns true if a database row is successfully updated. Otherwise, false. Will throw an exception if the database connection fails. |
Event | Description |
---|---|
BeforeDelete | This event is raised before the Delete method is executed. |
BeforeInsert | This event is raised before the Insert method is executed. |
BeforeUpdate | This event is raised before the Update method is executed. |
DeleteFailed | This event is raised when the Delete method fails to delete a row in the database. |
DeleteSucceeded | This event is raised when the Delete method successfully deletes this entity. |
InsertFailed | This event is raised when the Insert method fails to insert an entity to a table. |
InsertSucceeded | This event is raised when the Insert method successfully inserts this entity. |
UpdateFailed | This event is raised when the Update method fails to update a row in the database table. |
UpdateSucceeded | This event is raised when the Update method successfully updates this entity. |
Method | Description |
---|---|
DoQuery() static async |
Executes a SQL command against the specified database connection. Returns true if the query is successfully executed. Otherwise, false. Will throw an exception if the connection to the database fails, or the provided SQL command is not valid. |
DoScalar() static async |
Executes a SQL query against the specified database connection
and returns a single value. Will throw an exception if the connection to the database fails, or the provided SQL query is not valid. |
TableMake() static async |
Executes a SQL query against the specified database connection
and returns the query results inside a DataTable object. Will throw an exception if the connection to the database fails, or the provided SQL query is not valid. |
Method | Description |
---|---|
GenDelete() static | Returns a string with a DELETE statement generated from the provided arguments. |
GenFieldsEnumeration() static | Returns a string with a list of comma-separated columns/fields. |
GenInsert() static | Returns a string with a INSERT statement generated from the provided arguments. |
GenSelect() static | Returns a string with a SELECT statement generated from the provided arguments. |
GenSetPairs() static | Returns a string with column/value assignments separated by commas. |
GenUpdate() static | Returns a string with an UPDATE statement generated from the provided arguments. |
GenValuesEnumeration() static | Returns a string with a list of formatted comma-separated values. |
GenWhere() static | Returns a string with a WHERE clause that evaluates that the rows' values are equal to the values provided in each key/value pair. |
GenWhereDiffered() static | Returns a string with a WHERE clause that evaluates that the rows' values are not equal to the values provided in each key/value pair. |
The following attributes let you manipulate MagnaDB's model's behavior and
implement lots of cool functionalities.
All attributes target class properties
We'll be looking at each one of them below.
...
[ColumnName("pedigree")]
public string Breed { get; set; }
...
[DataDisplayable]
public string Name { get; set; }
...
[DateTimeType(DateTimeSpecification.Date)]
public DateTime DateOfBirth { get; set; }
...
[DDLIgnore]
public decimal StockGrowthRatio { get; set; }
...
[DMLIgnore]
public HttpPostedFileBase UploadedImage { get; set; }
...
public const int PersonalInfoIndex = 1;
public const int SocialSecurityIndex = 2;
public const int PassportIndex = 3;
[DuplicationColumn(PersonalInfoIndex)]
public string FirstName { get; set; }
[DuplicationColumn(PersonalInfoIndex)]
public string LastName { get; set; }
[DuplicationColumn(PersonalInfoIndex)]
public DateTime DateOfBirth { get; set; }
[DuplicationColumn(SocialSecurityIndex)]
public string SocialSecurityNumber { get; set; }
[DuplicationColumn(PassportIndex)]
public string PassportNumber { get; set; }
// When invoking the IsDuplicated() method, a parameter for which duplication index
// will be evaluated can be provided; if -1 (default if none) is provided as
// an argument, all indexed-groups will be evaluated.
// FirstName, LastName, and DateOfBirth will be evaluated as a group, meaning that
// duplicated entities have the same full names and were born on the same day.
// Social Security Number will be evaluated individually.
// Passport Number will also be evaluated individually.
...
public class Cat : TableModel<Cat>
{
protected override string ConnectionString => "Data Source=testmagna.db;New=True;Version=3;";
protected override string TableName => "Cats";
protected override MagnaKey Key => this.MakeKey(cat => cat.Id);
[Identity]
public long Id { get; set; }
public string Name { get; set; }
public string Breed { get; set; }
public DateTime DateOfBirth { get; set; }
[ForeignRelation("<{0}.Id> = {1}.PatientId")]
public List<Checkup> Checkups { get; set; }
}
public class Checkup : TableModel<Checkup>
{
protected override string ConnectionString => "Data Source=testmagna.db;New=True;Version=3;";
protected override string TableName => "Checkups";
protected override MagnaKey Key => this.MakeKey(cat => cat.Id);
[Identity]
public long Id { get; set; }
public DateTime CheckupDate { get; set; }
public string Prescription { get; set; }
public long PatientId { get; set; }
[ForeignRelation("<{0}.PatientId> = {1}.Id")]
public Cat Patient { get; set; }
}
...
[Identity]
public long Id { get; set; }
...
[InsertIgnore]
public DateTime TimeStamp { get; set; }
...
[SelectIgnore]
public string SocialSecurityNumber { get; set; }
...
[UpdateIgnore]
public string SongId { get; set; }
MagnaDB events provide functionalities similar to database triggers.
Whether you're trying a only-code approach for your application, or your
database services are limited, and don't provide you with database triggers;
events are a perfect workaround. And, who knows? They might even provide some
extra functionalities.
The events in MagnaDB are the following:
For ViewModel<T>
using System;
using MagnaDB.SQLite;
namespace TestMagnaDB
{
public class User : TableModel<User>
{
protected override string ConnectionString => "Data Source=testmagna.db;New=True;Version=3;";
protected override string TableName => "Users";
protected override MagnaKey Key => this.MakeKey(user => user.Id);
[Identity]
public long Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public bool EmailIsConfirmed { get; set; }
public User()
{
BeforeInsert += User_BeforeInsert;
InsertSucceeded += User_InsertSucceeded;
}
// The sender parameter receives the invoking object for Insert(),
// Update(), and Delete() methods. For methods retrieving data the
// object(s) resulting from the queries are received as the sender
// parameter instead.
// The MagnaEventArgs contains properties relating to the database
// connection including the transaction (if any), the connection
// (if any), and/or the connection string (if any), the triggering
// method's command was executed against.
public void User_BeforeInsert(object sender, MagnaEventArgs e)
{
PasswordHash = AnExistingClassYouMayHave.HashFunctionThatIsSomewhereElse(PasswordHash);
}
// Notice how this event handler is implemented asynchronously
// In some contexts this can mean performance optimization for
// your application, so don't be afraid to try it.
public async void User_InsertSucceeded(object sender, MagnaEventArgs e)
{
await AnExistingClassYouMightAlsoHave.SendEmailConfirmationAsync(Id);
}
}
}
These are the existing exception types in MagnaDB.
Exception | Description |
---|---|
DbConnectionException | Thrown when an error occurs while connecting to the database or database Server. |
DisparityException | This exception is thrown when a SqlGenerator method is being called with incorrect key/value pairs. |
InvalidModelException | Thrown when the existing model does not correctly match with the database table/view. |
InvalidTableException | Thrown when a Table (or other queryable database object) doesn't exist this exception is raised |
MagnaDB is currently available for the following DB Engines: