I looked at this answer and I am in a situation where I don't need to maintain backward compatibility and I have to have a solution that works without having to decorate dozens of classes with the attributes needed for protobuf-net. So I tried using RuntimeTypeModel.Default.InferTagFromNameDefault = true;
but I may be not using it correctly because the Serializer.Serialize call still throws an exception asking for a contract. Here is my quick test, what am I doing wrong?
public enum CompanyTypes
{
None, Small, Big, Enterprise, Startup
}
public class BaseUser
{
public string SSN { get; set; }
}
public class User : BaseUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public DateTime BirthDate { get; set; }
public List<string> Friends { get; set; }
public Company Company { get; set; }
}
public class Company
{
public string Name { get; set; }
public string Address { get; set; }
public CompanyTypes Type { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public string Name { get; set; }
public string Sku { get; set; }
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void SerializeDeserializeTest()
{
var user = new User
{
Age = 10,
BirthDate = DateTime.Now.AddYears(-10),
FirstName = "Test First",
LastName = "Test Last",
Friends = new List<string> { "Bob", "John" },
Company = new Company
{
Name = "Test Company",
Address = "Timbuktu",
Type = CompanyTypes.Startup,
Products = new List<Product>
{
new Product{Name="Nerf Rocket", Sku="12324AC"},
new Product{Name="Nerf Dart", Sku="DHSN123"}
}
}
};
RuntimeTypeModel.Default.InferTagFromNameDefault = true;
using (var memoryStream = new MemoryStream())
{
Serializer.Serialize(memoryStream, user);
var serialized = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
}
}
InferTagFromName
(and it's twin, InferTagFromNameDefault
) only take a hand when it is necessary to resolve a tag number for a member; they don't influence which members need to be serialized (so currently the answer to that would be: none, even if the system knew about them). The option you might have chosen would be ImplicitFields
, but that is currently only available as a [ProtoContract(...)]
marker. If you don't mind a little annotation, a pragmatic fix may be:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
on User
, Company
and Product
, and something a bit more complex for BaseUser
(because of the inheritance):
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic, ImplicitFirstTag = 10)]
[ProtoInclude(1, typeof(User))]
Note we haven't had to add lots of per-member annotation. If you are really really anti-attributes, then it is also possible to configure the entire model through code, via:
RuntimeTypeModel.Default.Add(typeof(Product), false).Add("Name", "Sku");
RuntimeTypeModel.Default.Add(typeof(Company), false).Add("Name", "Address",
"Type", "Products");
RuntimeTypeModel.Default.Add(typeof(User), false).Add("FirstName", "LastName",
"Age", "BirthDate", "Friends", "Company");
RuntimeTypeModel.Default.Add(typeof(BaseUser), false).Add(10, "SSN")
.AddSubType(1, typeof(User));