I'm looking at Microsoft's How to: Create a Basic Data Contract for a Class or Structure, but it leaves me with lots of questions.
They provide this very simplistic example:
using System;
using System.Runtime.Serialization;
[DataContract]
public class Person
{
// This member is serialized.
[DataMember]
internal string FullName;
// This is serialized even though it is private.
[DataMember]
private int Age;
// This is not serialized because the DataMemberAttribute
// has not been applied.
private string MailingAddress;
// This is not serialized, but the property is.
private string telephoneNumberValue;
[DataMember]
public string TelephoneNumber
{
get { return telephoneNumberValue; }
set { telephoneNumberValue = value; }
}
}
For my case, I need this to also include another custom class object called ADUser (Active Directory User).
I understand that ADUser has to be marked with the DataContractAttribute, but I do not understand how exactly to go about that.
Here is Microsoft's class again, but this time with the ADUser field added:
using System;
using System.Runtime.Serialization;
[DataContract]
public class Person
{
// This member is serialized.
[DataMember]
internal string FullName;
// This is serialized even though it is private.
[DataMember]
private int Age;
// This is not serialized because the DataMemberAttribute
// has not been applied.
private string MailingAddress;
// This is not serialized, but the property is.
private string telephoneNumberValue;
[DataMember]
public string TelephoneNumber
{
get { return telephoneNumberValue; }
set { telephoneNumberValue = value; }
}
[DataMember]
public ADUser UserInfo { get; set; }
}
I don't really understand how or what all needs to be done to my ADUser class, but I feel certain that private stuff can be left untouched.
How would I need to fix this ADUser class example?
public class ADUser
{
private string first, last, loginID;
public ADUser() {
first = null;
last = null;
loginID = null;
}
private void getInfo() {
// code goes here
// which sets loginID;
}
public void SetName(string first, string last) {
this.first = first;
this.last = last;
getInfo();
}
public string LoginID { get { return loginID; } }
}
As #outcoldman and #EthanLi suggested:
Add the [DataContract] attribute to the ADUser class.
Add a public constructor without arguments.
Choose the fields you want to pass through WCF. Mark all of them with the [DataMember] attribute.
Properties with only getters will fail during serialization: all exposed properties should have both a getter and (public!) setter. So, for example, your LoginID property will fail if you'll try to apply the [DataMember] attribute to it. In this case, consider changing it to be the method.
Related
I have the following issue
Here is third party class which we are used(so i cannot change it)
public class ThirdPartyEmployee
{
public string F_Name { get; set; }
public string L_Name { get; set; }
public DateTime Date_of_birth { get; set; }
public string Telephone1 { get; set; }
public string Telephone2 { get; set; }
public string Position { get; set; }
//..... and so on
}
Also we have our own smaller and better Employee class
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
}
Sometimes we need to convert third party class to our own. There is extension method for it
public static class ThirdPartyExtensions
{
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
Now about the issue. If somebody consider to add some other properties to Employee class he\she can forget to change ConvertTo method. How we can avoid it ? Ideally i would like to have some compilation errors ...
Any suggestions ?
If your Employee class is just a container, there's one simple approach:
public class Employee
{
private readonly string firstName;
public Employee(string firstName)
{
this.firstName = firstName;
}
}
Now your conversion method has no choice but to pass all the arguments, so you get a compiler error when the conversion method isn't updated.
Of course, this still isn't foolproof - if you also care about changing the arguments, this doesn't help much.
And now that we have Roslyn, with great integration in Visual Studio, you can actually make your own compiler errors using a Roslyn analyzer. If you're not afraid of getting your hands dirty, this would be a great opportunity to show how useful something like that can be. Sadly, it's not very easy to use right now, and needs "the right kind of thinking" to be wielded well. It will allow you to make rules like "a class conversion extension method must assign all properties in the resulting class", for example.
You can not create a compilation error with standard means. There may be Visual Studio plugins that allow you to do that.
But it may not be necessary: You could change the CopyTo method so that instead of hardcoding all the properties to be copied, it uses reflection to obtain a list of all public properties to copy.
Example code to start with:
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
i.SetValue(destination, fi.GetValue(source));
}
To handle different property names: You could introduce an attribute which allows you to specify which property of ThirdPartyEmployee translates to which property of Employee. This can also be evaluated using reflection.
Example:
public class Employee
{
[CopyFromThirdPartyEmployee("F_Name")]
public string FirstName { get; set; }
[CopyFromThirdPartyEmployee("L_Name")]
public string LastName { get; set; }
[CopyFromThirdPartyEmployee("Telephone1")]
public string MobileTelephone { get; set; }
}
You could have the CopyTo method throw an exception when it finds a public property which does not have the required mapping attribute. That way you could be sure that every property also has the attribute - but that would be a runtime error, not a compile time error.
Another approach be to simply make Employee a wrapper for ThirdPartyEmployee:
public class Employee
{
private ThirdPartyEmployee _baseEmployee;
public Employee() { _baseEmployee = new ThirdPartyEmployee(); }
public Employee(ThirdPartyEmployee e) { _baseEmployee = e; }
public string FirstName
{
get { return _baseEmployee.F_Name; }
set { _baseEmployee.F_Name = value; }
}
...
}
That way you'd notice that if you can't access a property you haven't implemented it. The downside is that every employee would then be based on a ThirdPartyEmployee.
You can do this with the help of reflection, but dictionary for names mapping is needed:
public static class ThirdPartyExtensions
{
static Dictionary<string, string> map;
static ThirdPartyExtensions()
{
map = new Dictionary<string, string>{ {"F_Name", "FirstName"} /*and others*/};
}
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
if(map.Count < typeof(Employee).GetProperties().Count())
throw new Exception("Forget to add mapping for new field!");
foreach(var prop in typeof(ThirdPartyEmployee).GetProperties())
if(map.ContainsKey(prop.Name))
{
var temp = typeof(Employee).GetProperty(map[prop.Name]);
temp.SetValue(result, prop.GetValue(thirdPartyEmployee));
}
return result;
}
}
Using Roslyn analyzers it's possible to produce compile-time (and IntelliSense) errors that go beyond the scope of the compiler. Here's a very simple implementation of an analyzer that checks that all the properties of a type returned by a method have been assigned. It doesn't take into account control flow (e.g. if).
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AssignAllPropertiesAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("AssignAllPropertiesAnalyzer",
"All properties must be assigned.", "All properties of the return type must be assigned.", "Correctness",
DiagnosticSeverity.Warning, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
var methodNode = (MethodDeclarationSyntax)context.Node;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode);
if (methodSymbol.GetReturnTypeAttributes().Any(x => x.AttributeClass.Name == "AssignAllPropertiesAttribute"))
{
var properties = methodSymbol.ReturnType.GetMembers().OfType<IPropertySymbol>().Where(x => !x.IsReadOnly).ToList();
foreach (var assignmentNode in methodNode.DescendantNodes().OfType<AssignmentExpressionSyntax>())
{
var propertySymbol = context.SemanticModel.GetSymbolInfo(assignmentNode.Left).Symbol as IPropertySymbol;
if (propertySymbol != null)
{
properties.Remove(propertySymbol);
}
}
if (properties.Count > 0)
{
var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0]);
context.ReportDiagnostic(diagnostic);
}
}
}
The analyzer assumes an attribute named AssignAllProperties is applied to return type of a method. In the following example, ~~~~~~ marks the location where analyzer would produce a diagnostic.
class A
{
public string S { get; set; }
}
[return: AssignAllProperties]
public static A Create()
~~~~~~
{
return new A();
}
An analyzer can be installed both as a VSIX and as a NuGet package. I would recommend always using the NuGet approach - it would apply the analyzer for everyone consuming the code and would allow you to change the severity (e.g. to error), thus failing the compilation. To get started with building an analyzer library, install the Roslyn SDK and create an Analyzer with Code Fix C# project.
You cannot generate a compile error for that, but... I would move the conversion method to the Employee class. I suggest to avoid Extension methods that are dependend on data (like properties from other classes)
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
public static Employee From(ThirdPartyEmployee employee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
This way you keep all functionality in the right class/file and it is clear for others if they add properties.
I have an interface with a read only property
public interface IPerson
{
string Name { get; }
}
and a concrete class...
public class Person : IPerson
{
public Person()
{
Name = "Person";
}
public string Name
{
get
{
return Name;
}
}
}
I want Name to be read only externally to this class, but how do I set it from within the concrete class?
Error: Person.Name cannot be assigned to.
How do I set the value of this property from within Person class?
This doesn't have anything to do with the interface, you're just declaring the property incorrectly. In C# 6, you can create a read-only property like this:
public class Person : IPerson
{
public Person()
{
Name = "Person";
}
public string Name { get; }
}
In earlier versions, you can use a read-only backing field which you can set:
public class Person : IPerson
{
private readonly string _name;
public Person()
{
_name = "Person";
}
public string Name
{
get { return _name; }
}
}
Note that the interface only requires the property has a getter, the implementation doesn't have to be read-only. You could add a setter if you had reason to modify the value:
public class Person : IPerson
{
public Person()
{
Name = "Person";
}
public string Name { get; set; }
}
The setter could be private if you only needed to be able to change the value from within the class.
You can use a private property to hold the value.
public class Person : IPerson
{
private string _name;
public Person()
{
_name = "Person";
}
public string Name
{
get
{
return _name;
}
}
}
Right now, you're trying to read the property by reading the property. Needless to say, this will result in an endless loop. Instead, you either need to use full-blown auto-properties, or a manual backing field.
public class Person : IPerson
{
public Person()
{
Name = "Person";
}
public string Name { get; private set; }
}
You simply have a private setter (pre c#6):
public class Person : IPerson
{
public Person()
{
Name = "Person";
}
public string Name { get; private set; }
}
Try using accessors:
private string _name;
public string Name
{
get
{
return _name;
}
}
You can then set the value of _name inside any method or the constructor.
I want Name to be read only externally to this class, but how do I set it from within the concrete class
First of all, let's realize that: an interface only describes a set of public requirements; it does not prevent us from implementing other public members, nor does it limit our ability to create private members.
Therefore, to make a property writeable from inside the class but read-only elsewhere, we can declare the set method with private scope:
public class Person : IPerson
{
// Name is read-only outside the class, but can be set internally
public string Name { get; private set; } = "DefaultName";
public Person() { }
public Person(string name)
{
// Potentially do some validation before setting the name
if (!IsValidName(name))
throw new ArgumentException("Name cannot be null, empty, or whitespace.");
Name = name;
}
private bool IsValidName(string name)
{
return !string.IsNullOrWhitespace(name);
}
// Continued below...
And if we want to, we can give access to the private setter through a method. This is often done in order to make the renaming of a person very intentional (for example, it prevents accidental assignments when a comparison was intended). The client will get a design-time error from the compiler if they try person.Name = "Foo"; but they can write person.RenameAs("Foo");
public void RenameAs(string newName)
{
// Potentially do some validation before setting the name
if (IsValidName(newName)) Name = newName;
}
}
I am building a library for Windows Phone 8 which requires local databases. For obvious reasons, user of the library is going to create a mappable LINQ-to-SQL class with appropriate [Table]s and [Column]s. However, To in every such class, I need a few more columns for internal functioning of the library. The idea was that I would include a base class in the library which will have members corresponding to the required columns. The user would simply inherit from this class, add his own members and use that class as final LINQ-to-SQL map.
So far, my base class looks like this:
//Class to contain all the essential members
[Table]
public class SyncableEntityBase : NotifyBase, ISyncableBase
{
[Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)]
public int ItemId { get; set; }
[Column]
public bool IsDeleted { get; set; }
[Column]
public DateTime RemoteLastUpdated { get; set; }
[Column]
public DateTime LocalLastUpdated { get; set; }
}
And the derived class, something like this:
[Table]
public class ToDoCategory : SyncableEntityBase
{
private string _categoryName;
[Column]
public string CategoryName
{
get
{
return _categoryName;
}
set
{
if (_categoryName != value)
{
NotifyPropertyChanging("CategoryName");
_categoryName = value;
NotifyPropertyChanged("CategoryName");
}
}
}
private string _categoryColor;
[Column]
public string CategoryColor
{
get
{
return _categoryColor;
}
set
{
if (_categoryColor != value)
{
NotifyPropertyChanging("CategoryColor");
_categoryColor = value;
NotifyPropertyChanged("CategoryColor");
}
}
}
}
Idea is to have final class with the four essential columns and two added by user. According to MSDN documentation here, I need to append [InheritanceMapping] which requires the inherited type. However, as I am building a library, I have no way to know what types (and how many) the user will derive from my base class. Is there any way around this? How?
I have two classes (Person and Address) that i need to send via wcf, the classes look like this:
public class PersoanaFizica :IExtensibleDataObject
{
[DataMember]
private Guid _id;
[DataMember(Name = "Id")]
protected virtual Guid Id
{
get { return _id; }
set { _id = value; }
}
private ExtensionDataObject _extensionData;
public virtual ExtensionDataObject ExtensionData
{
get
{
return _extensionData;
}
set
{
_extensionData = value;
}
}
private string _firstName;
[Searchable(PropertyName="FirstName")]
[DataMember]
public virtual string FirstName
{
get { return this._firstName; }
set { this._firstName = value; }
}
private string _lastName;
[Searchable(PropertyName="LastName")]
[DataMember]
public virtual string LastName
{
get { return this._lastName; }
set { this. _lastName = value; }
}
private Address _address;
[Searchable(PropertyName="Address")]
[DataMember]
public virtual Address Address
{
get { return this._address; }
set { this._address = value; }
}
}
public class Address : IExtensibleDataObject
{
[DataMember]
private Guid _id;
[DataMember]
public virtual Guid Id
{
get { return _id; }
set { _id = value; }
}
private ExtensionDataObject _extensionData;
public virtual ExtensionDataObject ExtensionData
{
get
{
return _extensionData;
}
set
{
_extensionData = value;
}
}
private string _country;
[Searchable(PropertyName="Country")]
[DataMember]
public virtual string Country
{
get { return this._country; }
set { this._country = value; }
}
// and some other properties related to the address
}
The problem is that when i try to send them via wcf, the client receives the Id properties set to 00000-0000-00000-00000 or something like this.
Any idea why this is happening? And how to serialize the proper values?
nope... did not help. I just looked at
the code generated by svcutil.exe
(thats what i use to generate my
client) and i couldn't find an Id
property at all. I tried changing the
protected keyword to public and
everything worked fine. It seems that
the problem is with the fact that i
need the properties to be protected
and not public.
Denis - any chance at all that your WCF client uses the XmlSerializer (which only serializes public read/write properties with a get and set method) instead of the DataContractSerializer?? The DataContractSerializer would definitely serialize a protected property or field - it really doesn't care about the .NET visibility modifiers....
You should see this in the WCF client side proxy being generated - do you have [DataContract] and [DataMember] attributes there, or do you see [XmlElement] and so forth?? Does your class in the WCF client side proxy have a [DataContractAttribute] or a [XmlTypeAttribute] on it??
You should not be marking both the field and the property with DataMember attributes! I have a feeling this is probably what's causing the issue you're seeing but I don't know that for sure. But basically by marking both the field and its backing property as DataMember's you are serializing the value twice and it will be deserialized twice and depending upon how your client-side code is generated this may even result in storing the value twice.
So long story short, mark either your fields as DataMember or the properties, but not both. Marking the fields may require you to specify a Name on the DataMemberAttribute in order for the client-side code generation to create the expected property names.
You don't initialize the properties in some way, so they have their default value assigned. If you assign a value to them, that should be properly send.
I am trying to serialize some objects using XmlSerializer and inheritance but I am having some problems with ordering the outcome.
Below is an example similar to what I have setup: ~
public class SerializableBase
{
[XmlElement(Order = 1)]
public bool Property1 { get; set;}
[XmlElement(Order = 3)]
public bool Property3 { get; set;}
}
[XmlRoot("Object")]
public class SerializableObject1 : SerializableBase
{
}
[XmlRoot("Object")]
public class SerializableObject2 : SerializableBase
{
[XmlElement(Order = 2)]
public bool Property2 { get; set;}
}
The outcome I want is as follows: ~
<Object>
<Property1></Property1>
<Property2></Property2>
<Property3></Property3>
</Object>
However I am getting an outcome of: ~
<Object>
<Property1></Property1>
<Property3></Property3>
<Property2></Property2>
</Object>
Does anyone know if it is possible or of any alternative?
Thanks
Technically, from a pure xml perspective, I would say that this is probably a bad thing to want to do.
.NET hides much of the complexity of things like XmlSerialization - in this case, it hides the schema to which your serialized xml should conform.
The inferred schema will use sequence elements to describe the base type, and the extension types. This requires strict ordering -- even if the Deserializer is less strict and accepts out of order elements.
In xml schemas, when defining extension types, the additional elements from the child class must come after the elements from the base class.
you would essentially have a schema that looks something like (xml-y tags removed for clarity)
base
sequence
prop1
prop3
derived1 extends base
sequence
<empty>
derived2 extends base
sequence
prop2
There's no way to stick a placeholder in between prop1 and prop3 to indicate where the properties from the derived xml can go.
In the end, you have a mismatch between your data format and your business object. Probably your best alternative is to define an object to deal with your xml serialization.
For example
[XmlRoot("Object")
public class SerializableObjectForPersistance
{
[XmlElement(Order = 1)]
public bool Property1 { get; set; }
[XmlElement(Order = 2, IsNullable=true)]
public bool Property2 { get; set; }
[XmlElement(Order = 3)]
public bool Property3 { get; set; }
}
This separates your xml serialization code from your object model. Copy all the values from SerializableObject1 or SerializableObject2 to SerializableObjectForPersistance, and then serialize it.
Essentially, if you want such specific control over the format of your serialized xml that doesn't quite jive with the expectations xml serialization framework, you need to decouple your business object design (inheritance structure in this case) and the responsibility for serialization of that business object.
EDIT: This approach doesn't work. I've left the post in so that people can avoid this line of thinking.
The serializer acts recursively. There's a benefit to this; on deserialization, the deserialization process can read the base class, then the derived class. This means that a property on the derived class isn't set before the properties on the base, which could lead to problems.
If it really matters (and I'm not sure why it's important to get these in order) then you can try this --
1) make the base class' Property1 and Property3 virtual.
2) override them with trivial properties in your derived class. Eg
public class SerializableBase
{
[XmlElement(Order = 1)]
public virtual bool Property1 { get; set;}
[XmlElement(Order = 3)]
public virtual bool Property3 { get; set;}
}
[XmlRoot("Object")]
public class SerializableObject1 : SerializableBase
{
}
[XmlRoot("Object")]
public class SerializableObject2 : SerializableBase
{
[XmlElement(Order = 1)]
public override bool Property1
{
get { return base.Property1; }
set { base.Property1 = value; }
}
[XmlElement(Order = 2)]
public bool Property2 { get; set;}
[XmlElement(Order = 3)]
public override bool Property3
{
get { return base.Property3; }
set { base.Property3 = value; }
}
}
This puts a concrete implementtion of the property on the most derived class, and the order should be respected.
It looks like the XmlSerializer class serializes the base type and then derived types in that order and is only respecting the Order property within each class individually. Even though the order is not totally what you want, it should still Deserialize properly. If you really must have the order just like that you will need to write a custom xml serializer. I would caution against that beacuse the .NET XmlSerializer does a lot of special handling for you. Can you describe why you need things in the order you mention?
This post is quite old now, but I had a similar problem in WCF recently, and found a solution similar to Steve Cooper's above, but one that does work, and presumably will work for XML Serialization too.
If you remove the XmlElement attributes from the base class, and add a copy of each property with a different name to the derived classes that access the base value via the get/set, the copies can be serialized with the appropriate name assigned using an XmlElementAttribute, and will hopefully then serialize in the default order:
public class SerializableBase
{
public bool Property1 { get; set;}
public bool Property3 { get; set;}
}
[XmlRoot("Object")]
public class SerializableObject : SerializableBase
{
[XmlElement("Property1")]
public bool copyOfProperty1
{
get { return base.Property1; }
set { base.Property1 = value; }
}
[XmlElement]
public bool Property2 { get; set;}
[XmlElement("Property3")]
public bool copyOfProperty3
{
get { return base.Property3; }
set { base.Property3 = value; }
}
}
I also added an Interface to add to the derived classes, so that the copies could be made mandatory:
interface ISerializableObjectEnsureProperties
{
bool copyOfProperty1 { get; set; }
bool copyOfProperty2 { get; set; }
}
This is not essential but means that I can check everything is implemented at compile time, rather than checking the resultant XML. I had originally made these abstract properties of SerializableBase, but these then serialize first (with the base class), which I now realise is logical.
This is called in the usual way by changing one line above:
public class SerializableObject : SerializableBase, ISerializableObjectEnsureProperties
I've only tested this in WCF, and have ported the concept to XML Serialization without compiling, so if this doesn't work, apologies, but I would expect it to behave in the same way - I'm sure someone will let me know if not...
I know this question has expired; however, here is a solution for this problem:
The name of the method should always begin with ShouldSerialize and then end with the property name. Then you simply need to return a boolean based on whatever conditional you want, as to whether to serialize the value or not.
public class SerializableBase
{
public bool Property1 { get; set;}
public bool Property2 { get; set;}
public bool Property3 { get; set;}
public virtual bool ShouldSerializeProperty2 { get { return false; } }
}
[XmlRoot("Object")]
public class SerializableObject1 : SerializableBase
{
}
[XmlRoot("Object")]
public class SerializableObject2 : SerializableBase
{
public override bool ShouldSerializeProperty2 { get { return true; } }
}
The outcome when using SerializableObject2: ~
<Object>
<Property1></Property1>
<Property2></Property2>
<Property3></Property3>
</Object>
The outcome when using SerializableObject1: ~
<Object>
<Property1></Property1>
<Property3></Property3>
</Object>
Hope this helps many others!
Like Nader said, maybe think about making a more loose-coupled design. However, in my case, loose-coupling was not appropriate. Here's my class hierarchy, and how I propose to solve the problem without using custom serialization or DTOs.
In my project, I'm constructing a whole bunch of objects to represent pieces of an XML document that will be submitted via a web service. There are a very large number of pieces. Not all are sent with every request (actually, in this example, I'm modeling a response, but the concepts are the same). These pieces are used much like building blocks to assemble a request (or disassemble a response, in this case). So here's an example of using aggregation/encapsulation to accomplish the desired ordering despite the inheritance hierarchy.
[Serializable]
public abstract class ElementBase
{
// This constructor sets up the default namespace for all of my objects. Every
// Xml Element class will inherit from this class.
internal ElementBase()
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:my-default-namespace:XSD:1")
});
}
[XmlNamespacesDeclaration]
public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
private XmlSerializationNamespaces _namespaces;
}
[Serializable]
public abstract class ServiceBase : ElementBase
{
private ServiceBase() { }
public ServiceBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null)
{
this._requestId = requestId;
this._asyncRequestId = asyncRequestId;
this._name = name;
}
public Guid RequestId
{
get { return this._requestId; }
set { this._requestId = value; }
}
private Guid _requestId;
public Guid? AsyncRequestId
{
get { return this._asyncRequestId; }
set { this._asyncRequestId = value; }
}
private Guid? _asyncRequestId;
public bool AsyncRequestIdSpecified
{
get { return this._asyncRequestId == null && this._asyncRequestId.HasValue; }
set { /* XmlSerializer requires both a getter and a setter.*/ ; }
}
public Identifier Name
{
get { return this._name; }
set { this._name; }
}
private Identifier _name;
}
[Serializable]
public abstract class ServiceResponseBase : ServiceBase
{
private ServiceBase _serviceBase;
private ServiceResponseBase() { }
public ServiceResponseBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null, Status status = null)
{
this._serviceBase = new ServiceBase(requestId, asyncRequestId, name);
this._status = status;
}
public Guid RequestId
{
get { return this._serviceBase.RequestId; }
set { this._serviceBase.RequestId = value; }
}
public Guid? AsyncRequestId
{
get { return this._serviceBase.AsyncRequestId; }
set { this._serviceBase.AsyncRequestId = value; }
}
public bool AsynceRequestIdSpecified
{
get { return this._serviceBase.AsyncRequestIdSpecified; }
set { ; }
}
public Identifier Name
{
get { return this._serviceBase.Name; }
set { this._serviceBase.Name = value; }
}
public Status Status
{
get { return this._status; }
set { this._status = value; }
}
}
[Serializable]
[XmlRoot(Namespace = "urn:my-default-namespace:XSD:1")]
public class BankServiceResponse : ServiceResponseBase
{
// Determines if the class is being deserialized.
private bool _isDeserializing;
private ServiceResponseBase _serviceResponseBase;
// Constructor used by XmlSerializer.
// This is special because I require a non-null List<T> of items later on.
private BankServiceResponse()
{
this._isDeserializing = true;
this._serviceResponseBase = new ServiceResponseBase();
}
// Constructor used for unit testing
internal BankServiceResponse(bool isDeserializing = false)
{
this._isDeserializing = isDeserializing;
this._serviceResponseBase = new ServiceResponseBase();
}
public BankServiceResponse(Guid requestId, List<BankResponse> responses, Guid? asyncRequestId = null, Identifier name = null, Status status = null)
{
if (responses == null || responses.Count == 0)
throw new ArgumentNullException("The list cannot be null or empty", "responses");
this._serviceResponseBase = new ServiceResponseBase(requestId, asyncRequestId, name, status);
this._responses = responses;
}
[XmlElement(Order = 1)]
public Status Status
{
get { return this._serviceResponseBase.Status; }
set { this._serviceResponseBase.Status = value; }
}
[XmlElement(Order = 2)]
public Guid RequestId
{
get { return this._serviceResponseBase.RequestId; }
set { this._serviceResponseBase.RequestId = value; }
}
[XmlElement(Order = 3)]
public Guid? AsyncRequestId
{
get { return this._serviceResponseBase.AsyncRequestId; }
set { this._serviceResponseBase.AsyncRequestId = value; }
}
[XmlIgnore]
public bool AsyncRequestIdSpecified
{
get { return this._serviceResponseBase.AsyncRequestIdSpecified; }
set { ; } // Must have this for XmlSerializer.
}
[XmlElement(Order = 4)]
public Identifer Name
{
get { return this._serviceResponseBase.Name; }
set { this._serviceResponseBase.Name; }
}
[XmlElement(Order = 5)]
public List<BankResponse> Responses
{
get { return this._responses; }
set
{
if (this._isDeserializing && this._responses != null && this._responses.Count > 0)
this._isDeserializing = false;
if (!this._isDeserializing && (value == null || value.Count == 0))
throw new ArgumentNullException("List cannot be null or empty.", "value");
this._responses = value;
}
}
private List<BankResponse> _responses;
}
So, while I have to create properties for all of the contained classes, I can delegate any custom logic I might have within the contained class(es) property setters/getters by simply using the contained class's properties when the leaf class's properties are accessed. Since there's no inheritance, I can decorate all the properties of the leaf class with the XmlElementAttribute attribute and use any ordering that I see fit.
UPDATE:
I came back to revisit this article because my design decisions about using class inheritance came back to bite me again. While my solution above does work, I'm using it, I really think that Nader's solution is the best and should be considered before the solution I presented. In fact, I'm +1'ing him today! I really like his answer, and if I ever have the opportunity to refactor my current project, I will definitely be separating the business object from the serialization logic for objects that would otherwise benefit greatly from inheritance in order to simplify the code and make it easier for others to use and understand.
Thanks for posting your response Nader, as I think many will find it very instructive and useful.