Search code examples
c#ormlite-servicestackprotobuf-csharp-port

Is there way to use protobuf-csharp-port generated classes with servicestack.ormlite?


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)?


Solution

  • 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));
            }
        }
    }