I have a ton of C# classes generated using protobuf-csharp-port. I ended up creating my own simple ORM mechanism for them.
Turns out OrmLite is exactly what I want. But I'm now "stuck" with protobuf classes. Biggest issue is that for each entity, I have two classes: EntityClass (which is readonly) and EntityClass.Builder.
Is there any way to integrate OrmLite and protobuf-csharp-port classes (and their builders)?
I managed to make it work like this:
I created a InitProtoTable extension to IDbConnection which needs to be called for every proto, in the very start of the program, like:
var dbFactory = new OrmLiteConnectionFactory(...);
var db = dbFactory.Open();
db.InitProtoTable<Person.Builder>();
After that, one can call OrmLite methods:
db.DropTable<Person.Builder>();
db.CreateTable<Person.Builder>();
The extension looks like this:
public static class OrmLiteExtensions
{
public static void InitProtoTable<B>(this IDbConnection db)
where B : IBuilder, new()
{
var desc = new B().DescriptorForType;
var model = ModelDefinition<B>.Definition;
model.Name = desc.Name;
model.IgnoredFieldDefinitions.Clear();
var fieldList = new List<FieldDefinition>();
var fieldMap = desc.Fields
.ToDictionary(f => f.Name, StringComparer.OrdinalIgnoreCase);
foreach (var field in model.FieldDefinitions)
{
if (fieldMap.ContainsKey(field.Name)) fieldList.Add(field);
}
model.FieldDefinitions = fieldList;
model.AfterInit();
if (db.TableExists<B>())
{
var columns = GetColumnNames<B>(db, model.ModelName);
var missing = model.FieldDefinitions
.Where(field => !columns.Contains(field.FieldName));
foreach (var field in missing)
{
field.DefaultValue = fieldMap[field.Name].DefaultValue.ToString();
db.AddColumn(typeof(B), field);
Console.WriteLine(db.GetLastSql());
}
}
}
private static HashSet<string> GetColumnNames<T>(IDbConnection db, string tableName)
{
using (var cmd = db.CreateCommand())
{
// Workaround to RDMS agnostic table column names discovery.
cmd.CommandText = string.Format("SELECT * FROM {0} WHERE 1!=1", tableName);
var table = new DataTable();
table.Load(cmd.ExecuteReader());
return new HashSet<string>(
table.Columns.OfType<DataColumn>().Select(c => c.ColumnName));
}
}
}