My sample project is a MVC WebApi project.
The standard AuthorizeAttribute takes a Roles = "" or Users = "" parameter.
I didn't have a look at the implementation yet, but I don't want to either :)
Can you make this a little bit more dummy proof with expressions?
In a way that you get a compile time error when you decide to rename the attribute's "Roles" property to anything else?
public interface IControllerContext
{
int MagicNumber { get; }
int ScaryNumber { get; }
string Version { get; }
}
public class AppAuthorizationAttribute : FilterAttribute
{
public IControllerContext SomeControllerContext
{
get; // implementation omitted
}
// ...
}
// This is my sample class which needs to be validated using the attribute.
public class TestClass : BaseClass
{
[AppAuthorization((n) => n.MagicNumber == 13)] // or literally: which property and how to validate it"
protected void SomeMethodWhichRequiresPreProcessingValidation()
{
// do something.
// inside here I have access to an instance of ISomeContext
// and can do anything I want.
}
}
Optional bonus question: Would it somehow be possible to access a field of the current controller (not static) from within the attribute definition? It would be a cool thing to inject a controller field into the validation lambda expression. Something like that: AppAuthorization((controller) => controller.SomePropertyHere.MagicNumer == 13)
Attribute declaration can only use simple types and constants. The compiler will not allow you to use expressions so your planned approach will not work.
You also cannot reference fields or properties, just constants. The closest you could get is something along the lines of
public const MagicNumberPropertyName = "MagicNumber";
public enum Operator
{
Equals
}
[AppAuthorization(MagicNumberPropertyName, Operator.Equals, 13)]
protected void Method() {}
Here is the example , where you can use Enum for Authorise attribute , you can write your logic inside authorise
/// <summary>
/// Class RoleAuthorizeAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RoleAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="RoleAuthorizeAttribute"/> class.
/// </summary>
/// <param name="roles">The roles.</param>
/// <exception cref="System.ArgumentException">The roles parameter may only contain enums;roles</exception>
public RoleAuthorizeAttribute(params object[] roles)
{
if (roles.Any(r => r.GetType().BaseType != typeof (Enum)))
{
throw new ArgumentException(
"The roles parameter may only contain enums",
"roles");
}
Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)).ToList());
}
}
Related
I am using AutoMapper 4.x.
I have a couple of classes as follows:
/// <summary>
/// All service outputs need to descend from this class.
/// </summary>
public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
{
private readonly OzCpResultErrors _OzCpResultErrors;
public OzCpAppServiceOutputBase()
{
_OzCpResultErrors = new OzCpResultErrors();
}
public OzCpResultErrors ResultErrors
{
get { return _OzCpResultErrors; }
}
public bool ResultSuccess
{
get { return _OzCpResultErrors.Messages.Count == 0; }
}
}
/// <summary>
/// Return from the booking service when a simple booking is made.
/// </summary>
public class OzCpSimpleManualCruiseBookingOutput : OzCpAppServiceOutputBase
{
public int OzBookingId { get; set; }
}
}
public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
{
}
My issue comes in when I call AutoMapper to translate between OzCpSimpleManualCruiseBookingOutput and SimpleManualCruiseBookingOutput is that the ResultErrors is cleared.
public SimpleManualCruiseBookingOutput SimpleManualCruiseBooking(SimpleManualCruiseBookingInput aParams)
{
OzCpSimpleManualCruiseBookingOutput result = _PlatformBookingService.SimpleManualBooking(Mapper.Map<OzCpSimpleManualCruiseBookingInput>(aParams));
//**TESTING
result.ResultErrors.AddFatalError(1, "Oh Dear!!!!");
//**As soon as I perform the mapping the ResultErrros collection loses the item I have added above
return Mapper.Map<SimpleManualCruiseBookingOutput>(result);
}
I am guessing it is because it is a read only property, but I cannot figure out how to make it transfer the collection.
Any help greatly appreciated.
EDIT
I have also tried adding the items in the collection myself so changing my mapping from:
Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>();
to using the after map function as follows:
Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>()
.AfterMap((src, dst) => dst.ResultErrors.Messages.AddRange(src.ResultErrors.Messages));
but this then results in the destination having TWO items in the list instead of 1 viz:
which are both the same entry of "Oh Dear!!!!"
SOLUTION
Using the private setter approach suggested by DavidL (and an upgrade to Automapper 4.x) meant I got the required behaviour. So this is what I ended up with:
/// <summary>
/// Defines the contract for all output DTO's to platform
/// application services.
/// </summary>
/// <seealso cref="OzCpAppServiceOutputBase" />
public interface IOzCpAppServiceOutputBase : IOutputDto
{
/// <summary>
/// Contains a list of errors should a call to an application service fail.
/// </summary>
OzCpResultErrors ResultErrors{ get; }
/// <summary>
/// When TRUE the underlying call to the application service was successful, FALSE
/// otherwise. When FALSE see ResultErrors for more information on the error condition.
/// </summary>
bool ResultSuccess { get; }
}
public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
{
public OzCpAppServiceOutputBase()
{
ResultErrors = new OzCpResultErrors();
}
/// <remarks>The private setter is here so that AutoMapper works.</remarks>
public OzCpResultErrors ResultErrors { get; private set; }
public bool ResultSuccess
{
get { return ResultErrors.Messages.Count == 0; }
}
}
So while needing to add a private setter "just for" AutoMapper that is a small price to pay to have this work and not use complicated mappings to deal with the issue.
With the current inheritance structure, AutoMapper will NOT be able to do what you want it to do. Since your destination structure has the same properties as your source structure, the properties are also readonly. AutoMapper will not map to readonly properties that do not have a setter declared.
You have a few options:
Make the property setter explicitly private. This answer suggests that later versions of AutoMapper support this functionality. In this case it works for 4.x.
Make the property setter internal, so that only members of this assembly can set it. Since latest versions of AutoMapper will map to private setters, they should also map to internal setters.
Make the property settable.
Downcast the object instead of mapping (you've mentioned you don't want to do this because your object structures will eventually diverge).
Shadow the property on the destination object with a public setter. Ugly and a good source of strange bugs.
public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
{
public new OzCpResultErrors ResultErrors { get; set; }
}
Create a helper that maps your read-only properties via reflection. DO NOT DO THIS!
PropertyInfo nameProperty = aParams.GetType().GetProperty ("ResultErrors");
FieldInfo nameField = nameProperty.GetBackingField ();
nameField.SetValue (person, aParams.ResultErrors);
I am setting NullDisplayText in the DisplayFormat from resource through the following code
public class LocalizedDisplayFormatAttribute : DisplayFormatAttribute
{
private readonly PropertyInfo _propertyInfo;
public LocalizedDisplayFormatAttribute(string resourceKey, Type resourceType)
: base()
{
this._propertyInfo = resourceType.GetProperty(resourceKey, BindingFlags.Static | BindingFlags.Public);
if (this._propertyInfo == null)
{
return;
}
base.NullDisplayText = (string)this._propertyInfo.GetValue(this._propertyInfo.DeclaringType, null);
}
public new string NullDisplayText
{
get
{
return base.NullDisplayText;
}
set
{
base.NullDisplayText = value;
}
}
}
My default culture used is "en-US",Once I change the culture to es-AR and load the pages its working fine, but when I change the culture back to en-US fields are not getting converted back.
I change the culture throught the following way
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
try
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture");
string culutureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en";
CultureInfo ci = new CultureInfo(culutureCode);
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.Threading.Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(ci.Name);
}
catch
{
}
}
I use DisplayFormat attribute in ViewModel as
public class AlarmCodeDetailsViewModel
{
/// <summary>
/// Gets or sets the alarm code ID
/// </summary>
public int AlarmCodeID { get; set; }
/// <summary>
/// Gets or sets the alarm code
/// </summary>
[LocalizedDisplayName("Label_AlarmCode")]
[LocalizedDisplayFormatAttribute("Warning_NullDisplayText", typeof(Properties.Resources), HtmlEncode = false)]
public string Code { get; set; }
/// <summary>
/// Gets or sets the Description
/// </summary>
[LocalizedDisplayName("Label_Description")]
[LocalizedDisplayFormatAttribute("Warning_NullDisplayText", typeof(Properties.Resources), HtmlEncode = false)]
public string Description { get; set; }
/// <summary>
/// Gets or sets the Notes
/// </summary>
[LocalizedDisplayName("Label_Notes")]
[LocalizedDisplayFormatAttribute("Warning_NullDisplayText", typeof(Properties.Resources), HtmlEncode = false)]
public string Notes { get; set; }
}
Here's some insight into what's wrong.
Mvc is using a form of TypeDescriptor (AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type)) to read attributes off your models. The TypeDescriptors are caching information about properties and attributes. So your LocalizedDisplayFormatAttribute attribute is only getting instantiated once, in terms of the TypeDescriptor api's, which means that the resource information is only read once (at construction). See the bottom of the answer for references.
Solutions that dont work
The blink reaction is to just pull the latest resource information from your LocalizedDisplayFormatAttribute NullDisplayText every time it is accessed via the getter. Unfortunately DisplayFormatAttribute NullDisplayTextis not virtual, and you are shadowing the property with a new keyword. This won't work from a polymorphic dispatch perspective (Mvc is calling the getter as a DisplayFormatAttribute instead of a LocalizedDisplayFormatAttribute, so your shadowed property is never being called)
I tried TypeDescriptor.Refresh() overloads https://msdn.microsoft.com/en-us/library/z1ztz056(v=vs.110).aspx and had no luck
The remaining options that I'm aware of are not as convenient or not amazing in one way or another. Probably not recommended.
Some way to successfully refresh the AssociatedMetadataTypeTypeDescriptionProvider TypeDescriptors. I'm not too familiar with these, so there could totally be one. I'm just not seeing one currently.
Rework or create a ModelMetadataProvider of your own. Everything is open source, so its possible, though I'm not sure I would recommend it except as a last resort.
You could possibly work with the TypeDescriptor api's to force a re-instantiation of your attribute whenever it is being pulled. See https://stackoverflow.com/a/12143653/897291.
Model the needed properties directly in MVC (as model properties, instead of attributes). Could either be entirely new properties, or you could have some sort of logic within your original properties, that when null return something else. Awkward to deal with though.
Nothing great, I know. Maybe this will give someone else enough insight to come up with something better?
To verify this yourself, see
https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs CreateMetaData method which calls SetFromDataTypeAndDisplayAttributes method setting result.NullDisplayText = displayFormatAttribute.NullDisplayText;
DataAnnotationsModelMetadataProvider extends AssociatedMetadataProvider which is repsonsible for passing in the attributes. See https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/AssociatedMetadataProvider.cs GetMetadataForProperty method as an example.
There is ObfuscationAttibute in .NET. But I don't understand, how to exclude code inside constructor from obfuscation.
// Obfuscated class
class MyClass {
[Obfuscation(Exclude = true)] // "Attribute 'Obfuscation' is not valid on this declaration type"
public MyClass() {
//some code, I need to exclude this code from obfuscation
}
// Obfuscated method
public void Method1() {
//some code
|
// Obfuscated method
public void Method2() {
//some code
|
}
UPD: The question is NOT about renaming constructor. It's name obviously became ".ctor". I need to prevent obfuscation of code itself. Yes, some obfuscators not only rename symbols, but change code, too. Yes, I know I can't do it with this attribute. Compiler says the same. I already know what I cannot do. I am asking what i can do, prefferably using only standard .net instruments.
You can do what you want using only ObfuscationAttribute, but it's tedious: apply [Obfuscation(ApplyToMembers=false)] to the class, and [Obfuscation] to every individual member except the constructor.
Alternatively, use your obfuscator's configuration to exclude the constructor from consideration. Since ObfuscationAttribute offers only very limited control (just turning features on and off, basically) most have separate configuration for fine-grained control.
Finally, consider making your constructor so simple and uninteresting that it doesn't matter if the flow is obfuscated or not. This should ideally be the case anyway -- if your constructor only performs member initialization, there isn't much to obfuscate in the first place. You can call member functions for the more involved stuff, and you can control obfuscation of those using the attribute.
Constructors are always renamed to .ctor internally, you can't use an obfuscated name (but you can't use the original name either). And decompilers will name the constructor with the obfuscated class name.
I suppose you mean obfuscation of the code inside the function, not the member name? Presumably a more advanced obfuscator that supports code rearrangement and not just name obfuscation will have its own attribute to control that... because System.Reflection.ObfuscationAttribute isn't suitable for control of more powerful obfuscation techniques.
In particular, the AttributeUsageAttribute on the ObfuscationAttribute class doesn't include AttributeTargets.Constructor as an allowed usage.
I very much agree with the comments noting that obfuscation is not all about renaming, and it does seem to be an oversight that constructors were not considered a valid target for [Obfuscation]. I have run into the problem with dead code removal, where the obfuscator can remove code that is not reachable (I'm simplifying). Overriding this behaviour is sometimes necessary, for example for reflection/serialization scenarios, and can equally apply to constructors as much as any other code element. Note that [AttributeUsage] is only advisory, and is enforced by the C# (or VB) compiler. It is not enforced by the CLR. So if your obfuscator is designed to look for [Obfuscation] on constructors (which might well be the case by accident given that some obfuscator logic might treat all methods equally), and you can use some kind of post-compilation IL processing framework (eg, PostSharp), then you can put [Obfuscation] onto constructors. For PostSharp users, here is a multicast version of ObfuscationAttribute that will happily apply [Obfuscation] to constructors:
using System;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Reflection;
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// A multicast adapter for <see cref="ObfuscationAttribute"/>. Instructs obfuscation tools to take the specified actions for the target assembly, type, or member.
/// </summary>
[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false )]
[MulticastAttributeUsage(
MulticastTargets.Assembly | MulticastTargets.Class | MulticastTargets.Struct | MulticastTargets.Enum | MulticastTargets.Method | MulticastTargets.Property | MulticastTargets.Field | MulticastTargets.Event | MulticastTargets.Interface | MulticastTargets.Parameter | MulticastTargets.Delegate | MulticastTargets.InstanceConstructor | MulticastTargets.InstanceConstructor,
AllowMultiple = true,
PersistMetaData = false)]
public sealed class MulticastObfuscationAttribute : MulticastAttribute, IAspectProvider
{
bool _stripAfterObfuscation = true;
bool _exclude = true;
bool _applyToMembers = true;
string _feature = "all";
bool _stripAfterObfuscationIsSpecified;
bool _excludeIsSpecified;
bool _applyToMembersIsSpecified;
bool _featureIsSpecified;
static readonly ConstructorInfo ObfuscationAttributeCtor = typeof( ObfuscationAttribute ).GetConstructor( Type.EmptyTypes );
IEnumerable<AspectInstance> IAspectProvider.ProvideAspects( object targetElement )
{
var oc = new ObjectConstruction( ObfuscationAttributeCtor );
if ( _applyToMembersIsSpecified )
oc.NamedArguments[ nameof( ObfuscationAttribute.ApplyToMembers ) ] = _applyToMembers;
if ( _excludeIsSpecified )
oc.NamedArguments[ nameof( ObfuscationAttribute.Exclude ) ] = _exclude;
if ( _featureIsSpecified )
oc.NamedArguments[ nameof( ObfuscationAttribute.Feature ) ] = _feature;
if ( _stripAfterObfuscationIsSpecified )
oc.NamedArguments[ nameof( ObfuscationAttribute.StripAfterObfuscation ) ] = _stripAfterObfuscation;
return new[] { new AspectInstance( targetElement, new CustomAttributeIntroductionAspect( oc ) ) };
}
/// <summary>
/// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the obfuscation tool should remove this attribute after processing.
/// </summary>
/// <returns>
/// <see langword="true" /> if an obfuscation tool should remove the attribute after processing; otherwise, <see langword="false" />. The default is <see langword="true" />.
/// </returns>
public bool StripAfterObfuscation
{
get => _stripAfterObfuscation;
set
{
_stripAfterObfuscationIsSpecified = true;
_stripAfterObfuscation = value;
}
}
/// <summary>
/// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the obfuscation tool should exclude the type or member from obfuscation.
/// </summary>
/// <returns>
/// <see langword="true" /> if the type or member to which this attribute is applied should be excluded from obfuscation; otherwise, <see langword="false" />. The default is <see langword="true" />.
/// </returns>
public bool Exclude
{
get => _exclude;
set
{
_excludeIsSpecified = true;
_exclude = value;
}
}
/// <summary>
/// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the attribute of a type is to apply to the members of the type.
/// </summary>
/// <returns>
/// <see langword="true" /> if the attribute is to apply to the members of the type; otherwise, <see langword="false" />. The default is <see langword="true" />.
/// </returns>
public bool ApplyToMembers
{
get => _applyToMembers;
set
{
_applyToMembersIsSpecified = true;
_applyToMembers = value;
}
}
/// <summary>
/// Gets or sets a string value that is recognized by the obfuscation tool, and which specifies processing options.
/// </summary>
/// <returns>
/// A string value that is recognized by the obfuscation tool, and which specifies processing options. The default is "all".
/// </returns>
public string Feature
{
get => _feature;
set
{
_featureIsSpecified = true;
_feature = value;
}
}
}
Why [ObfuscationAttribute] is not allowed on constructors
You can't put [Obfuscation(Exclude = true)] onto a constructor because obfuscation renames symbols, not the contents of methods (usually - more advanced obfuscators can change the flow of code, modify constants, etc. to make reverse-engineering harder).
For example, consider the following:
// obfuscated class
public class MyClass
{
public MyClass()
{
}
public void MyMethod()
{
}
}
// unobfuscated class
public class CallingClass
{
public static void TestMyClass()
{
MyClass class = new MyClass();
class.MyMethod();
}
}
The obfuscator would rename MyClass to something else (e.g. qfghjigffvvb) and MyMethod() to something else (e.g. ghjbvxdghh()) and change all references so that the code still works the same, i.e.
public class qfghjigffvvb
{
public qfghjigffvvb()
{
}
public void ghjbvxdghh()
{
}
}
// unobfuscated class
public class CallingClass
{
public static void TestMyClass()
{
qfghjigffvvb class = new qfghjigffvvb();
class.ghjbvxdghh();
}
}
If you put the [Obfuscation(Exclude = true)] attribute onto the constructor for MyClass, then CallingClass.TestMyClass() would look like this:
public class CallingClass
{
public static void TestMyClass()
{
qfghjigffvvb class = new MyClass(); // ?
class.ghjbvxdghh();
}
}
If you need the contents of the MyClass constructor to not be obfuscated, you'd need to put the [Obfuscation(Exclude = true)] attribute onto everything that it calls so that the symbols won't be renamed.
Thought experiment: excluding the contents of the constructor
Say you had a [ContentObfuscation] attribute which you could use to stop the contents of a method (or constructor or property) from being obfuscated. What would you want it to do here?
public class MyClass
{
[ContentObfuscation(Exclude = true)]
public MyClass()
{
// SecurityCriticalClass is obfuscated
var securityCriticalClass = new SecurityCriticalClass();
securityCriticalClass.DoSomeTopSecretStuff();
}
}
If the contents of the constructor are not obfuscated, then SecurityCriticalClass would also have to be not obfuscated, possibly creating a security issue.
I am trying to access some specific variable that are only available in a child class. But the problem is that I recieve the parent of this class by parameter. Even with casting I can't seem to be able to access the members. Can it be done?
public class ENUMTranslator : ITranslate<RedisData>
{
public string Translate(RedisData message)
{
string bitMask = message.AssociatedParam.ParamDictionary["Bitmask"];
var enumerations = (EnumParams)message.AssociatedParam.EnumDictionary
}
}
The thing is that the data is not in message itself but inside AssociatedParam Which is the parent class of EnumParams.
The EnumDictionary is what I am trying to access that should be in EnumParams, but I just can't access it.
EDIT : Here is the EnumParam class.
message.AssociatedParams
is a GAPParam
public class EnumParams : GAPParam
{
#region Class Members
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
private Dictionary<string, string> _enumDictionary;
#endregion // Class Members
#region Properties
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
public Dictionary<string, string> EnumDictionary
{
get { return _enumDictionary; }
set { _enumDictionary = value; }
}
#endregion // Properties
#region Constructor
/// <summary>
/// Initialise the dictionaries
/// </summary>
public EnumParams()
{
_enumDictionary = new Dictionary<string, string>();
}
#endregion // Constructor
}
I cannot see it with intellisense and it would not compile either.
Well you could cast message.AssociatedParam to an EnumParams:
var enumerations = ((EnumParams)message.AssociatedParam).EnumDictionary
but if message.AssociatedParam is not castable to an EnumParams then it will fail at runtime. Some way to mitigate the risk:
should EnumDictionary be on GAPParam instead? Even if it's virtual or abstract?
should message.AssociatedParam be an EnumDictionary instead of a GAPParam?
do a check before casting to make sure message.AssociatedParam is an EnumParams - but then what do you do if its not?
I've got a WCF DataContract that looks like the following:
namespace MyCompanyName.Services.Wcf
{
[DataContract(Namespace = "http://mycompanyname/services/wcf")]
[Serializable]
public class DataContractBase
{
[DataMember]
public DateTime EditDate { get; set; }
// code omitted for brevity...
}
}
When I add a reference to this service in Visual Studio, this proxy code is generated:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://mycompanyname/services/wcf")]
public partial class DataContractBase : object, System.ComponentModel.INotifyPropertyChanged {
private System.DateTime editDateField;
private bool editDateFieldSpecified;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
this.RaisePropertyChanged("EditDate");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool EditDateSpecified {
get {
return this.editDateFieldSpecified;
}
set {
this.editDateFieldSpecified = value;
this.RaisePropertyChanged("EditDateSpecified");
}
}
// code omitted for brevity...
}
As you can see, besides generating a backing property for EditDate, an additional <propertyname>Specified property is generated. All good, except that when I do the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
the EditDate was not getting picked up by the endpoint of the service (does not appear in the transmitted XML).
I debugged the code and found that, although I was setting EditDate, the EditDateSpecified property wasn't being set to true as I would expect; hence, the XML serializer was ignoring the value of EditDate, even though it's set to a valid value.
As a quick hack I modified the EditDate property to look like the following:
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
// hackhackhack
if (value != default(System.DateTime))
{
this.EditDateSpecified = true;
}
// end hackhackhack
this.RaisePropertyChanged("EditDate");
}
}
Now the code works as expected, but of course every time I re-generate the proxy, my modifications are lost. I could change the calling code to the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
myDataContract.EditDateSpecified = true;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
but that also seems like a hack-ish waste of time.
So finally, my question: does anyone have a suggestion on how to get past this unintuitive (and IMO broken) behavior of the Visual Studio service proxy generator, or am I simply missing something?
It might be a bit unintuitive (and caught me off guard and reeling, too!) - but it's the only proper way to handle elements that might or might not be specified in your XML schema.
And it also might seem counter-intuitive that you have to set the xyzSpecified flag yourself - but ultimately, this gives you more control, and WCF is all about the Four Tenets of SOA of being very explicit and clear about your intentions.
So basically - that's the way it is, get used to it :-) There's no way "past" this behavior - it's the way the WCF system was designed, and for good reason, too.
What you always can do is catch and handle the this.RaisePropertyChanged("EditDate"); event and set the EditDateSpecified flag in an event handler for that event.
try this
[DataMember(IsRequired=true)]
public DateTime EditDate { get; set; }
This should omit the EditDateSpecified property since the field is specified as required
Rather than change the setters of the autogenerated code, you can use an extension class to 'autospecify' (bind the change handler event). This could have two implementations -- a "lazy" one (Autospecify) using reflection to look for fieldSpecified based on the property name, rather than listing them all out for each class in some sort of switch statement like Autonotify:
Lazy
public static class PropertySpecifiedExtensions
{
private const string SPECIFIED_SUFFIX = "Specified";
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically set any xxxSpecified fields when a property is changed. "Lazy" via reflection.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
/// <param name="specifiedSuffix">optionally specify a suffix for the Specified property to set as true on changes</param>
/// <param name="specifiedPrefix">optionally specify a prefix for the Specified property to set as true on changes</param>
public static void Autospecify(this INotifyPropertyChanged entity, string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null)
{
entity.PropertyChanged += (me, e) =>
{
foreach (var pi in me.GetType().GetProperties().Where(o => o.Name == specifiedPrefix + e.PropertyName + specifiedSuffix))
{
pi.SetValue(me, true, BindingFlags.SetField | BindingFlags.SetProperty, null, null, null);
}
};
}
/// <summary>
/// Create a new entity and <see cref="Autospecify"/> its properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="specifiedSuffix"></param>
/// <param name="specifiedPrefix"></param>
/// <returns></returns>
public static T Create<T>(string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) where T : INotifyPropertyChanged, new()
{
var ret = new T();
ret.Autospecify(specifiedSuffix, specifiedPrefix);
return ret;
}
}
This simplifies writing convenience factory methods like:
public partial class MyRandomClass
{
/// <summary>
/// Create a new empty instance and <see cref="PropertySpecifiedExtensions.Autospecify"/> its properties when changed
/// </summary>
/// <returns></returns>
public static MyRandomClass Create()
{
return PropertySpecifiedExtensions.Create<MyRandomClass>();
}
}
A downside (other than reflection, meh) is that you have to use the factory method to instantiate your classes or use .Autospecify before (?) you make any changes to properties with specifiers.
No Reflection
If you don't like reflection, you could define another extension class + interface:
public static class PropertySpecifiedExtensions2
{
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically call each class's <see cref="IAutoNotifyPropertyChanged.Autonotify"/> method on the property name.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
public static void Autonotify(this IAutoNotifyPropertyChanged entity)
{
entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName);
}
/// <summary>
/// Create a new entity and <see cref="Autonotify"/> it's properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Create<T>() where T : IAutoNotifyPropertyChanged, new()
{
var ret = new T();
ret.Autonotify();
return ret;
}
}
/// <summary>
/// Used by <see cref="PropertySpecifiedExtensions.Autonotify"/> to standardize implementation behavior
/// </summary>
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
void WhenPropertyChanges(string propertyName);
}
And then each class themselves defines the behavior:
public partial class MyRandomClass: IAutoNotifyPropertyChanged
{
public void WhenPropertyChanges(string propertyName)
{
switch (propertyName)
{
case "field1": this.field1Specified = true; return;
// etc
}
}
}
The downside to this is, of course, magic strings for property names making refactoring difficult, which you could get around with Expression parsing?
Further information
On the MSDN here
In her answer, Shreesha explains that:
"Specified" fields are only generated on optional parameters that are structs. (int, datetime, decimal etc). All such variables will have additional variable generated with the name Specified.
This is a way of knowing if a parameter is really passed between the client and the server.
To elaborate, an optional integer, if not passed, would still have the dafault value of 0. How do you differentiate between this and the one that was actually passed with a value 0 ? The "specified" field lets you know if the optional integer is passed or not. If the "specified" field is false, the value is not passed across. If it true, the integer is passed.
so essentially, the only way to have these fields set is to set them manually, and if they aren't set to true for a field that has been set, then that field will be missed out in the SOAP message of the web-service call.
What I did in the end was build a method to loop through all the members of the object, and if the property has been set, and if there is a property called name _Specified then set that to true.
Ian,
Please ignore my previous answers, was explaining how to suck eggs. I've voted to delete them.
Could you tell me which version of Visual Studio you're using, please?
In VS2005 client - in the generated code, I get the <property>Specified flags, but no event raised on change of values. To pass data I have to set the <property>Specified flag.
In Visual Web Developer 2008 Express client - in the generated code, I get no <property>Specified flags, but I do get the event on change of value.
Seems to me that this functionality has evolved and the Web Dev 2008 is closer to what you're after and is more intuitive, in that you don't need to set flags once you've set a value.
Bowthy
Here's a simple project that can modify the setters in generated WCF code for optional properties to automatically set the *Specified flags to true when setting the related value.
https://github.com/b9chris/WcfClean
Obviously there are situations where you want manual control over the *Specified flag so I'm not recommending it to everyone, but in most simple use cases the *Specified flags are just an extra nuisance and automatically setting them saves time, and is often more intuitive.
Note that Mustafa Magdy's comment on another answer here will solve this for you IF you control the Web Service publication point. However, I usually don't control the Web Service publication and am just consuming one, and have to cope with the *Specified flags in some simple software where I'd like this automated. Thus this tool.
Change proxy class properties to nullable type
ex :
bool? confirmed
DateTime? checkDate