Moranbernate is a light weight, high performance, high throughput ORM for .Net applications, allowing simple and robust data access for large scale applications.
- Built for high performance with scale and throughput in mind
- Bulk operations for CRUD queries, allowing optimized performance when doing data manipulations
- Operation atomicity - no transactions
- Object based mapping and queries
- Fluent Mapping compatibility - easier to convert from other ORMs
- Composite keys support
- .Net Core support - targeting .Net Standard 1.4 & 2.0
Moranbernate requires mapping the POCO objects that will be used to access the database:
// UserDto.cs
public class UserDto
{
public long Id { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
public DateTime LastLoginDate { get; set; }
}
// UserDtoMap.cs
using OhioBox.Mornabernate.Mapping;
...
public class UserDtoMap : ClassMap<UserDto>
{
public UserDtoMap()
{
Table("users"); // Name of the table
Id(x => x.Id).GeneratedBy.AutoGenerated(); // Declaring an id column, with value that is generated from the database as auto increment column
Map(x => x.Name); // Map property Name to column Name. Note that the casing must be the same if your database is case sensative.
Map(x => x.Age); // Since Age is nullable, the column will also be considered as nullable
Map(x => x.LastLoginDate,"last_login_date"); // Map with custom column name
}
}
To use Moranbernate, initialize it in your application startup:
var assemblies = new[] { GetType().Assembly, ... }; // Put all assemblies that includes ClassMap implementation here
MappingRepoDictionary.InitializeAssemblies(assemblies);
Queries on Moranbernate are done by using extensions on a IDbConnection instance.
using(var conn = new MySqlConnection(ConfigurationManager.ConnectionStrings["MySql"].ConnectionString))
{
conn.Open();
// Run all queries here
}
var user = conn.GetById<UserDto>(1L);
var todaysUsers = conn.Query<UserDto>(q => q
.Where(w => w
.GreaterOrEqual(x => x.LastLoginDate, DateTime.UtcNow.Date))
.OrderByDescending(x => x.LastLoginDate))
.ToArray();
var usersStartingWithY = conn.Query<UserDto>(q => q
.Where(w => w
.StartsWith(x => x.Name, "Y")))
.ToArray();
var newUser = new UserDto { Name = "Max Payne", LastLoginDate = DateTime.UtcNow };
conn.Insert(newUser);
Console.WriteLine(newUser.Id); // Will print the id as set from the DB;
Bulk insert allows you to insert several rows as a single query to the database, avoiding insert loops
var newUsers = new[] { new UserDto {...}, ... };
conn.BulkInsert(newUsers);
Update is very easy with Moranbernate:
var user = conn.GetById<UserDto>(1L);
user.LastLoginDate = DateTime.UtcNow;
conn.Update(user);
By default update is done according to the Id property, as defined in the mapping.
Same result can be achieved by user UpdateByQuery
:
conn.UpdateByQuery<UserDto>(
u => u.Set(x => x.LastLoginDate, DateTime.UtcNow),
q => q.Equal(x => x.Id, 1L));
Delete works the same as update. You can delete an object, which is then deleted by its Id, or by specifing a query, which you can use to delete a bulk of rows at a time:
var user = conn.GetById<UserDto>(1L);
conn.Delete(user);
// And with query:
conn.DeleteByQuery<UserDto>(q => q.LessThan(x => x.LastLoginDate, DateTime.UtcNow.Date - 365));
Moranbernate supports mapping of composite keys:
public class UserPermissionDto : ClassMap<UserPermissionDto>
{
public UserPermissionDto()
{
Table("users_permissions");
CompositeId()
.KeyProperty(x => x.UserId)
.KeyProperty(x => x.PermissionId);
}
}
Moranbernate also support mapping of custom types to columns.
For example, lets assume UserDto
has a list of tags:
public class UserDto
{
...
IList<string> Tags;
}
Moranbernate allows you to create a custom mapper which knows how to resolve and map this time into a single Text column in the DB:
public class CsvPersister<T> : ICustomTypeMapper<T[]>
{
private readonly char[] _separator = new[] { ',' };
public object ToParameter(T[] input)
{
return string.Join(",", input.ToString());
}
public T[] FromDb(object input)
{
return input.ToString()
.Split(_separator, StringSplitOptions.RemoveEmptyEntries)
.Select(x => Convert.ChangeType(x, typeof(T))).OfType<T>()
.ToArray();
}
}
In the mapping, map the column with the new mapper:
public class UserDtoMap : ClassMap<UserDto>
{
public UserDtoMap()
{
...
Map(x => x.Tags).CustomType(new CsvPersister<string>());
}
}
Current version of Mornabernate natively support MySql dialect, and tested with MySql.Data 6.9.9 MySql 5.6 database.
Moranbernate can be expended to support more databases by implementing dialect.
We encorage contribution via pull requests on any feature you see fit.
When submitting a pull request make sure to do the following:
- Check that new and updated code follows OhioBox existing code formatting and naming standard
- Run all unit and integration tests to ensure no existing functionality has been affected
- Write unit or integration tests to test your changes. All features and fixed bugs must have tests to verify they work Read GitHub Help for more details about creating pull requests
Moranbernate has unit tests that covers its logic, and integration tests to test actual queries against a real, live database to validate query functionality.
Running integration tests requires an instance of MySql database on your local machine (can be hosted on Docker of course).
Once an instance is ran, run these from command line:
scripts\import_databases.bat PUT_ROOT_DB_USER_NAME_HERE PUT_ROOT_DB_PASSWORD_HERE
scripts\generate_connection_strings.bat PUT_DB_USER_NAME_HERE PUT_DB_PASSWORD_HERE
The first script generates a schema named moranbernate
on the local instance with the tables required by the tests.
The second one generates a connection string configuration file for the test project which references this database with the given user and password.
Now you can simply run the tests in Visual Studio or with NUnit test runner.