Yes, that’s truth. World now has yet another ORM system written in .NET. Again someone (me) reinvented the wheel. Why?
Before you started abusing me, let me provide you with some insight.
Of course there was need for this ORM to be born
I’ve been working with NHibernate for manner of couple of years. I would say team got used to it. Few months ago we’ve got challenge to integrate some of our code with legacy system. It is written in Delphi, but exposes some .NET stuff. Underlying data provider was legacy cache system (relational) covered with Delphi and bit exposed in .NET, also cache was capable of switching to SQLServer when in connected mode. When we decided to integrate our system with this legacy code we had to come up with solution that utilizes existing WCF (!) entities for mapping, solution that can call legacy code with correct select sql statements depending on connect mode and can handle update/insert/delete for the object graphs utilizing legacy interfaces.
Long story short, we ended very much with these three methods. Some other methods are also available, but I’m showing this for more rush:
object InsertUpdateRow(string tblName, IEnumerable fields, IEnumerable fldsValues, IEnumerable keyFlds);
void DeleteRow(string tblName, IEnumerable fieldsList, IEnumerable fieldsValues, IEnumerable keyFields);
DataTable SelectSQL(string selectSql);
This doesn’t look like exposing a lot of possibilities. We had option to go with naked SQL, than mapping from DataTables to entities reading data with DataReader. And all manual. Isn’t it boring and too-oo-oo much code?
It is fun to work on ORM, so why not take it further
For me it was really nice work to make that whole reflection and data access working smoothly. So I tried to have my code as much abstract as I can so it can be isolated somehow from that legacy and separated code for my personal project very early. Some of the features I had to recode at work, some at home. Instead of “IntegrationDataAccess” I wrote simple AdoDataAccess over mine IDataAccess and got it more or less completed.
I think it is part of my learning curve, to write this project. I’m learning what it means to start something open source, how to use yet another source control. Playing with hardcore reflection and other not-business logic code is real fun.
Another thing why I would like to make it open to the world and not mine home-hidden project is being exposed to more job opportunities. When you are at interview you often have no code to show, because all you’ve written is production and you signed papers forbidding you to show that code.
So… here it is – CustomORM
CustomORM is tiny ORM system written in .NET. CORM is slightly bigger than so-called micro-ORMs and much smaller than NHibernate. It has its uniqueness.
Features at glance:
POCO (real POCO without any “virtual” constraints or attributes)
Mapping much like FluentNH
Very light, thus faster
No syntaxes to learn (nothing like complex criteria or another version of sql)
Source code in your project you can debug, and change quickly
Now, let me show you some code
Here is how select works:
Criteria criteria = CreateCriteria(typeof(Customer), "c")
@"left join Orders o
on c.[Customer ID] = o.[Customer ID]
left join Employees e
on e.[Employee ID] = o.[Employee ID]
where c.[Customer ID] = @customerId")
List<Customer> customers = ExecuteCriteria<Customer>(criteria);
As you can see you add type of entity to take part in join, assign alias to it and then write old known SQL. In the end you get nice collection of objects. No root transformations to be applied.
Here is code you might write for the save:
public Customer SaveCustomer(Customer customer)
var transaction = BeginTransaction();
Customer savedCustomer = null;
savedCustomer = Save(customer);
All this above will work after small setup and mappings for your objects.
class CustomerMap : EntityToTableMapper<Customer>
Id(x => x.CustomerID, "Customer ID")
Map(x => x.CompanyName, "Company Name");
Map(x => x.ContactName, "Contact Name");
Map(x => x.ContactTitle, "Contact Title");
Map(x => x.City, "City");
HasMany(x => x.Orders, "Customer ID");
Get started code
To get started all setup you need is basically two things: derive you own Repository class from RepositoryBase and add bootstrap code at start of your app similar to this:
// Let CORM know where your mappings live and that's mostly it
// Initialize AdoDataAccess or (advanced) implement your own IDataAcces
var s = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];
var ceDataAccess = new AdoDataAccess(s.ConnectionString, s.ProviderName);
// You are ready to use you repository, it already has Fetch<T>, Save, Delete
var customerRepository = new CustomerRepository(ceDataAccess);
Hope it doesn’t look like much code.
What is at codeplex now and what’s next?
As for now I have CRUD operations tested on Northwind SqlCe database. I verified that fetching 91 customers (2000 db records) is 2-3 times faster than NHibernate.
Project still lacks very much in transactions/sessions/concurrency and completely doesn’t have caching (I don’t think it should ever have).
Project has bad code coverage and probably isn’t ready for community review until I polish it. But anyway sharing this with you because someone has to kick-me-off.
If you still would like to take a look at some code please start with CustomORM.Examples => Program for usage code and for engine code go to CustomORM where most of the heavy logic lives in RepositoryBase and EntityToTableMapper.
Depending on feedback and my mood I will decide if it worth investing more of mine time or not.