Resolve ContentPartDefinition into actual type and other reflection questions

Topics: Core, Writing modules
Oct 12, 2011 at 12:50 PM

Hi all,

We are working on OData (WCF Data) Service module for Orchard CMS. If you don't know what OData means please check it here: http://www.odata.org/developers/protocols I hope you'll find the idea to expose Content Types via OData Service interesting because it opens a lot of possibilities.

Here is a very basic implementation of module: https://github.com/netvoxlab/Nvx.Orchard/tree/master/src/Nvx.Orchard.OData

I'm trying to expose all properties of Content Type: https://github.com/netvoxlab/Nvx.Orchard/blob/master/src/Nvx.Orchard.OData/Models/OrchardDataServiceMetadataProvider.cs

private ResourceType GetComplexResourceType(ContentTypePartDefinition part) {
            ResourceType t;
            var name = part.PartDefinition.Name;
            if (resourceTypes.TryGetValue(name, out t))
                return t;
            t = new ResourceType(typeof(ContentPart), ResourceTypeKind.ComplexType, null, null, name, false);
            t.CanReflectOnInstanceType = false;
            t.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))));
            foreach (var field in part.PartDefinition.Fields)
            {
//?????? No fields
            }
            t.SetReadOnly();
            resourceTypes.Add(name, t);
            return t;
        }

For each ContentPart I need to create ResourceType instance with list of available properties (ResourceProperty). For instance for BlogPart it should be Name,Description,PostCount. How can I find this information?
Coordinator
Oct 12, 2011 at 9:41 PM

Reflection? Basically we don't force parts to expose a static description of what they do, which enables some very dynamic scenarios but prevents those very generic ones from being completely reliable. If you build a reflection-based provider here, that will probably work fine in most sistuations, but you should allow for an extensibility point for different models to be able to chime in as well. Look into export/import for an example of something very similar.

Oct 13, 2011 at 8:02 AM

Thank you for pointing to Orchard.ImportExport. I'll try to figure it out.

Oct 13, 2011 at 8:20 AM

It seems to me that Export doesn't work well. For instance here is metadata for BlogPost:

<?xml version="1.0"?>
<!--Exported from Orchard-->
<Orchard>
<Recipe>
<Name>Generated by Orchard.ImportExport</Name>
<Author>admin</Author>
</Recipe>
<Metadata>
<Types>
<BlogPost DisplayName="Blog Post" TypeIndexing.Included="true" ContentTypeSettings.Draftable="True"> <BlogPostPart/>
<CommonPart DateEditorSettings.ShowDateEditor="true"/>
<PublishLaterPart/>
<RoutePart/> 
<BodyPart/>
<CommentsPart/> 
<TagsPart/> 
<LocalizationPart/> 
<OpenStreetMapNodePart/> 
</BlogPost> 
</Types> 
<Parts>
<MenuPart ContentPartSettings.Attachable="True"/>
<CommonPart ContentPartSettings.Attachable="True"/> 
<PublishLaterPart ContentPartSettings.Attachable="True"/> 
<RoutePart ContentPartSettings.Attachable="True"/> 
<BodyPart ContentPartSettings.Attachable="True" BodyPartSettings.FlavorDefault="html"/> 
<TagsPart ContentPartSettings.Attachable="True"/> 
<LocalizationPart ContentPartSettings.Attachable="True"/> 
<CommentPart/> 
<IdentityPart ContentPartSettings.Attachable="True"/> 
<CommentsContainerPart/> 
<BlogPart/> 
<AdminMenuPart ContentPartSettings.Attachable="True"/> 
<BlogPostPart/> 
<CommentsPart ContentPartSettings.Attachable="True"/> 
<OpenStreetMapNodePart ContentPartSettings.Attachable="True"/> 
</Parts> 
</Metadata>
As you see there is no fields in parts. But there are some in the content itself:
<BlogPost Status="Published" Id="/Route.Slug=first-record"> 
<CommonPart ModifiedUtc="2011-10-10T22:38:16Z" PublishedUtc="2011-10-10T22:38:16Z" CreatedUtc="2011-10-10T22:14:56Z" Owner="/User.UserName=admin" Container="/Route.Slug=blog"/> 
<BodyPart Text="<p>First!</p>"/> 
<RoutePart Path="blog/first-record" Slug="first-record" Title="First Record"/> 
<CommentsPart CommentsActive="true" CommentsShown="true"/> 
<LocalizationPart Culture="en-US"/> 
<TagsPart Tags=""/> 
</BlogPost>
So it's not good enough...

Coordinator
Oct 13, 2011 at 8:22 PM

Can you explain what's missing? Note that for export to work, the parts and fields must implement their own export logic and if they don't well, there won't be any data for them.

Oct 13, 2011 at 8:27 PM

There is not enough part metadata, see:

<BlogPostPart/> 

There is no information on "Text" property.

 

As I understand I have to do some custom reflection-like system by myself and let content parts expose their fields, am I right?

Coordinator
Oct 13, 2011 at 8:41 PM

Text is on Body, not BlogPostPart.

Oct 13, 2011 at 8:44 PM

Sorry, my fault. But it doesn't change the issue. I've expected something like:

<BodyPart ContentPartSettings.Attachable="True" BodyPartSettings.FlavorDefault="html"> 
<Text Type="System.String" />
</BodyPart>

