Is my understanding of PSPropertyAdapter correct? - c#

I am writing a binary PowerShell module that, much like the ActiveDirectory module, will have a number of cmdlets and types that can potentially return more than the default set of properties - and those properties (100+) are dependent on what the user requests. Just like the AD module, I would like to return the type with the properties that were requested so that it is transparent to the user i.e. not one huge class with lots of empty properties that they haven't requested.
I was looking at the documentation for the ActiveDirectory module and I noticed that things like ADUser and ADComputer ultimately are inherited from ADPropertyCollection which I assume has the properties required in its InnerDictionary after whatever searches it does.
This did not really explain how PowerShell presents the AD types flexibly i.e.
(Get-ADUser -Identity some.user).GetType()
(Get-ADUser -Identity some.user -Properties *).GetType()
# Both return a type of ADUser, despite have drastically different amounts of properties.
This was until I looked at the Types ps1xml file for the AD module, which presents something like this:
<Types>
<!-- other types -->
<Type>
<Name>Microsoft.ActiveDirectory.Management.ADUser</Name>
<TypeAdapter>
<TypeName>Microsoft.ActiveDirectory.Management.ADEntityAdapter</TypeName>
</TypeAdapter>
</Type>
</Types>
So I guess the source of the "magic" properties is ADEntityAdapter which is inherited from the abstract class PSPropertyAdapter.
The issue I have is now I am not sure how to implement it and there are not any easily searchable examples of it being implemented. I appreciate its a real edge case. I have had a small attempt at a very rough implementation below, please ignore any code faux pas - I will of course not actually write the code like below. I just wanted to at least try and show I have thought about this.
using System.Collections.Generic;
namespace Sample
{
public class PropertyCollection
{
public string Id { get; set; }
public Dictionary<string, object> Attributes { get; set; }
public PropertyCollection()
{
this.Id = "SampleID";
this.Attributes = new Dictionary<string, object>();
for (var i = 0; i < 10; i++)
{
this.Attributes.Add("KeyAttribute" + i, i);
}
for (var i = 10; i < 100; i++)
{
this.Attributes.Add("OtherAttribute" + i, i);
}
}
}
}
namespace Sample
{
public class ReturnedClass : PropertyCollection
{
public string SomeName { get; set; }
public ReturnedClass() : base() { }
}
}
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
namespace Sample
{
public class PropertyEntityAdapter : PSPropertyAdapter
{
public override Collection<PSAdaptedProperty> GetProperties(object baseObject)
{
PropertyCollection pc = baseObject as PropertyCollection;
if (pc == null)
{
throw new Exception("Some Exception");
}
Collection<PSAdaptedProperty> collection = new Collection<PSAdaptedProperty>();
foreach (string name in pc.Attributes.Keys)
{
collection.Add(new PSAdaptedProperty(name, null));
}
return collection;
}
public override PSAdaptedProperty GetProperty(object baseObject, string propertyName)
{
PropertyCollection pc = baseObject as PropertyCollection;
if (pc.Attributes.TryGetValue(propertyName, out object pcValue))
{
return new PSAdaptedProperty(propertyName, pcValue);
}
throw new Exception("Prop not found");
}
public override string GetPropertyTypeName(PSAdaptedProperty adaptedProperty)
{
if (adaptedProperty == null)
{
throw new Exception("prop null");
}
PropertyCollection pc = adaptedProperty.BaseObject as PropertyCollection;
if (pc == null)
{
throw new Exception("not found");
}
if (pc.Attributes.TryGetValue(adaptedProperty.Name, out object pcValue))
{
return pcValue.GetType().FullName;
}
return pc.GetType().FullName;
}
public override object GetPropertyValue(PSAdaptedProperty adaptedProperty)
{
if (adaptedProperty?.BaseObject == null || adaptedProperty?.Name == null)
{
throw new Exception("prop null");
}
PropertyCollection pc = adaptedProperty.BaseObject as PropertyCollection;
if (pc == null)
{
throw new Exception("not found");
}
if (pc.Attributes.TryGetValue(adaptedProperty.Name, out object pcValue))
{
return pcValue;
}
return null;
}
public override bool IsGettable(PSAdaptedProperty adaptedProperty)
{
if (adaptedProperty == null)
{
throw new Exception("prop null");
}
PropertyCollection pc = adaptedProperty.BaseObject as PropertyCollection;
if (pc == null)
{
throw new Exception("not found");
}
if (pc.Attributes.ContainsKey(adaptedProperty.Name))
{
return true;
}
return false;
}
public override bool IsSettable(PSAdaptedProperty adaptedProperty)
{
return false;
}
public override void SetPropertyValue(PSAdaptedProperty adaptedProperty, object value)
{
throw new System.NotImplementedException();
}
}
}
** I have purposely not added any code for the Set* methods to reduce the code required here. If the above seems right, then I am sure I know what to do.
Has anyone done this before? Are there any very basic examples out there? Am I on the right path, or am I misunderstanding? Thanks in advance.

