How to add attributes to aspect/compiler-generated property? - postsharp

I'm adding a property to a class and have my Aspect set up like this:
[PSerializable]
public class MyAspect : InstanceLevelAspect
{
[MaxLength(100)]
[IntroduceMember]
public string MyProp { get; set; }
}
My target class is then decorated with the [MyAspect] attribute.
When Postsharp adds the MyProp property to the target class, it does not include the MaxLength attribute on the generated property. The property in the Aspect class includes it, but the property added to my target class does not.
I need the attributes specified for the property in the aspect definition to be added to the property added to my target class. How can I add the attributes to the generated property?

To intruct PostSharp to copy the attribute, you need to use [CopyCustomAttributes] (doc), so your aspect would look like this:
[PSerializable]
public class MyAspect : InstanceLevelAspect
{
[MaxLength(100)]
[IntroduceMember]
[CopyCustomAttributes(typeof(MaxLengthAttribute))]
public string MyProp { get; set; }
}

Related

MVVM "A 'Binding' can only be set on a DependencyProperty of a DependencyObject."

I have this Model
[NotifyPropertyChanged]
public class WidgetConfiguration
{
#region Properties
#endregion Properties
}
Which i use in my ViewModel for a Collection and a Selected item property (ListView / GridView SelectedItem="{Binding SelectedWidget}" ... )
[NotifyPropertyChanged]
public class WidgetViewModel
{
public ObservableCollection<WidgetConfiguration> Configurations { get; set; } = new ObservableCollection<WidgetConfiguration>();
public WidgetConfiguration SelectedWidget { get; set; }
}
I then want to Bind SelectedWidget to a UserControl that function as editor for the SelectedItem:
<controls:WidgetConfig Widget="{Binding SelectedWidget}" />
The UserControl is defined like this (using PostSharp to declare DependencyProperties)
[NotifyPropertyChanged]
public partial class WidgetConfig : UserControl
{
[DependencyProperty]
public WidgetConfiguration Widget { get; set; }
public WidgetConfig()
{
InitializeComponent();
this.DataContext = this;
}
}
But im getting an error on the UserControl binding:
Severity Code Description Project File Line Suppression State Error A
'Binding' cannot be set on the 'Widget' property of type
'Squiddy_Client_Views_WidgetConfig_10_577403948'. A 'Binding' can only
be set on a DependencyProperty of a
DependencyObject. Client C:\develop\Squiddy\Client\Views\WidgetManager.xaml 21
I've tried implementing the DependencyProperties manually and ensured that all types was correct, even the default type and default value. it didn't help.
I've read all results on google and don't know what to do.
Is this even possible or do i need to make a proxy binding ?
EDIT:
Just for the sake of it, i tried implementing the DependencyProperty manually:
public static readonly DependencyProperty WidgetProperty =
DependencyProperty.Register("Widget", typeof(WidgetConfiguration),
typeof(WidgetConfig));
[SafeForDependencyAnalysis]
public WidgetConfiguration Widget
{
get { return GetValue(WidgetProperty) as WidgetConfiguration; }
set { SetValue(WidgetProperty, value); }
}
Now the XAML error is gone, but the binding is "dead". When selecting a new object in the ListView, the UserControl doesn't get updated:
The PropertySetter doesn't get invoked
PropertyChanged events on the ViewModel DO happen though...
EDIT 2:
I totally missed this part in the PostSharp documentation, i lacked adding the DependencyProperty along with the attribute. (thanks to Daniel Balas)
public static DependencyProperty WidgetProperty { get; private set; }
[DependencyProperty]
public WidgetConfiguration Widget { get; set; }
EDIT 3:
I finally found the answer to DataContext / root after watching this video:
https://www.youtube.com/watch?v=h7ZrdGiOm3E
I removed "this.DataContext = this" from the UserControl constructor
I added a Name="root" in the UserControl element in XAML
The bindings inside the UserControl should point to ElementName=root and use the property Widget.xxx
like this:
<UserControl Name="root">
<TextBlock Text="{Binding Header, ElementName=root}"></TextBlock>
<Label Content="{Binding Widget.Name, ElementName=root}" />
</UserControl>
The Xaml/Baml compiler determines whether the property Foo is a dependency property by looking for a FooProperty static field or property with DependencyProperty type. This field is not automatically injected by the [DependencyProperty] aspect (due to limitations of PostSharp's aspect framework).
However, when you declare this field or property it would be enough for the Xaml compiler to recognize the property as a dependency property. ( The aspect will then set the field/property at runtime, so it has a correct value at runtime and is usable. )
public static DependencyProperty WidgetProperty { get; private set; }
[DependencyProperty]
public WidgetConfiguration Widget { get; set; }
Your property setter is not invoked, because WPF bindings change the value store on the DependencyObject itself instead of accessing the property setter.
The problem seems to be in the fact that you are changing the DataContext of your control in the constructor. This is going to break Bindings set on the DataContext in the parent control (bindings use DataContext of controls they assigned to). One way to reference properties of your control is like this:
<Label x:Name="label" Content="{Binding ElementName=root, Path=Widget.Name}" HorizontalAlignment="Left" />
Where "root" is a x:Name="root" given to your control (the root UserControl element).

How to use AOP (PostSharp) for Serialization?

Is it possible to inject code to read/write the properties of an object using a PostSharp aspect? For example, consider the following class:
[ BinarySerializable ]
public class Employee {
public string Name {get; set; }
public string Title {get; set;}
}
In this case, "BinarySerializable" would be a custom aspect that introduces a custom "IBinarySerializable" interface, as follows:
public interface IBinarySerializable
{
void Write(BinaryWriter writer);
void Read(BinaryReader reader);
}
After compilation, the resulting class would look like this:
public class Employee : IBinarySerializable
{
public string Name {get; set;}
public string Title {get; set; }
void IBinarySerializable.Write(BinaryWriter writer)
{
writer.Write(Name);
writer.Write(Title);
}
void IBinarySerializable.Read(BinaryReader reader)
{
Name = reader.ReadString();
Title = reader.ReadString();
}
}
Intuitively, I feel this should be possible using PostSharp, but I need some direction as to the right approach. If this is possible, then how to handle properties that are themselves injected by some other aspect?
UPDATE: I tried creating a simple example using the built-in PSerializable aspect, but ran into problems when members inherit from .NET framework classes that do not have that attribute.
Adding the [PSerializable] attribute to the EmployeeCollection class fails to compile with "Cannot apply [PSerializable] to type 'AOPSerialization.EmployeeCollection' because the base type does not have a [PSerializable] or [Serializer] attribute."
Omitting the [PSerializable] attribute from the EmployeeCollection class throws a runtime PortableSerializationException: Cannot find a serializer for type 'AOPSerialization.EmployeeCollection'.
For example:
[PSerializable]
public class AOPComponent
{
public string Title { get; set; }
public string Description { get; set; }
public AOPComponent(string title, string description){...}
}
[PSerializable]
public class AOPComponentCollection<T> : ObservableCollection<T>
{...}
[PSerializable]
public class EmployeeCollection : AOPComponentCollection<Employee>
{...}
[PSerializable]
public class Company : AOPComponent
{
public EmployeeCollection Engineers { get; set; }
public EmployeeCollection Managers { get; set; }
}
I figured out that the Serializer and ImportSerializer attributes are used to tell the PortableFormatter which custom ISerializer or ISerializerFactory implementation to use.
But the question remains:
How to specify a custom serializer for the generic base collection type?
This approach fails because an attribute may not include type parameters.
[PSerializable, ImportSerializer(typeof(ObservableCollection<T>), typeof(AOPComponentSerializerFactory))]
public class AOPComponentCollection<T> : ObservableCollection<T> where T : AOPComponent
{...}
This approach fails because PostSharp cannot find a serializer for ObservableCollection< T >
[PSerializable, Serializer(typeof(AOPComponentSerializerFactory))]
public class AOPComponentCollection<T> : ObservableCollection<T> where T : AOPComponent
{...}
It would be possible to do that with PostSharp, but only by using the low-level PostSharp SDK, which is undocumented and unsupported.
Good news are that we already implemented this for you, in the namespace PostSharp.Serialization. The aspect is [PSerializable] and the formatter is PortableFormatter.

Code first Type per hierarchy

I use EF 5 with the code first approach. Now I try to define a "code" table in which I want to have several different codes (like address code, medium code, etc.). In this table I just have the following properties: ID (Guid), Name (String), Description (String) and a discriminator (in this case something like the type of the code: address code, medium code, etc.).
So I defined the following base class:
public abstract class Code : EntityBase
{
public string Name { get; set; }
public string Beschreibung { get; set; }
}
Then I derived two classes from code
public class AddressCode : Code {}
public class MediumCode : Code {}
The class EntityBase is abstract and just defines the Id property, we use it for every POCO class...
The goal is that I can use AddressCode as a property on my address POCO class:
public class Adresse : EntityBase
{
#region Properties
public string Name1 { get; set; }
public virtual AddressCode AddressCode { get; set; }
#endregion
}
The question now is, how can I explain EF how to do that? Anyone can help?
Thanks
Marco
Thanks for your answer!
I tried to do it like you said. Unfortunately I get an error because of my EntityBase class:
public abstract class EntityBase
{
#region Properties
public virtual Guid Id { get; set; }
public virtual bool IsValid
{
get
{
{
return Validate();
}
}
}
[NotMappedAttribute]
public virtual IList<ValidationFailure> ValidationFailures { get; set; }
#endregion
#region Methods
private bool Validate()
{
var validatorFactory = new AttributedValidatorFactory();
IValidator validator = validatorFactory.GetValidator(GetType());
if (validator == null)
{
return true;
}
ValidationResult validationResult = validator.Validate(this);
ValidationFailures = validationResult.Errors;
return validationResult.IsValid;
}
#endregion
}
The error message is:
You cannot use Ignore method on the property 'ValidationFailures' on type 'Entities.AdresseCode' because this type inherits from the type 'Entities.EntityBase' where this property is mapped. To exclude this property from your model, use NotMappedAttribute or Ignore method on the base type.
As you can see I already defined the property ValidationFailures as NotMapped but still I get this error.. Do you have an idea?
Thanks
Marco
Just create a context (derived from DbContext)
public class AddressesDb : DbContext
{
public DbSet<Code> Codes { get; set; }
public DbSet<Adresse> Adressen { get; set; }
}
And (when used in code) EF will create a database with default table and column names. It will create a discriminator column of type text (nvarchar) which will contain the names of the classes that derive from Code.
If you want different names and/or types you should either use data annotations or fluent API to configure these.
Finally I got it work!
In the DBContext be aware to define DbSets for the code derived classes before all the other POCO's and then it works!

How Do I Add Display Name To An Inherited Property With EF Code First?

I have a class Movement inheriting from TimeBlock. TimeBlock is provided for me and I can't change it. TimeBlock provides a DurationDescription property and I want to display it. However I always use LabelFor etc which means I need to have Display metadata on DurationDescription so I can have "Duration Desc." etc.
How do I add metadata to an inherited class with EF Code First. Am I supposed to use buddy metadata?
Yes, you need to use the MetadataTypeattribute. It will work fine also with inheritance like with partial classes:
public class Base
{
public string Prop1 { get; set; }
}
[MetadataType(typeof(ClassMetadata))]
public class Class : Base
{
[DisplayName("My prop 2")]
public string Prop2 { get; set; }
class ClassMetadata
{
[DisplayName("My prop 1")]
public string Prop1 { get; set; }
}
}
On the UI the properties will be displayed as "My prop 1" and "My prop 2".

DataAnnotations MetadataType Class Ignores Base Class Properties

I've run into a bit of a wall in trying to use the .NET DataAnnotations feature to provide simple validations in a derived class. I am marking up my class with the standard annotations included in .NET 4 (from the System.ComponentModel.DataAnnotations namespace), then using the MS Enterprise Library v5 Validation Block to process the rules.
I have a number of objects derived from a common base class, which contains properties common to all of my objects. For validation purposes, I may have different rules for the various classes derived from this class.
Here's a simplified example:
public abstract class PersonBase
{
public int Id { get; set; }
public string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
public class Customer : PersonBase
{
}
[MetadataType(typeof(ManagerMD))]
public class Manager : PersonBase
{
}
public class CustomerMD
{
[Required]
[StringLength(20, ErrorMessage="Customer names may not be longer than 20 characters.")]
public object Name { get; set; }
}
public class ManagerMD
{
[Required]
[StringLength(30, ErrorMessage = "Manager names may not be longer than 30 characters.")]
public object Name { get; set; }
}
// calling code
var invalidCustomer = new Customer {Id=1, Name=string.Empty};
var valFactory = EnterpriseLibraryContainer.Current.GetInstance<ValidatorFactory>();
var customerValidator = valFactory.CreateValidator<Customer>();
var validationResults = customerValidator.Validate(invalidCustomer);
// validationResults.IsValid should equal False, but actually equals True.
I have found that I can get the expected validation results if I push the annotations down to the base class, but then I lose the ability to fulfill different requirements for different types. Also, if I put class-specific properties on a derived class and provide metadata for these properties, I get results, but only for these properties, not the properties from the base class.
I haven't yet tried using the EntLib provided validation attributes; I'd prefer to keep the library this lives in free of dependencies from outside the core framework, if at all possible.
Am I missing something, or am I just out of luck here?
I think I have a workable solution for this.
It appears that the Metadata class will not provide validation of properties belonging to the superclass of the target object. In order to get Metadata to work with this, I needed to mark the superclass properties as virtual, then provide overrides for the properties that I wanted to validate.
Example (see question above for original example):
public abstract class PersonBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
partial class Customer : PersonBase
{
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
}
With the override in place, the validator works as expected. It's a little more work, but it will get the job done.
I also tried adding annotations to the base class as fallback default rules; this allows me to have a base set of rules and override them as needed on a case by case basis. Looking good.
I run into the same issue and couldn't make it annotate a base class with Attributes using MethadataType. Like Scroll Lock I did the overriding part for base class virtual properties. On top of it I made "shadowing" for the none virtual properties.
public class BaseClass
{
public virtual int Id {get;set;}
public string Name {get;set;}
}
public class DerivedClass
{
[SomeAttribute]
public ovveride int Id {get{ return base.Id;} set{ base.Id = value;}}
[SomeAttribute]
public new string Name {get{ return base.Name;} set{ base.Name = value;}}
}