I’ve just open-sourced my latest pet project. This project was started at around the turn of the year, and its scope has been evolving over the last several months. I started out planning on a framework for rapid development and management of a certain type of web app, but the first hurdle as with anything was picking a favorite DAL methodology. I tend to lean towards O/RMs as I hate manually managed DAL code, but I didn’t want to license anything (it’d mean sublicensing if I redistribute what I produce), I have some issues with LINQ-to-SQL and LINQ-to-Entities, I find nHibernate difficult to swallow, I have some reservations about SubSonic, and I just don’t have time nor interest to keep perusing the many others. Overall, my biggest complaint with all the O/RM offerings, including Microsoft’s, is that they’re too serious. I wanted something lightweight that I don’t have to think about much when building apps.
So as a project in itself I decided first to roll my own O/RM, in my own ideal flavor. Introducing Gemli.Data! It’s a lightweight O/RM that infers as much as possible using reflection, assumes the most basic database storage scenarios, and helps me keep things as simple as possible.
That’s hype-speak to say that this is NOT a “serious O/RM”, it’s intended more for from-scratch prototype projects for those of us who begin with C# classes and want to persist them, and don’t want to fuss with the database too much.
I got the code functioning well enough (currently 92 unit tests, all passing) that I felt it was worth it to go ahead and let other people start playing with it. Here it is!
Gemli Project Home: http://www.gemli-project.org/
Gemli Project Code: http://gemli.codeplex.com/
Gemli.Data is currently primarily a reflection-based mapping solution. Here’s a tidbit sample of functioning Gemli.Data code (this comes from the CodePlex home page for the project):
// attributes only used where the schema is not inferred
// inferred: [DataModelTableMapping(Schema = "dbo", Table = "SamplePoco")]
public class SamplePoco
{
// inferred: [DataModelFieldMapping(ColumnName = "ID", IsPrimaryKey = true, IsIdentity = true,
// IsNullable = false, DataType = DbType.Int32)] // note: DbType.Int32 is SQL type: int
public int ID { get; set; }
// inferred: [DataModelFieldMapping(ColumnName = "SampleStringValue", IsNullable = true,
// DataType = DbType.String)] // note: DbType.String is SQL type: nvarchar
public string SampleStringValue { get; set; }
// inferred: [DataModelFieldMapping(ColumnName = "SampleDecimalValue", IsNullable = true,
// DataType = DbType.Decimal)] // note: DbType.Decimal is SQL type: money
public decimal? SampleDecimalValue { get; set; }
}
[TestMethod]
public void CreateAndDeleteEntityTest()
{
var sqlFactory = System.Data.SqlClient.SqlClientFactory.Instance;
var dbProvider = new DbDataProvider(sqlFactory, TestSqlConnectionString);
// create my poco
var poco = new SamplePoco { SampleStringValue = "abc" };
// wrap and auto-inspect my poco
var dew = new DataModel<SamplePoco>(poco); // data entity wrapper
// save my poco
dew.DataProvider = dbProvider;
dew.Save(); // auto-synchronizes ID
// or...
//dbProvider.SaveModel(dew);
//dew.SynchronizeFields(SyncTo.ClrMembers); // manually sync ID
// now let's load it and validate that it was saved
var mySampleQuery = DataModel<SamplePoco>.NewQuery()
.WhereProperty["ID"].IsEqualTo(poco.ID); // poco.ID was inferred as IsIdentity so we auto-returned it on Save()
var data = dbProvider.LoadModel(mySampleQuery);
Assert.IsNotNull(data); // success!
// by the way, you can go back to the POCO type, too
SamplePoco poco2 = data.Entity; // no typecast nor "as" statement
Assert.IsNotNull(poco2);
Assert.IsTrue(poco2.ID > 0);
Assert.IsTrue(poco2.SampleStringValue == "abc");
// test passed, let's delete the test record
data.MarkDeleted = true;
data.Save();
// ... and make sure that it has been deleted
data = dbProvider.LoadModel(mySampleQuery);
Assert.IsNull(data);
}
Gemli.Data supports strongly typed collections and multiple records, too, of course.
var mySampleQuery = DataModel<SamplePoco>.NewQuery()
.WhereMappedColumn["SampleStringValue"].IsLike("%bc");
var models = dbProvider.LoadModels(mySampleQuery);
SamplePoco theFirstSamplePocoEntity = models.Unwrap<SamplePoco>()[0];
// or.. SamplePoco theFirstSamplePocoEntity = models[0].Entity;
Anyway, go to the URLs above to look at more of this. It will be continually evolving.