In V1 of the Entity Framework it is possible to annotate a schema using attributes declared in another XSD.
However XML attributes are a very limited form of annotation. It would be better if we could annotate using full elements.
This is what we are calling Structural Annotations.
This feature will allow both customers and partners like Reporting Services to modify the model so that it includes information important to them which can’t be captured in vanilla EDM format.
While it should be possible to annotate any level in the XML hierarchy, there are however some restrictions.
The general rule is you can annotation any CSDL / SSDL element that has a corresponding MetadataItem which in practice means everything except <Using>, <Schema>, <Key> and <PropertyRef> elements.
These annotations should be named, so that they can be accessed using the same API as today.
i.e. something like this:
<EntityType Name="Content"> <Key> <PropertyRef Name="ID" /> </Key> <Property Name="ID" Type="Guid" Nullable="false" /> <Property Name="HTML" Type="String" Nullable="false" MaxLength="Max" Unicode="true" FixedLength="false" /> <CLR:Attributes> <CLR:Attribute TypeName="System.Runtime.Serialization.DataContract"/> <CLR:Attribute TypeName="MyNamespace.MyAttribute"/> </CLR:Attributes> <RS:Security> <RS:ACE Principal="S-0-123-1321" Rights="+R+W"/> <RS:ACE Principal="S-0-123-2321" Rights="-R-W"/> </RS:Security> </EntityType>
This:
<EntityType Name="Content" CLR:Attribute="Blah"> <CLR:Attributes> <CLR:Attribute TypeName="System.Runtime.Serialization.DataContract"/> </CLR:Attributes>
…would be invalid, because of the identity collision, likewise this would also be invalid:
<EntityType Name="Content" My:Attribute="Blah"> <CLR:Attributes> <CLR:Attribute TypeName="System.Runtime.Serialization.DataContract"/> </CLR:Attributes> <CLR:Attributes> <CLR:Attribute TypeName="MyNamespace.MyAttribute"/> </CLR:Attributes>
where-as this is fine:
<EntityType Name="Content" My:Attribute="Blah"> <CLR:Attributes> <CLR:Attribute TypeName="System.Runtime.Serialization.DataContract"/> </CLR:Attributes> <RS:Security> <RS:ACE Principal="S-0-123-1321" Rights="+R+W"/> <RS:ACE Principal="S-0-123-2321" Rights="-R-W"/> </RS:Security>
… since all the above annotations have unique identities.
A structural annotation is simply an XML element. As such it can be considered a root of an XML document that can contain any valid XML structures. These structures are simply ignored by the Entity Framework and Metadata APIs.
If an element in the CSDL or SSDL has a corresponding MetadataItem in the Metadata API that element should support structural annotations. Since <Using>, <Schema>, <Key> and <PropertyRef> elements have no corresponding MetadataItem(s) they don’t support structural annotations. Notice while TypeUsage(s) have no CSDL representation today,they are MetadataItem(s) so they could in theory be annotated in the future. For example if a public mutable metadata API is produced.
In this example we are using the Entity Frameworks Metadata API to get the EdmType for an EntityType called Content. From that we then get the MetadataProperty called http://schemas.microsoft.com/RS/2006:Security:
EdmType contentType = metadataWorkspace.GetEdmType(“MyNamespace.Content”); if (contentType.MetadataProperties.Contains( "http://schemas.microsoft.com/RS/2006:Security") { MetadataProperty annotationProperty = contentType.MetadataProperties[ "http://schemas.microsoft.com/RS/2006:Security"]; object annotationValue = annotationProperty.Value; //Do something... }
Notice that the way you access structural annotations is the same as the way you access attribute annotations, i.e. by name/identity, in the MetadataProperties collection.
This of course means that their names can’t collide, name collisions should be picked up when metadata is loaded from persistent formats (like CSDL) or when annotations are created by hand in the any future mutable metadata API.
When creating a MetadataProperty to hold the XElement for the structural annotation, we need to assign a TypeUsage too. For V1 style attribute annotations, the TypeUsage we chose was based on Edm.String. Ideally we should use Edm.XML etc, but since this is not currently available, we will use Edm.String in the meantime. We have filled a bug to change this if/when [j1] XML becomes an EDM type.
The Value of the MetadataProperty will be a XElement for structural annotations originating from CSDL.
So if you know that the annotation is a structural annotation you can access it like this:
MetadataProperty annotationProperty = contentType.MetadataProperties["CLR:Attributes"];
XElement myAnnotation = annotationProperty.Value as XElement;
The XElement returned will be the XElement containing everything including the structural annotation itself.
There is nothing to stop the consumer of the Metadata API from modifying the XElement since it is simply a reference, to an instance of a type the Entity Framework doesn’t control.
We could address this by cloning on access to make things a little safer. But this negatively affects the mainline scenario, namely where a customer uses annotations purely for read, and is therefore not required.
The important thing to understand is that should a user modify the XElement all bets are off. We should provide guidance both via API hints:
/// <summary> /// .... /// <remarks> /// Should this return a reference type like an XElement, you should consider /// this immutable, no guarantees are made about behavior if this reference /// type is modified in anyway. /// </remarks> /// </summary> public object Value { get {
And via appropriate documentation.
Rather than try to invent a new universal data format for annotations, we decided to be pragmatic.
So we are limiting ourselves to XML for structural annotations loaded from CSDL (an XML format).
However since the MetadataProperty.Value is of type object, in a world where we have an alternative EDM representation (like in memory using mutable metadata for instance) we could easily assign something other than an XElement to the MetadataProperty.Value, like for example a xaml Serializable CLR type instance.
The current plan is to simply support annotation in the CSDL & SSDL. Meaning annotating MSL is currently out of scope.
If we uncover meaningful important scenarios for MSL annotations, we will undertake that work separately.
Incidentally supporting structural annotations in MSL touches a completely separate code path, and as a result will require roughly the same amount of work. Additionally in order to make undertaking this work meaningful we would first need to expose a public mapping metadata API.
....
As always we are keen to hear any feedback you have on this item.
Do you think you will find a use for Structured Annotations?
Alex James Program Manager, Entity Framework Team
This post is part of the transparent design exercise in the Entity Framework Team. To understand how it works and how your feedback will be used please look at this post.