Coordinator
Oct 13, 2011 at 8:45 PM

No, this is not needed as the BodyPart's Text property is defined in code. Notice how the data is properly exported. I don't see a problem here.

Oct 13, 2011 at 9:15 PM

For built in export - no, but take a look on OData service metadata info:

http://odata.netflix.com/Catalog/$metadata

 

<EntityType Name="Title" m:HasStream="true">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false" MaxLength="128" Unicode="true" FixedLength="false"/>
<Property Name="Name" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" m:FC_TargetPath="SyndicationTitle" m:FC_ContentKind="text" m:FC_KeepInContent="True"/>
<Property Name="ShortName" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="Synopsis" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" m:FC_TargetPath="SyndicationSummary" m:FC_ContentKind="html" m:FC_KeepInContent="True"/>
<Property Name="ShortSynopsis" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="AverageRating" Type="Edm.Double" Nullable="true"/>
<Property Name="ReleaseYear" Type="Edm.Int32" Nullable="true"/>
<Property Name="Url" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="Runtime" Type="Edm.Int32" Nullable="true"/>
<Property Name="Rating" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="DateModified" Type="Edm.DateTime" Nullable="false" m:FC_TargetPath="SyndicationUpdated" m:FC_ContentKind="text" m:FC_KeepInContent="True"/>
<Property Name="Type" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="WebsiteUrl" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="NetflixApiId" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="TinyUrl" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false"/>
<Property Name="BluRay" Type="Netflix.Catalog.v2.DeliveryFormatAvailability" Nullable="false"/>
<Property Name="BoxArt" Type="Netflix.Catalog.v2.BoxArt" Nullable="false"/>
<Property Name="Dvd" Type="Netflix.Catalog.v2.DeliveryFormatAvailability" Nullable="false"/>
<Property Name="Instant" Type="Netflix.Catalog.v2.InstantAvailability" Nullable="false"/>
<NavigationProperty Name="Disc" Relationship="Netflix.Catalog.v2.Title_Disc" FromRole="Title_Disc_Source" ToRole="Title_Disc_Target"/>
<NavigationProperty Name="Season" Relationship="Netflix.Catalog.v2.Title_Season" FromRole="Title_Season_Source" ToRole="Title_Season_Target"/>
<NavigationProperty Name="Series" Relationship="Netflix.Catalog.v2.Title_Series" FromRole="Title_Series_Source" ToRole="Title_Series_Target"/>
<NavigationProperty Name="Movie" Relationship="Netflix.Catalog.v2.Title_Movie" FromRole="Title_Movie_Source" ToRole="Title_Movie_Target"/>
<NavigationProperty Name="AudioFormats" Relationship="Netflix.Catalog.v2.TitleAudioFormat_Title" FromRole="TitleAudioFormat_Title_Target" ToRole="TitleAudioFormat_Title_Source"/>
<NavigationProperty Name="Awards" Relationship="Netflix.Catalog.v2.TitleAward_Title" FromRole="TitleAward_Title_Target" ToRole="TitleAward_Title_Source"/>
<NavigationProperty Name="ScreenFormats" Relationship="Netflix.Catalog.v2.TitleScreenFormat_Title" FromRole="TitleScreenFormat_Title_Target" ToRole="TitleScreenFormat_Title_Source"/>
<NavigationProperty Name="Languages" Relationship="Netflix.Catalog.v2.Language_Titles" FromRole="Language_Titles_Target" ToRole="Language_Titles_Source"/>
<NavigationProperty Name="Cast" Relationship="Netflix.Catalog.v2.Person_TitlesActedIn" FromRole="Person_TitlesActedIn_Target" ToRole="Person_TitlesActedIn_Source"/>
<NavigationProperty Name="Directors" Relationship="Netflix.Catalog.v2.Person_TitlesDirected" FromRole="Person_TitlesDirected_Target" ToRole="Person_TitlesDirected_Source"/>
<NavigationProperty Name="Discs" Relationship="Netflix.Catalog.v2.Title_Discs" FromRole="Title_Discs_Source" ToRole="Title_Discs_Target"/>
<NavigationProperty Name="Episodes" Relationship="Netflix.Catalog.v2.Title_Episodes" FromRole="Title_Episodes_Source" ToRole="Title_Episodes_Target"/>
<NavigationProperty Name="Genres" Relationship="Netflix.Catalog.v2.Genre_Titles" FromRole="Genre_Titles_Target" ToRole="Genre_Titles_Source"/>
<NavigationProperty Name="Seasons" Relationship="Netflix.Catalog.v2.Title_Seasons" FromRole="Title_Seasons_Source" ToRole="Title_Seasons_Target"/>
</EntityType>

How can I do this for Content Parts?

Coordinator
Oct 13, 2011 at 9:21 PM

Yes, I suppose you would have to do reflection to get that level of detail automatically. My point earlier was that you can have a reflection-based fallback but I would still have a way to have specific pluggable logic, like export does.

May 16, 2014 at 2:42 PM
Hello,

You might may pay attention at this project.
https://orchardsyndication.codeplex.com/SourceControl/latest#Orchard.OData/Services/ODataServiceMetadata.cs
It seems I tried to do something similar.

Hoping this helps !
Regards,
Olivier