If anyone comes comes looking for this and was interested in doing something similar, then my question is actually more or less how it is done. I hope it is of use or interest to others working on binary modules.
One thing I did find were there were some additional properties that were passed through the GetProperty method that seemed to be from the PowerShell engine itself i.e. PSComputerName, so there was no need to throw an error here, just return a new PSAdaptedProperty with a null object.
public override PSAdaptedProperty GetProperty(object baseObject, string propertyName)
{
PropertyCollection pc = baseObject as PropertyCollection;
if (pc.Attributes.TryGetValue(propertyName, out object pcValue))
{
return new PSAdaptedProperty(propertyName, pcValue);
}
return new PSAdaptedProperty(propertyName, null);
}
You can then either load the Types data via your module manifest or in the console using Update-Typedata -PrependPath path_to_file\file_name.
All the properties in the dictionary are returned to give the appearance that they are full properties of the class.

Related

CRM 2011 - C# PLUGIN

I have a CRM 2011 plugin using C#: Synchronus, Post-Operation and Not-Sandbox.
Whenever launched, the plugin works fine for the first time. But my problem is when I call the plugin the second time (with other GUID), the entity will have the first calling's values. So instead of having the new entity attributes that im just calling, im having the old entity's attributes (first call). Is there any solution to this problem?
Note that I have tried the Async mode and it worked fine, but I need to display error messages on the screen after calling a Webservice so I need it to be on Sync mode.
Plugin CLass:
namespace FNBChangeCustomerInfGroup
{
public abstract class Plugin : IPlugin
{
public IServiceProvider _serviceProvider;
public IOrganizationServiceFactory _organizationServiceFactory;
public IPluginExecutionContext _pluginExecutionContext;
public IOrganizationService _organizationService;
public ResourceManager _resourceManager;
public Lazy<Helper> _helper;
public IPluginExecutionContext PluginExecutionContext
{
get
{
if (_pluginExecutionContext == null)
{
_pluginExecutionContext = _serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
if (_pluginExecutionContext == null)
{
throw new InvalidOperationException("Cannot get Execution Context");
}
}
return _pluginExecutionContext;
}
}
public IOrganizationServiceFactory OrganizationServiceFactory
{
get
{
if (_organizationServiceFactory == null)
{
_organizationServiceFactory = _serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
if (_organizationServiceFactory == null)
{
throw new InvalidOperationException("Cannot get Service Factory");
}
}
return _organizationServiceFactory;
}
}
public IOrganizationService OrganizationService
{
get
{
if (_organizationService == null)
{
_organizationService = OrganizationServiceFactory.CreateOrganizationService(_pluginExecutionContext.UserId);
if (_organizationService == null)
{
throw new InvalidOperationException("Cannot get Organization Service");
}
}
return _organizationService;
}
}
protected virtual ResourceManager ResourceManager
{
get
{
if (_resourceManager == null)
{
_resourceManager = new ResourceManager(Constants.File_Resources, Assembly.GetExecutingAssembly());
if (_resourceManager == null)
{
throw new InvalidOperationException("Cannot access Resources file");
}
}
return _resourceManager;
}
}
protected Lazy<Helper> Helper
{
get
{
if (_helper == null || !_helper.IsValueCreated)
{
_helper = new Lazy<Helper>(() => new Helper(_organizationService));
}
return _helper;
}
}
public IExtensibleDataObject CheckType<ExtensibleDataObjectType>(string entityLogicalName) where ExtensibleDataObjectType : IExtensibleDataObject, new()
{
// Check if the input parameters property bag contains a target
// of the operation and that target is of type EntityReference.
if (PluginExecutionContext.InputParameters.Contains(Constants.InputParameter_Target) && PluginExecutionContext.InputParameters[Constants.InputParameter_Target] is ExtensibleDataObjectType)
{
// Obtain the target business entity from the input parameters.
IExtensibleDataObject entity = (PluginExecutionContext.InputParameters[Constants.InputParameter_Target]) as IExtensibleDataObject;
// Verify that the entity represents a risk carrier quote.
if (typeof(Entity).IsAssignableFrom(typeof(ExtensibleDataObjectType)))
{
if (!String.Equals(((Entity)entity).LogicalName, entityLogicalName)) { return null; }
}
else if (typeof(EntityReference).IsAssignableFrom(typeof(ExtensibleDataObjectType)))
{
if (!String.Equals(((EntityReference)entity).LogicalName, entityLogicalName)) { return null; }
}
return entity;
}
else
{
return null;
}
}
public virtual void Execute(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
}
}
This is what happens when a plugin class contains fields. Plugin classes are not allowed to be stateful, because they are instantiated once by the CRM platform and then reused. Therefore fields on these classes are not thread safe.
Check your CRMPluginBase class. Looking at method base.CheckType<Entity>() chances are this class has a private field holding an IPluginExecutionContext instance. Redesign your plugin base class and make shure it does not contain instance fields at all.

MSMQ binding mismatch

We have a setup that used to work and has stopped sometime over the past few months. We use a custom dead-letter queue that is specified in config but basically gets set like this::
MsmqIntegrationBinding msmq = new MsmqIntegrationBinding(
// Defaults to WindowsDomain. We want all messages to be authenticated.
MsmqIntegrationSecurityMode.Transport);
msmq.DeadLetterQueue = DeadLetterQueue.Custom;
msmq.CustomDeadLetterQueue = new Uri("net.msmq://localhost/private/BulkUpdatesDeadLetter");
We've started getting this error, which seems to be pretty clear:
System.InvalidOperationException: A mismatch occurred between the binding and the MSMQ configuration. Messages cannot be sent. The custom dead letter queue specified in the binding must be a transactional queue. Ensure that the custom dead letter queue address is correct and the queue is a transactional queue.
We've verified both that we are pointing to the correct queue in the config and that the queue is transactional. Are there any other issues that might cause this exception to be thrown, or are we just missing something in the obvious?
Update: Had our web ops team delete and recreate the queues and still receiving the error.
The following code is provided by Microsoft corporation in the article BindingsSection.cs source code in C# .NET
namespace System.ServiceModel.Configuration
{
using System.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.Security;
public sealed partial class BindingsSection : ConfigurationSection, IConfigurationContextProviderInternal
{
static Configuration configuration = null;
ConfigurationPropertyCollection properties;
public BindingsSection() { }
Dictionary<string, bindingcollectionelement=""> BindingCollectionElements
{
get
{
Dictionary<string, bindingcollectionelement=""> bindingCollectionElements = new Dictionary<string, bindingcollectionelement="">();
foreach (ConfigurationProperty property in this.Properties)
{
bindingCollectionElements.Add(property.Name, this[property.Name]);
}
return bindingCollectionElements;
}
}
new public BindingCollectionElement this[string binding]
{
get
{
return (BindingCollectionElement)base[binding];
}
}
protected override ConfigurationPropertyCollection Properties
{
get
{
if (this.properties == null)
{
this.properties = new ConfigurationPropertyCollection();
}
this.UpdateBindingSections();
return this.properties;
}
}
[ConfigurationProperty(ConfigurationStrings.BasicHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public BasicHttpBindingCollectionElement BasicHttpBinding
{
get { return (BasicHttpBindingCollectionElement)base[ConfigurationStrings.BasicHttpBindingCollectionElementName]; }
}
// This property should only be called/set from BindingsSectionGroup TryAdd
static Configuration Configuration
{
get { return BindingsSection.configuration; }
set { BindingsSection.configuration = value; }
}
[ConfigurationProperty(ConfigurationStrings.CustomBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public CustomBindingCollectionElement CustomBinding
{
get { return (CustomBindingCollectionElement)base[ConfigurationStrings.CustomBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.MsmqIntegrationBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public MsmqIntegrationBindingCollectionElement MsmqIntegrationBinding
{
get { return (MsmqIntegrationBindingCollectionElement)base[ConfigurationStrings.MsmqIntegrationBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetPeerTcpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetPeerTcpBindingCollectionElement NetPeerTcpBinding
{
get { return (NetPeerTcpBindingCollectionElement)base[ConfigurationStrings.NetPeerTcpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetMsmqBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetMsmqBindingCollectionElement NetMsmqBinding
{
get { return (NetMsmqBindingCollectionElement)base[ConfigurationStrings.NetMsmqBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetNamedPipeBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetNamedPipeBindingCollectionElement NetNamedPipeBinding
{
get { return (NetNamedPipeBindingCollectionElement)base[ConfigurationStrings.NetNamedPipeBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetTcpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetTcpBindingCollectionElement NetTcpBinding
{
get { return (NetTcpBindingCollectionElement)base[ConfigurationStrings.NetTcpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSFederationHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSFederationHttpBindingCollectionElement WSFederationHttpBinding
{
get { return (WSFederationHttpBindingCollectionElement)base[ConfigurationStrings.WSFederationHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WS2007FederationHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WS2007FederationHttpBindingCollectionElement WS2007FederationHttpBinding
{
get { return (WS2007FederationHttpBindingCollectionElement)base[ConfigurationStrings.WS2007FederationHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSHttpBindingCollectionElement WSHttpBinding
{
get { return (WSHttpBindingCollectionElement)base[ConfigurationStrings.WSHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WS2007HttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WS2007HttpBindingCollectionElement WS2007HttpBinding
{
get { return (WS2007HttpBindingCollectionElement)base[ConfigurationStrings.WS2007HttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSDualHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSDualHttpBindingCollectionElement WSDualHttpBinding
{
get { return (WSDualHttpBindingCollectionElement)base[ConfigurationStrings.WSDualHttpBindingCollectionElementName]; }
}
public static BindingsSection GetSection(Configuration config)
{
if (config == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("config");
}
return (BindingsSection)config.GetSection(ConfigurationStrings.BindingsSectionGroupPath);
}
public List<bindingcollectionelement> BindingCollections
{
get
{
List<bindingcollectionelement> bindingCollections = new List<bindingcollectionelement>();
foreach (ConfigurationProperty property in this.Properties)
{
bindingCollections.Add(this[property.Name]);
}
return bindingCollections;
}
}
internal static bool TryAdd(string name, Binding binding, Configuration config, out string bindingSectionName)
{
bool retval = false;
BindingsSection.Configuration = config;
try
{
retval = BindingsSection.TryAdd(name, binding, out bindingSectionName);
}
finally
{
BindingsSection.Configuration = null;
}
return retval;
}
internal static bool TryAdd(string name, Binding binding, out string bindingSectionName)
{
// TryAdd built on assumption that BindingsSectionGroup.Configuration is valid.
// This should be protected at the callers site. If assumption is invalid, then
// configuration system is in an indeterminate state. Need to stop in a manner that
// user code can not capture.
if (null == BindingsSection.Configuration)
{
DiagnosticUtility.DebugAssert("The TryAdd(string name, Binding binding, Configuration config, out string binding) variant of this function should always be called first. The Configuration object is not set.");
DiagnosticUtility.FailFast("The TryAdd(string name, Binding binding, Configuration config, out string binding) variant of this function should always be called first. The Configuration object is not set.");
}
bool retval = false;
string outBindingSectionName = null;
BindingsSection sectionGroup = BindingsSection.GetSection(BindingsSection.Configuration);
sectionGroup.UpdateBindingSections();
foreach (string sectionName in sectionGroup.BindingCollectionElements.Keys)
{
BindingCollectionElement bindingCollectionElement = sectionGroup.BindingCollectionElements[sectionName];
// Save the custom bindings as the last choice
if (!(bindingCollectionElement is CustomBindingCollectionElement))
{
MethodInfo tryAddMethod = bindingCollectionElement.GetType().GetMethod("TryAdd", BindingFlags.Instance | BindingFlags.NonPublic);
if (tryAddMethod != null)
{
retval = (bool)tryAddMethod.Invoke(bindingCollectionElement, new object[] { name, binding, BindingsSection.Configuration });
if (retval)
{
outBindingSectionName = sectionName;
break;
}
}
}
}
if (!retval)
{
// Much of the time, the custombinding should come out ok.
CustomBindingCollectionElement customBindingSection = CustomBindingCollectionElement.GetBindingCollectionElement();
retval = customBindingSection.TryAdd(name, binding, BindingsSection.Configuration);
if (retval)
{
outBindingSectionName = ConfigurationStrings.CustomBindingCollectionElementName;
}
}
// This little oddity exists to make sure that the out param is assigned to before the method
// exits.
bindingSectionName = outBindingSectionName;
return retval;
}
/// <securitynote>
/// Critical - uses SecurityCritical method UnsafeLookupCollection which elevates
/// Safe - does not leak config objects
/// </securitynote>
[SecurityCritical, SecurityTreatAsSafe]
void UpdateBindingSections()
{
ExtensionElementCollection bindingExtensions = ExtensionsSection.UnsafeLookupCollection(ConfigurationStrings.BindingExtensions, ConfigurationHelpers.GetEvaluationContext(this));
// Extension collections are additive only (BasicMap) and do not allow for <clear>
// or <remove> tags, nor do they allow for overriding an entry. This allows us
// to optimize this to only walk the binding extension collection if the counts
// mismatch.
if (bindingExtensions.Count != this.properties.Count)
{
foreach (ExtensionElement bindingExtension in bindingExtensions)
{
if (null != bindingExtension)
{
if (!this.properties.Contains(bindingExtension.Name))
{
ConfigurationProperty property = new ConfigurationProperty(bindingExtension.Name,
Type.GetType(bindingExtension.Type, true),
null,
ConfigurationPropertyOptions.None);
this.properties.Add(property);
}
}
}
}
}
/// <securitynote>
/// Critical - uses SecurityCritical method UnsafeGetAssociatedBindingCollectionElement which elevates
/// Safe - does not leak config objects
/// </securitynote>
[SecurityCritical, SecurityTreatAsSafe]
internal static void ValidateBindingReference(string binding, string bindingConfiguration, ContextInformation evaluationContext, ConfigurationElement configurationElement)
{
// ValidateBindingReference built on assumption that evaluationContext is valid.
// This should be protected at the callers site. If assumption is invalid, then
// configuration system is in an indeterminate state. Need to stop in a manner that
// user code can not capture.
if (null == evaluationContext)
{
DiagnosticUtility.DebugAssert("ValidateBindingReference() should only called with valid ContextInformation");
DiagnosticUtility.FailFast("ValidateBindingReference() should only called with valid ContextInformation");
}
if (!String.IsNullOrEmpty(binding))
{
BindingCollectionElement bindingCollectionElement = null;
if (null != evaluationContext)
{
bindingCollectionElement = ConfigurationHelpers.UnsafeGetAssociatedBindingCollectionElement(evaluationContext, binding);
}
else
{
bindingCollectionElement = ConfigurationHelpers.UnsafeGetBindingCollectionElement(binding);
}
if (bindingCollectionElement == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigInvalidSection,
ConfigurationHelpers.GetBindingsSectionPath(binding)),
configurationElement.ElementInformation.Source,
configurationElement.ElementInformation.LineNumber));
}
if (!String.IsNullOrEmpty(bindingConfiguration))
{
if (!bindingCollectionElement.ContainsKey(bindingConfiguration))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigInvalidBindingName,
bindingConfiguration,
ConfigurationHelpers.GetBindingsSectionPath(binding),
ConfigurationStrings.BindingConfiguration),
configurationElement.ElementInformation.Source,
configurationElement.ElementInformation.LineNumber));
}
}
}
}
ContextInformation IConfigurationContextProviderInternal.GetEvaluationContext()
{
return this.EvaluationContext;
}
/// <securitynote>
/// RequiresReview - the return value will be used for a security decision -- see comment in interface definition
/// </securitynote>
ContextInformation IConfigurationContextProviderInternal.GetOriginalEvaluationContext()
{
DiagnosticUtility.DebugAssert("Not implemented: IConfigurationContextProviderInternal.GetOriginalEvaluationContext");
return null;
}
}
}
</remove></clear></bindingcollectionelement></bindingcollectionelement></bindingcollectionelement></string,></string,></string,>

ValidateProperties code

Hi all i am using mvvmcross and portable class libraries , so i cannot use prism or componentmodel data annotations, to validate my classes. basically i have a modelbase that all my models inherit from.
My validate code below is horribly broken, basically im looking for the code that data annotations uses to iterate thru all the properties on my class that is inheriting the base class ,
i have written various attributes that are there own validators inheriting from "validatorBase" which inherits from attribute. i just cannot for the life of me figure out thecode that says ... ok im a class im going to go through all the properties in me that have an attribute of type ValidatorBase and run the validator. my code for these are at the bottom
public class ModelBase
{
private Dictionary<string, IEnumerable<string>> _errors;
public Dictionary<string, IEnumerable<string>> Errors
{
get
{
return _errors;
}
}
protected virtual bool Validate()
{
var propertiesWithChangedErrors = new List<string>();
// Get all the properties decorated with the ValidationAttribute attribute.
var propertiesToValidate = this.GetType().GetRuntimeProperties()
.Where(c => c.GetCustomAttributes(typeof(ValidatorBase)).Any());
foreach (PropertyInfo propertyInfo in propertiesToValidate)
{
var propertyErrors = new List<string>();
TryValidateProperty(propertyInfo, propertyErrors);
// If the errors have changed, save the property name to notify the update at the end of this method.
bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors);
if (errorsChanged && !propertiesWithChangedErrors.Contains(propertyInfo.Name))
{
propertiesWithChangedErrors.Add(propertyInfo.Name);
}
}
// Notify each property whose set of errors has changed since the last validation.
foreach (string propertyName in propertiesWithChangedErrors)
{
OnErrorsChanged(propertyName);
OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName));
}
return _errors.Values.Count == 0;
}
}
here is my validator
public class BooleanRequired : ValidatorBase
{
public override bool Validate(object value)
{
bool retVal = true;
retVal = value != null && (bool)value == true;
var t = this.ErrorMessage;
if (!retVal)
{
ErrorMessage = "Accept is Required";
}
return retVal;
}
}
and here is an example of its usage
[Required(ErrorMessage = "Please enter the Amount")]
public decimal Amount
{
get { return _amount; }
set { _amount = value; }//SetProperty(ref _amount, value); }
}

Copy two identical object with different namespaces (recursive reflection)

I'm working in c# with several workspaces that have one specific class which his always the same in each workspace.
I would like to be able have a copy of this class to be able to work with it without dealing with namespaces differences.
example :
namespace1 {
class class1{
public class2;
}
class class2{
public string;
}
}
namespace2 {
class class1{
public class2;
}
class class2{
public string;
}
}
In my copied Class I've got a function to copy all data's to one of the namespace's class.
It's working if i only have c# standard types. I got exeption ( "Object does not match target type." ) as soon as I'm dealing with class2 object (which is also from different namespaces)
public Object toNamespaceClass(Object namespaceClass)
{
try
{
Type fromType = this.GetType();
Type toType = namespaceClass.GetType();
PropertyInfo[] fromProps = fromType.GetProperties();
PropertyInfo[] toProps = toType.GetProperties();
for (int i = 0; i < fromProps.Length; i++)
{
PropertyInfo fromProp = fromProps[i];
PropertyInfo toProp = toType.GetProperty(fromProp.Name);
if (toProp != null)
{
toProp.SetValue(this, fromProp.GetValue(namespaceClass, null), null);
}
}
}
catch (Exception ex)
{
}
return namespaceClass;
}
Anyone do have any idea of how to deal with this kind of "recursivity reflection".
I hope eveything is understandable.
Thanks, Bye!
Edit :
I think i got it solved (at least in my mind), I'll try the solution back at work tomorrow. Taking my function out of my class and using it recursively if a property is not a standard type is maybe the solution.
BinaryFormatter does not work in .Net 4.5 as it remembers from what type of class the instance was created. But with JSON format, it does not. JSON serializer is implemented by Microsoft in DataContractJosnSerializer.
This works:
public static T2 DeepClone<T1, T2>(T1 obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T1));
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T2));
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
ms.Position = 0;
return (T2)deserializer.ReadObject(ms);
}
}
and uses as follows:
var b = DeepClone<A, B>(a);
I had the similar problem. I got to use similar classes but different in terms of namespace only. As a quick solution I performed below steps and it works.
Serialize source class into XML.
In SerializedXML replace source namespace with the target one.
DeSerialize with target type.
I know there is performance overhead with above way but it is quick to implement and error free.
I got it solved , just to let you know how I did it :
This solution is sot perfect because it handle only 1 dimensions array not more.
public static Object CopyObject(Object from , Object to)
{
try
{
Type fromType = from.GetType();
Type toType = to.GetType();
PropertyInfo[] fromProps = fromType.GetProperties();
PropertyInfo[] toProps = toType.GetProperties();
for (int i = 0; i < fromProps.Length; i++)
{
PropertyInfo fromProp = fromProps[i];
PropertyInfo toProp = toType.GetProperty(fromProp.Name);
if (toProp != null)
{
if (toProp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
{
if (!toProp.PropertyType.IsArray)
{
ConstructorInfo ci = toProp.PropertyType.GetConstructor(new Type[0]);
if (ci != null)
{
toProp.SetValue(to, ci.Invoke(null), null);
toProp.SetValue(to, gestionRefelexion.CopyObject(fromProp.GetValue(from, null), toProp.GetValue(to, null)), null);
}
}
else
{
Type typeToArray = toProp.PropertyType.GetElementType();
Array fromArray = fromProp.GetValue(from, null) as Array;
toProp.SetValue(to, copyArray(fromArray, typeToArray), null);
}
}
else
{
toProp.SetValue(to, fromProp.GetValue(from, null), null);
}
}
}
}
catch (Exception ex)
{
}
return to;
}
public static Array copyArray(Array from, Type toType)
{
Array toArray =null;
if (from != null)
{
toArray= Array.CreateInstance(toType, from.Length);
for (int i = 0; i < from.Length; i++)
{
ConstructorInfo ci = toType.GetConstructor(new Type[0]);
if (ci != null)
{
toArray.SetValue(ci.Invoke(null), i);
toArray.SetValue(gestionRefelexion.CopyObject(from.GetValue(i), toArray.GetValue(i)), i);
}
}
}
return toArray;
}
Hope this can help some people.
Thanks for helping everyone.
Cheers
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T) formatter.Deserialize(ms);
}
}
from here
Two identical or similar objects from different namespaces ?
You have this:
namespace Cars
{
public class car {
public string Name;
public void Start() { ... }
}
}
namespace Planes
{
public class plane {
public string Name;
public void Fly() { ... }
}
}
Time to apply some class inheritance:
namespace Vehicles
{
public class vehicle
{
public string Name;
} // class
} // namespace
using Vehicles;
namespace Cars
{
public class car: vehicle
{
public string Name;
public void Start() { ... }
} // class
} // namespace
using Vehicles;
namespace Planes
{
public class plane: vehicle
{
public void Fly() { ... }
}
}
And to copy, there is a copy method or constructor, but, I prefer a custom one:
namespace Vehicles
{
public class vehicle {
public string Name;
public virtual CopyFrom (vehicle Source)
{
this.Name = Source.Name;
// other fields
}
} // class
} // namespace
Cheers.
You either need to refactor all of your duplicate classes into a single shared class or implement a common interface that all of your various classes implement. If you really can't modify the underlying types, create a subclass for each that implements your common interface.
Yes, you can do it with reflection... but you really shouldn't because you end up with brittle, error prone, code.
This problem can be elegantly solves using Protocol Buffers because Protocol Buffers do not hold any metadata about the type they serialize. Two classes with identical fields & properties serialize to the exact same bits.
Here's a little function that will change from O the original type to C the copy type
static public C DeepCopyChangingNamespace<O,C>(O original)
{
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize(ms, original);
ms.Position = 0;
C c = Serializer.Deserialize<C>(ms);
return c;
}
}
usage would be
namespace1.class1 orig = new namespace1.class1();
namespace2.class1 copy =
DeepCopyChangingNamespace<namespace1.class1, namespace2.class1>(orig);

.NET 4 RTM MetadataType attribute ignored when using Validator

I am using VS 2010 RTM and trying to perform some basic validation on a simple type using MetadataTypeAttribute. When I put the validation attribute on the main class, everything works. However, when I put it on the metadata class, it seems to be ignored. I must be missing something trivial, but I've been stuck on this for a while now.
I had a look at the Enterprise Library validation block as a workaround, but it doesn't support validation of single properties out of the box. Any ideas?
class Program
{
static void Main(string[] args)
{
Stuff t = new Stuff();
try
{
Validator.ValidateProperty(t.X, new ValidationContext(t, null, null) { MemberName = "X" });
Console.WriteLine("Failed!");
}
catch (ValidationException)
{
Console.WriteLine("Succeeded!");
}
}
}
[MetadataType(typeof(StuffMetadata))]
public class Stuff
{
//[Required] //works here
public string X { get; set; }
}
public class StuffMetadata
{
[Required] //no effect here
public string X { get; set; }
}
It seems that the Validator doesn't respect MetadataTypeAttribute:
http://forums.silverlight.net/forums/p/149264/377212.aspx
The relationship must be explicity registered:
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(Stuff),
typeof(StuffMetadata)),
typeof(Stuff));
This helper class will register all the metadata relationships in an assembly:
public static class MetadataTypesRegister
{
static bool installed = false;
static object installedLock = new object();
public static void InstallForThisAssembly()
{
if (installed)
{
return;
}
lock (installedLock)
{
if (installed)
{
return;
}
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
foreach (MetadataTypeAttribute attrib in type.GetCustomAttributes(typeof(MetadataTypeAttribute), true))
{
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(type, attrib.MetadataClassType), type);
}
}
installed = true;
}
}
}
Supplying an instance of the metadata class instead of the main class to the ValidationContext constructor seems to work for me.

Categories

Resources