I would like to add attributes to Linq 2 Sql classes properties. Such as this Column is browsable in the UI or ReadOnly in the UI and so far.
I've thought about using templates, anybody knows how to use it? or something different?
Generally speaking, would do you do to address this issue with classes being code-generated?
You can take advantage of the new Metadata functionality in the System.ComponentModel.DataAnnotations which will allow us to separate the MetaData from the existing domain model.
For example:
[MetadataType (typeof (BookingMetadata))]
public partial class Booking
{
// This is your custom partial class
}
public class BookingMetadata
{
[Required] [StringLength(15)]
public object ClientName { get; set; }
[Range(1, 20)]
public object NumberOfGuests { get; set; }
[Required] [DataType(DataType.Date)]
public object ArrivalDate { get; set; }
}
As requested, here's an approach using a CustomTypeDescriptor to edit the attributes at run-time; the example here is win-forms, but it should be pretty simple to swap it into WPF to see if it works...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
// example POCO
class Foo {
static Foo()
{ // initializes the custom provider (the attribute-based approach doesn't allow
// access to the original provider)
TypeDescriptionProvider basic = TypeDescriptor.GetProvider(typeof(Foo));
FooTypeDescriptionProvider custom = new FooTypeDescriptionProvider(basic);
TypeDescriptor.AddProvider(custom, typeof(Foo));
}
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
// example form
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run( new Form {
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = new BindingList<Foo> {
new Foo { Name = "Fred", DateOfBirth = DateTime.Today.AddYears(-20) }
}
}
}
});
}
}
class FooTypeDescriptionProvider : TypeDescriptionProvider
{
ICustomTypeDescriptor descriptor;
public FooTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{ // swap regular descriptor for bespoke (Foo) descriptor
if (descriptor == null)
{
ICustomTypeDescriptor desc = base.GetTypeDescriptor(typeof(Foo), null);
descriptor = new FooTypeDescriptor(desc);
}
return descriptor;
}
}
class FooTypeDescriptor : CustomTypeDescriptor
{
internal FooTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }
public override PropertyDescriptorCollection GetProperties()
{ // wrap the properties
return Wrap(base.GetProperties());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{ // wrap the properties
return Wrap(base.GetProperties(attributes));
}
static PropertyDescriptorCollection Wrap(PropertyDescriptorCollection properties)
{
// here's where we have an opportunity to swap/add/remove properties
// at runtime; we'll swap them for pass-thru properties with
// edited atttibutes
List<PropertyDescriptor> list = new List<PropertyDescriptor>(properties.Count);
foreach (PropertyDescriptor prop in properties)
{
// add custom attributes here...
string displayName = prop.DisplayName;
if (string.IsNullOrEmpty(displayName)) displayName = prop.Name;
list.Add(new ChainedPropertyDescriptor(prop, new DisplayNameAttribute("Foo:" + displayName)));
}
return new PropertyDescriptorCollection(list.ToArray(), true);
}
}
class ChainedPropertyDescriptor : PropertyDescriptor
{
// this passes all requests through to the underlying (parent)
// descriptor, but has custom attributes etc;
// we could also override properties here...
private readonly PropertyDescriptor parent;
public ChainedPropertyDescriptor(PropertyDescriptor parent, params Attribute[] attributes)
: base(parent, attributes)
{
this.parent = parent;
}
public override bool ShouldSerializeValue(object component) { return parent.ShouldSerializeValue(component); }
public override void SetValue(object component, object value) { parent.SetValue(component, value); }
public override object GetValue(object component) { return parent.GetValue(component); }
public override void ResetValue(object component) { parent.ResetValue(component); }
public override Type PropertyType {get { return parent.PropertyType; } }
public override bool IsReadOnly { get { return parent.IsReadOnly; } }
public override bool CanResetValue(object component) {return parent.CanResetValue(component);}
public override Type ComponentType { get { return parent.ComponentType; } }
public override void AddValueChanged(object component, EventHandler handler) {parent.AddValueChanged(component, handler); }
public override void RemoveValueChanged(object component, EventHandler handler) { parent.RemoveValueChanged(component, handler); }
public override bool SupportsChangeEvents { get { return parent.SupportsChangeEvents; } }
}
You may want to consider using Damien Guard's T4 templates for Linq To Sql. Modifying his templates would most likely give you the results you seek.
Hope this helps!
This is a common problem with code-generation; while you can add members and class level attributes via an additional partial class, you can't add attributes to the generated members. To compensate, some attribute-based mechanisms allow you to specify the attributes at the class (naming the member), but not any of the ones you cite.
One hardcore option would be to write a TypeDescriptionProvider that supplies custom PropertyDescriptor definitions for the properties. This would allow you to fully control the metadata used by UI binding tools like PropertyGrid, DataGridView, etc.
However, this is possibly too much work simply to set a few UI propertiex if you can also set them by hand! But if you are interested in pursuing that option, let me know - it is a familiar area to me, but too much code to write an example if you don't want it.
Note: if you are using PropertyGrid, then you can't set the properties by hand, but you can write a TypeConverter, which is a bit less work than a full TypeDescriptionProvider; just inherit from ExpandableObjectConverter and override GetProperties(). You'll still need a shim PropertyDescriptor, so still not trivial...
You can use a partial class to make your entity implement a interface that declares the same properties of your entity and then put the attributes on the interface.
This way you can use the interface type to get the attributes from the properties.
I don't know if you will be able to use the attributes this way, but you can try something like that.
Example:
public interface IConcept {
long Code { get; set; }
[Unique]
string Name { get; set; }
bool IsDefault { get; set; }
}
public partial class Concept : IConcept { }
[Table(Name="dbo.Concepts")]
public partial class Concept
{
//...
}
You can also write/use another code generator in place of the default MSLinqToSQLGenerator.
One option to start from is this one.
I built my own, according to my needs. I don't know if there is a way to indicate which attributes must be placed in each property using the DBML Designer, or xml file, but maybe you can find a way to use this alternative to help you with your problem.
Related
I am trying to serialize following class hierarchy:
public class Control
{
public virtual string Name {get; set;}
public virtual string Type {get; set;}
public virtual string Text { get; set; }
public virtual SerializableFont Font { get; set; }
private Operation op = new Operation();
public Operation Events { get { return op; } }
}
[System.Xml.Serialization.XmlInclude(typeof(Control))]
public class TextProperties : Control
{
public Label txt;
public TextProperties()
{
}
public override string Type
{
get { return "Text"; }
}
public override string Text
{
get { return txt.Text; }
set
{
txt.Text = value;
}
}
public override SerializableFont Font
{
get { return new SerializableFont(txt.Font); }
set
{
txt.Font = new SerializableFont().Font ;
}
}
}
As you can see I am including the base class but still its throwing following exception:
The type TextProperties was not expected. Use the
XmlInclude or SoapInclude attribute to specify types that are not
known statically.
I´m not sure that the problem is really your class hierarchy itself, it seems to be okay. You dont need to add the attribute.
Instead, you are likely trying to read/write an object of type TextProperties with an XmlSerializer created for objects of type Control. That won´t work, you need to use the serializer created specific to the type to serialize / deserialize.
If this is not known at compile time, try to create a wrapping class that implements IXMLSerializable. When reading, this class must first read data to define the class of the wrapped object (a node attribute named "TypeName", for example). It must then create the specific serializer for the given type at runtime and use it to load the wrapped object.
EDIT: Here is some sample code how it could be done. You will need to implement GetTypeByName yourself (eg. crawling through all loaded assemblies or just "hardwiring" some strings to a specific type you expect). The code should suffice to be integrated into a class with a property "WrappedObject".
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var typeName = reader.GetAttribute("TypeName");
reader.ReadStartElement();
var serializer = new XmlSerializer(GetTypeByName(typeName));
WrappedObject = serializer.Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("TypeName", "", GetTypeByName(WrappedObject));
var serializer = new XmlSerializer(typeof(WrappedObject));
serializer.Serialize(writer, WrappedObject);
}
I think you need to put attribute above the parent class like this
[XmlInclude(typeof(ChildClass))]
[Serializable]
public abstract class ParentClass
{
public abstract string Name { get; set; }
}
in child class you put
[Serializable]
public class ChildClass: ParentClass
{
}
Out of curiosity, imagine that you have such a UITypeEditor:
public class CustomEditor : System.Drawing.Design.UITypeEditor
{
public bool DoSomething { get; set; }
[...]
}
And you want to use it to edit one of your properties with DoSomething set to true:
public MyClass
{
[EditorAttribute(typeof(CustomEditor), typeof(System.Drawing.Design.UITypeEditor))]
public string MyProperty { get; set; }
[...]
}
How do you specify a value for the DoSomething property of CustomEditor to be set when instanciating the editor?
Is this possible at all or do you have to create as many classes inheriting CustomEditor as the number of possible configurations?
In your implementation of UITypeEditor.EditValue you can look at the context argument to get a reference to the descriptor of the property that is being edited. You could then look at another attribute in which you put the editor configuration values.
public class CustomEditor : System.Drawing.Design.UITypeEditor
{
public override object EditValue(
ITypeDescriptorContext context,
IServiceProvider provider,
object value)
{
var property = context.PropertyDescriptor;
var config = (MyConfigAttribute)
property.Attributes[typeof(MyConfigAttribute)];
// ...
}
}
We have a custom ConfigurationManager library that serializes/deserializes a config.json file into an ExpandoObject.
Would it be possible to create a custom attribute that overrides the Getter/Setter of these properties to abstract this ExpandoObject?
Ideally I would be able to use the Attribute like this:
[System.AttributeUsage(System.AttributeTargets.Property)]
class Configureable : System.Attribute
{
public string Default { get; set; }
public bool IsEncrypted { get; set; }
}
class Test
{
[Configureable(Default = "0",IsEncrypted = false)]
public string MyValue { get; set; }
}
When I set the value of the decorated property I want to auto-magically update the value of the ExpandoObject, which would then in turn force an update be written to my config.json file.
When I access the value of the decorated property I want the getter to actually return the value of the underlying ExpandoObject. I can do this by manually having the developer modify the getter/setter. I was wondering if I could also do this with code inside of the attribute.
Thank you!
I found http://doc.postsharp.net/location-interception
That seems to do exactly what I want.
[System.AttributeUsage(System.AttributeTargets.Property)]
[Serializable]
class Configureable : LocationInterceptionAspect
{
public string Default { get; set; }
public bool IsEncrypted { get; set; }
public override void OnGetValue(LocationInterceptionArgs args)
{
base.OnGetValue(args);
if (args.Value == null)
{
}
}
public override void OnSetValue(LocationInterceptionArgs args)
{
//base.OnSetValue(args);
}
}
class Test
{
[Configureable(Default = "0",IsEncrypted = false)]
public string MyValue { get; set; }
}
ExpandoObject is a dictionary with object syntax. It is useful only in simple scenarios. If you need complex logic, use DynamicObject intead. Override its TryGetMember and TrySetMember methods to replicate functionality of ExpandoObject, then customize logic of these methods in the way you want.
It's not clear what your requirements are though. If you have a class which holds properties, what is the point of having dynamic objects?
I'm trying to add a nested property to my custom control using a TypeConverter, here is my test code:
public class TestNestedOptionConverter : TypeConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context,
object value, Attribute[] filter)
{
return TypeDescriptor.GetProperties(typeof(TestNestedOption));
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
[TypeConverter(typeof(TestNestedOptionConverter))]
public class TestNestedOption
{
bool test1 = false;
[Description("TestParam1")]
public bool Test1
{
get { return test1; }
set { test1 = value; }
}
[Description("TestParam2")]
public int Test2 { get; set; }
}
public partial class UserControl1 : UserControl
{
public TestNestedOption TestOption { get; set; }
public UserControl1()
{
InitializeComponent();
}
}
When I add the control to a form, I see the TestOption property in the designer property grid, but the sub-properties do not show up at all (there isn't even an expansion box next to TestOption).
My understanding of this is that it is supposed to sort of recursively call the GetProperties() method on each property, so as a test hack I put a MessageBox.Show() in the TestNestedOptionConverter.GetProperties() method, and I don't see the message when the designer loads the control. This makes me think that the overridden GetProperties() is never being called by the designer for some reason.
Any ideas about what I am doing wrong?
I'm using Visual Studio 2008.
It can't display properties for the object because the object is null. Try simply creating a new object in the UserControl1 constructor:
public partial class UserControl1 : UserControl
{
public TestNestedOption TestOption { get; set; }
public UserControl1()
{
InitializeComponent();
TestOption = new TestNestedOption();
}
}
Also, rather than writing a custom TypeConverter for this, you can just use ExpandableObjectConverter, which does exactly what you've written. If you need to override other methods, you still may want to inherit from it.
I want to avoid placing an EditorAttribute on every instance of a certain type that I've written a custom UITypeEditor for.
I can't place an EditorAttribute on the type because I can't modify the source.
I have a reference to the only PropertyGrid instance that will be used.
Can I tell a PropertyGrid instance (or all instances) to use a custom UITypeEditor whenever it encounters a specific type?
Here is an MSDN article that provides are starting point on how to do this in .NET 2.0 or greater.
You can usually associate editors etc at runtime via TypeDescriptor.AddAttributes. For example (the Bar property should show with a "..." that displays "Editing!"):
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
class Foo
{
public Foo() { Bar = new Bar(); }
public Bar Bar { get; set; }
}
class Bar
{
public string Value { get; set; }
}
class BarEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
MessageBox.Show("Editing!");
return base.EditValue(context, provider, value);
}
}
static class Program
{
[STAThread]
static void Main()
{
TypeDescriptor.AddAttributes(typeof(Bar),
new EditorAttribute(typeof(BarEditor), typeof(UITypeEditor)));
Application.EnableVisualStyles();
Application.Run(new Form { Controls = { new PropertyGrid { SelectedObject = new Foo() } } });
}
}
Marc's solution bluntly applies the EditorAttribute to the Bar type globally. If you have a delicate disposition, you might rather annotate properties of a specific instances. Alas, that isn't possible with TypeDescriptor.AddAttributes
My solution was to write a wrapper ViewModel<T>, which copies properties from T, annotating some with extra attributes. Suppose we have a variable datum of type Report, we'd use it like this
var pretty = ViewModel<Report>.DressUp(datum);
pretty.PropertyAttributeReplacements[typeof(Smiley)] = new List<Attribute>() { new EditorAttribute(typeof(SmileyEditor),typeof(UITypeEditor))};
propertyGrid1.SelectedObject = pretty;
Where ViewModel<T> is defined:
public class ViewModel<T> : CustomTypeDescriptor
{
private T _instance;
private ICustomTypeDescriptor _originalDescriptor;
public ViewModel(T instance, ICustomTypeDescriptor originalDescriptor) : base(originalDescriptor)
{
_instance = instance;
_originalDescriptor = originalDescriptor;
PropertyAttributeReplacements = new Dictionary<Type,IList<Attribute>>();
}
public static ViewModel<T> DressUp(T instance)
{
return new ViewModel<T>(instance, TypeDescriptor.GetProvider(instance).GetTypeDescriptor(instance));
}
/// <summary>
/// Most useful for changing EditorAttribute and TypeConvertorAttribute
/// </summary>
public IDictionary<Type,IList<Attribute>> PropertyAttributeReplacements {get; set; }
public override PropertyDescriptorCollection GetProperties (Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>();
var bettered = properties.Select(pd =>
{
if (PropertyAttributeReplacements.ContainsKey(pd.PropertyType))
{
return TypeDescriptor.CreateProperty(typeof(T), pd, PropertyAttributeReplacements[pd.PropertyType].ToArray());
}
else
{
return pd;
}
});
return new PropertyDescriptorCollection(bettered.ToArray());
}
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
}
As defined above, this substitutes properties of a specific type, but you can substitute properties by name if you need the greater resolution.
Just add an Editor attribute to your class.