Here is an example class provided by Marc Gravel in his introduction on how to use Protobuf.net:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
I have some questions, which I could not find the answers for after searching the web:
If on day 1, I know that I don't need the name property [ProtoMember(2)], then if I omit the [ProtoMember(2)] attribute, will Protobut.net ignore that property and not include it in the output serialized data? If true, then when the data is deserialized on the other end - what is Name initialized to - null?
Lets say that all 3 properties are initially serialized as shown above. If in the future, it turns out that the name property [ProtoMember(2)] is no longer required, can the [ProtoMember(2)] attribute be safely omitted so that only the first and third property are serialized? If true, would it be OK to simply leave the attribute numbers as shown (i.e. 1 & 3)? Any caveats if this is the case?
If it is OK to omit a serialization attribute for a property in a class, then what happens if the class definition on the deserialization side is out of sync? For example, say that the deserialization class defines all 3 properties above, but the serialization code only defines 1 and 3? Likewise, if the deserialization code only expects to see properties 1 and 3, but the serialization code sends all 3, would that still work or will it generate an error?
correct; in terms of what it is initialized to - that's usually up to your type, so in this case (since your type doesn't initialize it), yes: null (note: there is also an option in which the constructor can be suppressed, in which case it would be null even if your class had a constructor / initializer)
yes, that's fine (and expected)
the addition and removal of properties is expected and normal, so the library forgives either; when unexpected fields are received, what happens next depends on whether your class implements IExtensible (often by subclassing Extensible) - if it does, the unexpected data is stored separately, so that it can still be queried manually, or (more common) "round tripped" (i.e. if you serialize it again, the extra data is persisted, even though you didn't expect it)
(yes I know you didn't ask a 4) - the library also supports "conditional serialization", where-by properties can be omitted based on conditions - for example public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
Since others may want to know the answer to these questions, I decided to post the question and share my findings.
These questions are actually quite easy to solve with a test program:
class Program
{
static void Main(string[] args)
{
var person = new Person1
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
//
byte[] arr = Serialize(person);
Person2 newPerson = Deserialize(arr);
/*
using (var file = File.Create("person.bin"))
{
Serializer.Serialize(file, person);
}
//
Person newPerson;
using (var file = File.OpenRead("person.bin"))
{
newPerson = Serializer.Deserialize<Person>(file);
}
*/
}
public static byte[] Serialize(Person1 person)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, person);
result = stream.ToArray();
}
return result;
}
public static Person2 Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
return Serializer.Deserialize<Person2>(ms);
}
}
}
[ProtoContract]
class Person1
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
[ProtoContract]
class Person2
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
So, to try out if Protobuf.net will ignore the Name property when serializing, first run the code in debug to see the total number of bytes with all 3 properties serialized. This can be done by setting a breakpoint at the Serialize(person) line and examining the size of arr.
Then, remove the [ProtoMember(2)] attribute from the Name property of the Person1 class. Running the code in debug shows that it leaves it out because the number of bytes is 6 less than before. When the object is then deserialized back to an object of Person2, it then shows that the Name property is initialized to null.
After replacing the [ProtoMember(2)] attribute for the Name property in the Person1 class, remove this same attribute for the Person2 class. After debugging through the code, it shows that after the Deserialize call, the Person2.Name property is set to null.
So, it looks like Protobuf.net was well designed to be quite flexible, efficient, and in some ways is backwards compatible in that it supports removing of obsolete properties.
Related
Is it possible to obtain the value from (lets say a string property) of a class from a custom attribute?
For example:
public class test
{
[EncodeHTML]
public string body { get; set; }
public int id { get; set; }
}
I would want the custom attribute EncodeHTML to be able to obtain the value of the setting value of the "body" property.
I know this can be achieved via the following:
public string body
{
get;
set {
value = HttpUtility.HTMLEncode(this);
}
But was wondering if this could be isolated for re-use across many class properties.
Here is a plain example of the custom attribute:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
sealed class EncodeHTMLAttribute : Attribute
{
public EncodeHTMLAttribute()
{
}
}
It is
var attr = typeof(test).GetProperty("body").GetCustomAttribute<EncodeHTMLAttribute>()
and you can access whatever you want.
To answer the question directly - no, the attributes are just metadata. They have no idea that a runtime instance of the decorated type even exist.
The closest you could do with an attribute is, at runtime, right when the app starts, find all types that have properties marked with that attribute and rewrite the setters code to what you want. It can be done in theory, but is hard, crazy, irresponsible and completely not recommended. Refer to this SO question to jump into the rabbit hole.
Having that said, to solve the underlying problem, you can just make a custom wrapper on a string.
public class HtmlEncodedString
{
public string Value { get; }
public HtmlEncodedString(string value) =>
Value = HttpUtility.HtmlEncode(value);
public static implicit operator string(HtmlEncodedString htmlEncodedString) =>
htmlEncodedString.Value;
public static implicit operator HtmlEncodedString(string value) =>
new HtmlEncodedString(value);
}
That's of course only a sketch. If you're using ASP.NET Core, consider implementing IHtmlContent. If you're allocating a lot of these, maybe making it a value type will decrease the pressure on the GC. Neverminding these details, you now can get reusability by just using this type instead of an attribute on a string.
public class Test
{
public HtmlEncodedString Body { get; set; }
public int Id { get; set; }
}
Because of the implicit operators, transition is seemless:
var test = new Test();
test.Body = "2 < 4";
string s = test.Body;
Console.WriteLine(s);
Console.WriteLine(test.Body);
> 2 < 4
> 2 < 4
Hi I Have a class derived from another class . Like this :
public class Customer
{
public string Date{ get; set; }
public string InstallationNo{ get; set; }
public string SerialNo { get; set; }
}
Then I have created a class named Customer_U which derived from Customer
public class Customer_U:Customer
{
public string Bill{ get; set; }
}
I have simple question. I have google many time but no answer. I have list filled with data like:
List<Customer_U> customer= new List<Customer_U>() I create a excel using this list. In excel colum order is like this :
Bill --- Date --- InstalltionNo --- SerialNo.
Idont want this order. I want "Bill" member to be last columns . How to set order of member when createin a class derived from another class
There is no defined order in a CLR class; it is up to the implementation that is inspecting the metadata of the class to determine how a class is interpreted.
For example, your Excel library is probably using reflection to inspect the class you pass it and reflection makes no guarantees as to the order in which things are processed.
Other implementations such as the WCF DataContractSerializer or ProtoBuf.NET handle order through the use of DataMember.
That said, if your library can handle dynamic or anonymous types then you can use the approach detailed in the other answers. .NET seems to consistently reflect these types in the same order that they were created.
var x = new { Z = "", Y = 1, X = true };
Console.WriteLine(x.GetType().GetProperties().Select(y => y.Name));
However it should be noted that this is an implementation detail and should not be relied upon. I'd see if you library allows you to specify the mapping between properties in your class and columns in your spreadsheet otherwise you might run into weird bugs in the future!
There is no ordering of class members in .NET. Whenever someone iterates over members in a class, they are imposing some order themselves.
The default seems to be shallow-first. So in your case, first all of Customer_U members are enumerated, and then Customer's.
If you do the enumeration yourself, there's nothing easier than simply using your own enumeration method:
class A
{
public string Date { get; set; }
public string SerialNo { get; set; }
}
class B : A
{
public string Bill { get; set; }
public string InstallationNo { get; set; }
}
public static IEnumerable<PropertyInfo> GetProperties(Type type)
{
if (type.BaseType == typeof(object)) return type.GetProperties().OrderBy(i => i.Name);
return GetProperties(type.BaseType)
.Concat
(
type
.GetProperties
(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
.OrderBy(i => i.Name)
);
}
This simple recursive method will output
Date
SerialNo
Bill
InstallationNo
Deep-first, alphabetical. If you don't want the alphabetical sort, you can omit the OrderBys, but note that then the order is simply unspecified, not necessarily the order you used in your class.
You can use this when building your Excel, for example - if there's a way to impose an order in the output data. If there's no way to impose your own order in whatever you're using to output your data, you could do a mapping to a new object based on this data, and hope that it turns out well - however, doing this dynamically is actually quite a bit of work.
As the other answers have pointed out, there is no such thing as a defined order for class properties in .NET.
However, it seems that what you are looking for is not an ordering of the properties themselves, but in fact a way to sort the properties when serializing the objects, e.g. to Excel.
This IS easily implemented using classes from the System.Runtime.Serialization namespace. There are various classes there that could help you control the serialization process, and allow you to be as specific as you want.
The simplest solution would likely be simply applying the DataMember attribute:
[DataContract]
public class Customer
{
[DataMember(Order = 1)]
public string Date{ get; set; }
[DataMember(Order = 2)]
public string InstallationNo{ get; set; }
[DataMember(Order = 3)]
public string SerialNo { get; set; }
}
You can create a new anonymous class using linq:
var x = from costumerItem in YourList
select new { Date = costumerItem.Date, ...and so on };
Afterwards, move this class to the excel.
Create a wrapper list like
var reOrderedCustomer = Customer.select(a => new { a.Date, a.InstallationNo ,
a.SerialNo, a.Bill }).ToList()
Or do this in your first select method which fills Customer list (If you want to avoid anonymous type)
Some sample code below. The interesting/problem case is the Data property in
Mad. This code blows up (null value in the enumerable). Also, it works if i don't use the static attributes but instead the runtime type model, where i put in member.SupportNull = true for the fields (which is the behaviour i want), so what am i missing in the attributes / settings? Google search seems to indicate this is an open issue with probuf-net? That the same functionality is not available via attributes?
As as aside, if someone could suggest a way - i really love the runtime type model, i want to use that everywhere with a nice compiled model... but with it i lose the object versioning that protocol buffers solves! (via explicit tags). Is there any good way to maintain object version compatibility (simply adding fields) without doing all the static notation with fixed tags?
Basically the key thing with the runtime model is the assignment of tag indices and i can't think of a way of handling versions without explicitly specifying the tag indices via attributes...
[ProtoContract]
[ProtoInclude(1, typeof(ing))]
public class Eff
{
[ProtoMember(2)]
public string gg { get; set; }
}
[ProtoContract]
public class ing : Eff
{
[ProtoMember(1)]
public int zz { get; set; }
}
[ProtoContract]
public class Mad
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public IEnumerable<ing> Data { get; set; }
[ProtoMember(3)]
public ing Single { get; set; }
}
private static void Main(string[] args)
{
var obj = new Mad
{
Name = "test"
,Data = new[] { new ing {gg = "ooga", zz = -101},null,new ing()}
,Single = new ing {gg = "abc", zz = -999}
};
var m = new MemoryStream();
Serializer.Serialize(m, obj);
m.Seek(0, SeekOrigin.Begin);
var copy = Serializer.Deserialize<Mad>(m);
}
Short answer, it seems unavailable via attributes.
Workaround i'm doing for now - for every single type of interest(including the whole inheritance hierarchy) - add it to the type model yourself (with default handling so that it processes attributes), then call .GetFields() and set .SupportNull = true for each field (or only the relevant one)
I'm working on a aspx application (C#), where I'm using HttpSessionState to save different objects in code behind between view changes. Some of these objects containts generic lists (List<T>), but those lists doesn't seem to be saved when I'm using an SQL database to store the state (sessionState mode = "SQLServer"). All other properties in the object gets saved, but when I try to retrieve the list, I get empty lists.
The funny thing is that it all works fine if I use sessionState mode = "InProc".
My classes have the Serializable attribute. I'm running C#/.NET 4.0.
Any ideas would be appreciated!
EDIT:
Just to clarify with some code (not verbatim!).
I have the classes I want to save (instances of) to my Session State:
public class MyClass
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public List<MyOtherClass> Property3 { get; set; }
}
public class MyOtherClass
{
public string AnotherProperty1 { get; set; }
public string AnotherProperty2 { get; set; }
}
Then, in my Code Behind - one action saves objects of the previous classes:
public void MyMethod()
{
MyClass myClass = new MyClass()
{
Property1 = "One string",
Property2 = "One other string",
Property3 = new List<MyOtherClass>() { new MyOtherClass() { AnotherProperty1 = "One", AnotherProperty2 = "Ohter" } }
};
HttpContext.Current.Session["MyKey"] = myClass;
}
...and another action will retrieve that object:
public void MyOtherMethod()
{
MyClass myClass = (MyClass)HttpContext.Current.Session["MyKey"];
int c = myClass.Property3.Count; // Will be 0!!
}
So myClass.Property1 and myClass.Property2 will hold the strings I set previously, but myClass.Property3 is an empty list.
The problem you are describing could happen if you
Store a reference to an object in that generic list that is valid when you store it, but
the reference is no longer valid on subsequent requests.
An example would be storing a reference to a control from the Page.Controls collection - this reference would not be valid if you try to retrieve it on another postback. In fact, the reference may just be cleaned up by garbage collection (thus why your list appears to be empty).
Without seeing your actual code, it's tough to say for sure (although your example code does a great job of clarifying what you mean). But this seems likely to me.
I have a module that iterates through the public properties of an object (using Type.GetProperties()), and performs various operations on these properties. However, sometimes some of the properties should be handled differently, e.g., ignored. For example, suppose I have the following class:
class TestClass
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
Now, I would like to be able to specify that whenever my module gets an object of type TestClass, the property Prop2 should be ignored. Ideally I would like to be able to say something like this:
ReflectionIterator.AddToIgnoreList(TestClass::Prop2);
but that obviously doesn't work. I know I can get a PropertyInfo object if I first make an instance of the class, but it doesn't seem right to create an artificial instance just to do this. Is there any other way I can get a PropertyInfo-object for TestClass::Prop2?
(For the record, my current solution uses string literals, which are then compared with each property iterated through, like this:
ReflectionIterator.AddToIgnoreList("NamespaceName.TestClass.Prop2");
and then when iterating over the properties:
foreach (var propinfo in obj.GetProperties())
{
if (ignoredProperties.Contains(obj.GetType().FullName + "." + propinfo.Name))
// Ignore
// ...
}
but this solution seems a bit messy and error-prone...)
List<PropertyInfo> ignoredList = ...
ignoredList.Add(typeof(TestClass).GetProperty("Prop2"));
should do the job... just check whether ignoredList.Contains(propinfo)
Could you add attributes to the properties to define how they should be used? eg
class TestClass
{
public int Prop1 { get; set; }
[Ignore]
public int Prop2 { get; set; }
}