I'm trying to create a custom C# Validation Attribute(without sealed keyword) for Data Annotation.
public class MyValidatorAttribute : ValidationAttribute
{
}
But Microsoft page here says "Make the class not inheritable." why?
Do I overlooked something by allowing "MyValidatorAttribute" class as inheritable?
According to this code analysis rule it is for performance reasons.
When retrieving custom attributes .NET will normally search the inheritance hierarchy and this can add an overhead that may not be required.
This would not stop you creating an abstract attribute that several sealed implementations inherited from.
Related
I wrote a class like this
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AbilityTemplateAttribute {
}
It makes
Error CS0641 Attribute 'AttributeUsage' is only valid on classes derived from System.Attribute
How can I make my own Attribute work similarly, that is, does not allow to be attached to classes not inherited from a certain class?
The only option I know of to have similar behaviour with custom attribute is to write custom Roslyn analyzer. If your are ok going down this route here some documentation:
Getting Started Writing a Custom Analyzer & Code Fix
How to write a Roslyn Analyzer
Tutorial: Write your first analyzer and code fix
I am trying to learn a smart, design pattern-ish way of validating the properties of a library class that holds the data of an e-commerce order returned from a web-service (eBay SDK).
There are other questions on this, but I haven't been able to apply them to my situation because:
The class I want to validate is from an SDK, it does not have properties marked virtual, deriving from it means I have to hide the base properties with new and call base in the new properties to access them.
If I derive from the base class and use the new modifier with base.propertyName, to effectively duplicate the class properties and also be able to add the validation attributes above them, I cannot cast the object I'm trying to validate to this derived class with attributes to be able to call .Validate() as you cannot 'cast up' from a base-type to a derived type.
Is there any approach to contract-style class properties validation, whereby you have no class-definition control of the class you're validating? Instead of creating a validator class that has 20 if-statements doing null-checking and logic on each property?
I only need this for a specific object and I know the properties and what values are valid, I just don't feel passing the order to a class with a bunch of if-statements is good in terms of maintainability and code-quality.
Here is the code I was thinking of for the derived type I could invoke .Validate() on, I stopped writing this as I don't know how to take baseclass, transform it into this class:
public class ValidatableOrderType : eBay.Service.Core.Soap.OrderType
{
public ValidatableOrderType(eBay.Service.Core.Soap.OrderType baseType)
{
}
[Required(ErrorMessage = "OrderID cannot be null.")]
new public string OrderID
{
get { return base.OrderID; }
}
}
I think your best choice its to use fluentvalidator.
https://fluentvalidation.net/
Its a brilliant software for validation even more powerful than metadata attributes.
I hope this helps you
I was trying to create an attribute that implies [Serializable] but I noticed that this SerializableAttribute class is sealed.
In Java it was possible to create an interface (say, MyInterface) that is inherited from Serializable interface and so all the subclasses of MyInterface would also be serializable, even its sub-sub classes would be so.
Let's say I am creating an ORM and I want customers to annotate their entity classes as [DatabaseEntity] but in order to make sure that entities are serializable, I also need to ask them to attribute their classes with extra [Serializable] which does not look quite compact and neat.
I am wondering why SerializableAttribute class is sealed and why has Inherited=false which implies that subclasses of serializable class will not be serializable unless it is explicitly stated. What motives are behind these design choices?
The SerializableAttribute is only used by the BinaryFormatter. If you are writing your own serialiser then don't worry about.
The sealed keyword is applied to the attribute not the class associated with the attribute. It is saying that the SerializableAttribute cannot be subclassed.
The BinaryFormatter uses an opt-in model. Any class (or subclass) must specify that it is serializable. This why the Inherited=false is used.
It's suggested best practice that all .Net attributes should be sealed, according to Microsoft:
The .NET Framework class library provides methods for retrieving custom attributes. By default, these methods search the attribute inheritance hierarchy; for example System.Attribute.GetCustomAttribute searches for the specified attribute type, or any attribute type that extends the specified attribute type. Sealing the attribute eliminates the search through the inheritance hierarchy, and can improve performance. [my emphasis]
So [Serializable] is sealed because it's quicker for .Net reflection to check the attributes. The cost is that you can't inherit and extend SerializableAttribute.
You can make your own un-sealed attributes if you want (you'll get code analysis warnings though).
This gets a little confusing with how attributes are used in inheritance for the classes that they apply to. It's probably best to use an example:
[Serializable]
public class A
{
public int SimpleSerialisableProperty { get; set;}
}
public class B : A
{
public C ComplexReferenceProperty { get; set; }
}
[Serializable]
public class D : A
{
public bool AnotherSerialisableProperty { get; set;}
}
You asked why SerializableAttribute.Inherited = false and this is why:
Class A is marked as [Serializable], and it is.
However class B inherits A and extends it with properties that are not serialisable. If .Net tries to serialise B it will encounter an error.
That Inherited = false tells .Net that just because A has been marked as [Serializable] not every class that inherits it will be serialisable too.
Now class D inherits A and is serialisable, so it gets its own [Serializable] attribute.
Finally, in terms of design attributes are a great way of extending behaviour (nice UI editors in property grids, etc). However they are terrible at enforcing it. If you need your customers to implement their entity classes in a particular way then an abstract base class or an interface is a much better way to go. If you make it an attribute then you're basically letting them know that [Serializable] is an option that you can handle either way.
Serialization is not a magic thing and you don't need any attribute to serialize an object. It is a process of writing your class' properties and fields to a stream (and attributes are only directives to serializers about how to behave while outputting an object).
See this over-simplified serializer code which totally ignores all attributes including NonSerializable
object obj = yourObject;
var props = obj.GetType()
.GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(obj, null));
string serializedText = String.Join("\n",
props.Select(kv => kv.Key + "=" + kv.Value ?? kv.Value.ToString()));
Above code, for example, would give
IsEmpty=False
X=3
Y=5
for object obj = new Point(3,5);
Deserialization process would be to read these values and set the properties back accordingly.
Put the [Serializable] attribute on top of the class you want serialized. Serialization is opt-in process. You have to manually do that for each class you want serialized. There are bunch of other keywords.
I recently read about attributes and reflection and I thought it would be a good method to include metadata in my program. I have this abstract class and I wanted all classes inheriting from it to declare with the class some attribute, since I wanted custom components(those derived classes) to be created for my program and wanted to read the metadata of these classes on runtime. However, the derived classes all have to explicitly declare the attribute in which I store metadata. So how to I force an attribute declaration in the derived classes? Thanks.
Define your attribute class to itself have an AttributeUsageAttribute attribute where the Inherited property is true.
Or don't, since that's the default...
Derived targets (that is, classes if the attribute is on a class, methods if it is on a method, etc.) will then inherit the attribute without explicit declaration.
If by "force", you mean "compile time enforcement": You can't.
As Daniel said, you cannot enforce attributes at compile time.
But if you want to read the data at runtime, why bother with attributes and reflection at all? You can create an abstract method in your abstract class:
abstract class Base
{
public abstract string Metadata();
}
class Derived1 : Base
{
public override string Metadata()
{
return "Metadata for Derived1";
}
}
class Derived2 : Base // won't compile, since Metadata has not been provided
{
}
The behaviour is slightly different, of course. With this option, you need a reference to an instance of the derived class instead of just the type information itself. On the other hand, it avoids reflection.
As Daniel says you can't force at compile time.
You could add the attribute(s) to the abstract parent and pick them up.
Another option is to add a method to check for the existence of the attrribute in the parent class and throw an exception if not present. Call that from suitable methods.
Came across this old question due to a similar use case. One way to enforce Attribute usage at compile time is by writing an analyzer, similar to what the xunit framework does. here's an example:
https://github.com/xunit/xunit.analyzers/blob/main/src/xunit.analyzers/TheoryMethodMustHaveTestData.cs
Involves a little more effort but does the work.
I am using Microsoft REST Stark Kit Preview 2 to explore REST Collection WCF Service. Within the Service.svc.cs class, there are some classes and interfaces being used as base or component classes. For example:
public interface ICollectionService<TItem> where TItem : class
{
...
[WebHelp(Comment = "Returns the items in the collection in JSON format, along with URI links to each item.")]
[WebGet(UriTemplate = "?format=json", ResponseFormat = WebMessageFormat.Json)]
ItemInfoList<TItem> GetItemsInJoson();
...
}
[CollectionDataContract(Name = "ItemInfoList", Namespace = "")]
public class ItemInfoList<TItem> : List<ItemInfo<TItem>> where TItem : class
...
where ICollectionServices and ItemInfoList are all in Microsoft.ServiceModel.Web.dll in the Preview 2. I would change those item's attributes such as WebHelp's Comment and CollectionDataContract's Name so that I could customize help message and templates for xml node names. The Preview 2's change with embedding those interfaces and classes in its dll makes it difficult to do any customization.
So my question is that if there is any way to change a class or interface's attributes or overwrite their existing attributes so that I don't need to get the source codes to make changes?
No, you can't.
What you might be able to do is inherit from the classes. If the attributes in question are not inheritable, you can add your own to your subclasses to override them.
I checked the CollectionDataContractAttribute, and it, at least, is not inheritable. That means if you create a subclass, you can apply a different CollectionDataContract attribute to that subclass.
[CollectionDataContract(Name = "MyItemInfoList", Namespace = "MyNamespace")]
public class MyItemInfoList<TItem> : ItemInfoList<TItem> where TItem : class
However, with members, this approach will only work if they are virtual, so you can override them.
Attributes are burnt in at compile time, and cannot for reflection be set at runtime. There are a few things you can do in "ComponentModel", but they wouldn't apply here. The only other common case here is for "i18n", where an attribute might be designed to pick up different values at runtime (from resx etc, based on a key rather than the description being set in the code) - and again, this doesn't apply in this case.
So, no.
In terms of REST Start kit Preview 2's customization issue, it look like the customization was disabled when the template basic classes are moved to its core dll. According to WCF REST Start kit forum at ASP.NET, the customization features will be back in the next release (soon).