I’m wanting to mimic something that I saw in Photon Server with my application. Entity Framework does something similar. Photon Server has an Operation class. You pass it a dictionary of <byte,object>
and it auto fills the class members and also does some data annotation. For example
public class MyCustomOperation : Operation
{
public MyCustomOperation(IRpcProtocol protocol, OperationRequest request) : base(protocol, request)
{
}
[DataMember(Code = 100, IsOptional = false)]
public string Message { get; set; }
}
Now in this, request contains a Dictionary<byte,object>
the byte is what the Data annotation refers to as the Code it somehow takes that dictionary looks up the key that matches what code is set to and assigns Message to the value of the key.
I need to do the same thing and have had no luck finding anything on google (although not sure what this is called) I know the [DataMember] line is a custom data annotation and those I do understand its the part of taking the code in the data annotation and assigning the value to the field under it.
What I know about custom data annotations is using validation with them (which this class also does in Photon via a MyCustomOperation.IsValid although this part I can figure out I think).
Ok I finally figured this out without using reflection as well
DataMemberAttribute class:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DataMemberAttribute : Attribute
{
public byte Code { get; set; }
public bool IsOptional { get; set; }
public string ErrorMessage { get; set; }
}
Operation Class:
public class Operation
{
private readonly List<string> _errorMessages;
protected Operation(Dictionary<byte,object> parameters)
{
_errorMessages = new List<string>();
foreach (var prop in GetType().GetProperties())
{
var da = prop.GetCustomAttributes(typeof(DataMemberAttribute), false) as DataMemberAttribute[];
if (!(da == null || da.Length <= 0))
{
if (parameters.ContainsKey(da[0].Code))
{
prop.SetValue(this, Convert.ChangeType(parameters[da[0].Code], prop.PropertyType), null);
}
else
{
_errorMessages.Add(!string.IsNullOrEmpty(da[0].ErrorMessage) ? da[0].ErrorMessage : string.Format("{0} is Required", prop.Name));
}
}
}
}
public bool IsValid
{
get {
return _errorMessages.Count <= 0;
}
}
public List<string> ErrorMessages
{
get { return _errorMessages; }
}
}
Sample Derived class:
public class Operation2 : Operation
{
public Operation2(Dictionary<byte,object> parameters) : base(parameters)
{}
[DataMember(Code = 1, IsOptional = false, ErrorMessage="Username is Required")]
public string Username { get; set; }
[DataMember(Code = 2, IsOptional = false, ErrorMessage="Password is Required"))]
public string Password { get; set; }
[DataMember(Code = 3, IsOptional = false, ErrorMessage="Email is Required"))]
public string Email { get; set; }
}
Test Program:
class Program
{
static void Main()
{
var paramters = new Dictionary<byte, object> {{1, "someuser"}, {2, "password"}, {3, "[email protected]"}};
var operation = new Operation2(paramters);
var paramters2 = new Dictionary<byte, object> { { 1, "someuser" }, { 10, "password" }, { 3, "[email protected]" } };
var operation2 = new Operation2(paramters2);
Console.WriteLine("Operation 1 Contains:");
Console.WriteLine("Username {0}", operation.Username);
Console.WriteLine("Email {0}", operation.Email);
Console.WriteLine("Password {0}", operation.Password);
Console.WriteLine("operation 1 {0}", operation.IsValid);
Console.WriteLine();
Console.WriteLine("Operation 2 Contains:");
Console.WriteLine("Username {0}", operation2.Username);
Console.WriteLine("Email {0}", operation2.Email);
Console.WriteLine("Password {0}", operation2.Password);
Console.WriteLine("operation 2 {0}", operation2.IsValid);
Console.ReadLine();
}
}
Produces the following output:
Operation 1 Contains:
Username someuser
Email [email protected]
Password password
operation 1 True
Operation 2 Contains:
Username someuser
Email [email protected]
Password
operation 2 False