I have a wcf service, dynamically calling wcf service through wsdl file at client. Now I want to set the properties of a method inside my wcf service. I had properties which are complextypes, and In each complex type I had 30 to 4o complex types. I cannot touch the service, only thing is using reflection assing values to the service datacontract and using methodInfo.Invoke(pass constructed object array). Client will pass input parameters for datacontract in dictionary. Is recursion need to navigate through inner classes of a complextype and set values.
Sample Code:
public CompositeType GetTestDataUsingDataContract(CompositeType composite, string str, int i,EmployeeIn obj)
{
///code here
}
**DataContract for CompositeType**
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
EmployeeIn employeeValue;
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
[DataMember]
public EmployeeIn EmploValue
{
get { return employeeValue; }
set { employeeValue = value; }
}
}
EmployeeIn Class
[DataContract]
public class EmployeeIn
{
bool UserIsOnline = true;
string UserName = "DANGER";
[DataMember]
public bool BoolValue
{
get { return UserIsOnline; }
set { UserIsOnline = value; }
}
[DataMember]
public string StringValue
{
get { return UserName; }
set { UserName = value; }
}
}
Using reflection I want to set these properties.
Here is a working code for setting properties via Reflection with property name provided by string:
void SetPropertyValue(object obj, string propertyPath, object value)
{
System.Reflection.PropertyInfo currentProperty = null;
string[] pathSteps = propertyPath.Split('/');
object currentObj = obj;
for (int i = 0; i < pathSteps.Length; ++i)
{
Type currentType = currentObj.GetType();
string currentPathStep = pathSteps[i];
var currentPathStepMatches = Regex.Match(currentPathStep, #"(\w+)(?:\[(\d+)\])?");
currentProperty = currentType.GetProperty(currentPathStepMatches.Groups[1].Value);
int arrayIndex = currentProperty.PropertyType.IsArray ? int.Parse(currentPathStepMatches.Groups[2].Value) : -1;
if (i == pathSteps.Length - 1)
{
currentProperty.SetValue(currentObj, value, arrayIndex > -1 ? new object[] { arrayIndex } : null);
}
else
{
if (currentProperty.PropertyType.IsArray)
{
currentObj = (currentProperty.GetValue(currentObj) as Array).GetValue(arrayIndex);
}
else
{
currentObj = currentProperty.GetValue(currentObj);
}
}
}
}
Now you can use this function to set properties like, for example:
SetPropertyValue(someClass, "SomeInt", 4);
SetPropertyValue(someClass, "ArrayProperty[5]/Char", (char)2);
SetPropertyValue(someClass, "TestField", new SomeOtherClass());
Related
Is there a way to do something like "string.Compare()", but for generic types. I want to check the range of some property values.
Here is what I am doing as a work around, but it is pretty ugly:
public class SomeClass<T>
{
public T MinValue { get; set; }
public T MaxValue { get; set; }
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
// Restrict range to Min/Max
if (Comparer<T>.Default.Compare(MaxValue, value) < 0)
_value = MaxValue;
if (Comparer<T>.Default.Compare(MinValue, value) > 0)
_value = MinValue;
}
}
}
This code demonstates what I was talking about in my comment. Of course you will have to modify it to fit with your precise paradigm, of using it in a comparer, but this should be clear enough...
public class Program
{
public static void Main(string[] args)
{
System.Console.WriteLine("Hello World!");
TestObject testObject = new TestObject(15);
TestObject testObject2 = new TestObject(9);
TestObject testObject3 = new TestObject(31);
System.Console.ReadLine();
}
}
public class TestObject
{
[ValidateIntMin(Min = 10)]
[ValidateIntMax(30)]
public int SomeInt { get; set; }
public TestObject(int value)
{
SomeInt = value;
if (!Validator.Validate(this))
{
System.Console.WriteLine("Invalid Value assigned: " + value);
}
else
{
System.Console.WriteLine("" + SomeInt + " was a valid value");
}
}
}
public class ValidateIntMax : Attribute
{
public int Max { get; set; }
public ValidateIntMax(int MaxValue)
{
Max = MaxValue;
}
}
public class ValidateIntMin: Attribute
{
public int Min { get; set; }
}
public static class Validator
{
public static bool Validate<T>(T input) {
var attrType = typeof(T);
var properties = attrType.GetProperties();
bool isValid = true;
foreach (PropertyInfo propertyInfo in properties)
{
var customerMaxValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMax), false).FirstOrDefault();
var customerMinValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMin), false).FirstOrDefault();
if (customerMaxValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMax)customerMaxValueInt).Max;
if (currentPropertyInfoBeingTested > currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
if (customerMinValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMin)customerMinValueInt).Min;
if (currentPropertyInfoBeingTested < currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
}
return isValid;
}
}
Should give the output:
Hello World!
15 was a valid value
Invalid Value assigned: 9
Invalid Value assigned: 31
Of course you can add validation for different types, etc.
This is just to show a totally custom way of setting up your attributes.
I recommend you read up on the ValidationAttribute however, to see if you can't use the implemented functionality.
But this is just a PoC piece.
All the objects in our system inherit a base class which has got a property of type object.
I have tried adding protoignore attribute to all the properties of the base class as well but that doesn't seem to work as well.
class Program
{
static void Main(string[] args)
{
Vehicle vehicle = new Vehicle();
vehicle.BodyStyleDescription = "4x4";
vehicle.BodyStyleText = "Prestige Medium";
dynamic protobufModel = TypeModel.Create();
AddTypeToModel<Vehicle>(protobufModel);
using (MemoryStream compressed = new MemoryStream())
{
using (GZipStream gzip = new GZipStream(compressed, CompressionMode.Compress, true))
{
protobufModel.Serialize(gzip, vehicle);
}
string str = Convert.ToBase64String(compressed.GetBuffer(), 0, Convert.ToInt32(compressed.Length));
}
}
public static MetaType AddTypeToModel<T>(RuntimeTypeModel typeModel)
{
var properties = typeof(T).GetProperties().Select(p => p.Name).OrderBy(name => name);
return typeModel.Add(typeof(T), true).Add(properties.ToArray());
}
}
Following is the hierarchy of the object
public interface IObjectBaseClass
{
[ProtoIgnore()]
object Parent { get; set; }
[ProtoIgnore()]
bool IsSaved { get; set; }
[ProtoIgnore()]
string XmlAtLoad { get; set; }
}
public class ObjectBaseClass : IObjectBaseClass
{
public ObjectBaseClass()
{
}
[ProtoIgnore()]
internal object _Parent;
[ProtoIgnore()]
internal bool _IsSaved;
[ProtoIgnore()]
internal string _XmlAtLoad;
[ProtoIgnore()]
public bool IsSaved
{
get { return _IsSaved; }
set { _IsSaved = value; }
}
[ProtoIgnore()]
public object Parent
{
get { return _Parent; }
set { _Parent = value; }
}
[ProtoIgnore()]
public string XmlAtLoad
{
get { return _XmlAtLoad; }
set { _XmlAtLoad = value; }
}
}
public class Vehicle : ObjectBaseClass
{
private string _BodyStyleText;
private string _BodyStyleDescription;
public string BodyStyleDescription
{
get { return _BodyStyleDescription; }
set { _BodyStyleDescription = value; }
}
public string BodyStyleText
{
get { return _BodyStyleText; }
set { _BodyStyleText = value; }
}
}
Your problem is that when you do typeModel.Add(typeof(T), true).Add(properties.ToArray()) you are adding all properties of T to the runtime type model, including those marked with ProtoIgnore. You can see this by calling the debugging method protobufModel.GetSchema(typeof(Vehicle)) which returns:
message Object {
}
message Vehicle {
optional string BodyStyleDescription = 1;
optional string BodyStyleText = 2;
optional bool IsSaved = 3;
optional Object Parent = 4;
optional string XmlAtLoad = 5;
}
To avoid adding properties marked with [ProtoIgnore], you could do:
public static MetaType AddTypeToModel<T>(RuntimeTypeModel typeModel)
{
var properties = typeof(T)
.GetProperties()
.Where(p => !p.GetCustomAttributes<ProtoIgnoreAttribute>().Any())
.Select(p => p.Name)
.OrderBy(name => name);
return typeModel.Add(typeof(T), true).Add(properties.ToArray());
}
Alternatively, since you are manually annotating some of your models with protobuf attributes anyway, you could mark the derived types with [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)], e.g.:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Vehicle : ObjectBaseClass
{
private string _BodyStyleText;
private string _BodyStyleDescription;
public string BodyStyleDescription
{
get { return _BodyStyleDescription; }
set { _BodyStyleDescription = value; }
}
public string BodyStyleText
{
get { return _BodyStyleText; }
set { _BodyStyleText = value; }
}
}
Using either method, the schema for Vehicle becomes:
message Vehicle {
optional string BodyStyleDescription = 1;
optional string BodyStyleText = 2;
}
This is what you require.
Is it possible to have an enum based custom attribute that could be set atop some class property in C#.Net?
Example: I am trying to do the following:
[AutoUpdateEnumParam(new AutoUpdateParamOption[]
{ new AutoUpdateParamOption("Offline", 0),
new AutoUpdateParamOption("Online", 1),
new AutoUpdateParamOption("Deactivated", 2),
new AutoUpdateParamOption("Reset", 3),
new AutoUpdateParamOption("Removed", 4) })]
public int Status
{
get;
set;
}
When I compile the project, it gives me following error:
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
[UPDATE]
Somebody asked me, how I have defined my AutoUpdateEnumParam class. Here it is:
[AttributeUsage(AttributeTargets.Property)]
public class AutoUpdateEnumParam : Attribute
{
AutoUpdateParamOption[] _paramEnum;
public AutoUpdateParamOption[] Options
{
get
{
return _paramEnum;
}
}
public AutoUpdateEnumParam(AutoUpdateParamOption[] paramEnum)
{
this._paramEnum = paramEnum;
}
}
public class AutoUpdateParamOption
{
private string _text = string.Empty;
private object _value = null;
private string _type = "string";
public string Text
{
get
{
return _text;
}
}
public object Value
{
get
{
return _value;
}
}
public string ValueType
{
get
{
return _type;
}
}
public AutoUpdateParamOption(string text, int value)
{
this._text = text;
this._value = value;
this._type = "int";
}
public AutoUpdateParamOption(string text, double value)
{
this._text = text;
this._value = value;
this._type = "double";
}
public AutoUpdateParamOption(string text, string value)
{
this._text = text;
this._value = value;
this._type = "string";
}
public AutoUpdateParamOption(string text, bool value)
{
this._text = text;
this._value = value;
this._type = "boolean";
}
}
My team is developing a WCF service to communicate with database tables. Lately, we have been noticing that if we insert a new record to some of the tables, the integer and Boolean values would not be saved to the record, but strings would work just fine.
It appeared that functions in the service that receive a DataContract class as a parameter would have null values for all their non-string properties.
I have set up a new class to test this out:
[DataContract]
public class MyObject
{
private string _Name;
[DataMember]
public string Name
{
get { return _Name; }
set { _Name = value; }
}
private int? _TestInt;
[DataMember]
public int? TestInt
{
get { return _TestInt; }
set { _TestInt = value; }
}
public MyObject()
{
_Name = "";
_TestInt = 0;
}
}
I have added functions to my service to simply return the value of the properties in the above class:
[OperationBehavior]
public string GetMyName(MyObject myObject)
{
return myObject.Name;
}
[OperationBehavior]
public int? GetMyTestInt(MyObject myObject)
{
return myObject.TestInt;
}
I have configured the service reference on my client application to not reuse types in referenced assemblies.
This is the code I use to test on the client:
MyObject record = new MyObject();
record.Name = "This is Me";
record.TestInt = 5;
int? returnValue = _client.GetMyTestInt(record);
string message;
if (returnValue == null)
message = "Integer value is null.";
else
message = "Integer value is " + returnValue.ToString();
MessageBox.Show(message, _client.GetMyName(record));
The code above shows a message that the integer returned by my service is null, instead of the 5 that I assigned it. GetMyName, however, does return the proper value for my string, which displays as the caption of my message box.
Why is it that the service seems to be receiving null values?
You have to add the [DataMember] attribute to the backing field.
Change your contract like this:
[DataContract]
public class MyObject
{
[DataMember] // data contract on backing field
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
[DataMember] // data contract on backing field
private int? _TestInt;
public int? TestInt
{
get { return _TestInt; }
set { _TestInt = value; }
}
public MyObject()
{
_Name = "";
_TestInt = 0;
}
}
In DataContract Add the Property attribute as
[DataMember(IsRequired = true)]
i want to cast a string from database to a class object without having to go over each possible outcome.
so not
if(type.StartsWith("ContactPersonType")){//} else if(type.StartsWith("ContactPersonTitle")){//}
this is what i have so far
private static T Create<T>(IDataRecord record)
{
var properties = typeof(T).GetProperties();
var returnVal = Activator.CreateInstance(typeof(T));
properties.ToList().ForEach(item =>
{
string type = item.GetMethod.ReturnParameter.ParameterType.Name;
if (type.StartsWith("ContactPerson"))
{
Type t = Type.GetType(item.GetMethod.ReturnParameter.ParameterType.ToString());
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), t));
}
else if (!type.StartsWith("ObservableCollection"))
{
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), item.PropertyType));
}
});
return (T)returnVal;
}
public class ContactPersonType
{
private int _id;
public int ID
{
get { return _id; }
set { _id = value; }
}
private String _name;
public String Name
{
get { return _name; }
set { _name = value; }
}
}
thanks
Use an anonymous collection when you want a certain action to apply to different input values.
foreach(var option in new[] {"ContactPerson", "ContactPersonTitle" }){
if (type.StartsWith(option))
{
Type t = Type.GetType(item.GetMethod.ReturnParameter.ParameterType.ToString());
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), t));
}
}