Updates to CodeGeneration module

Topics: Announcements, General
Mar 3, 2012 at 4:58 PM
Edited Mar 3, 2012 at 10:47 PM

I made some updates to Szmyd's CodeGeneration module. Tweeted to him to ask what the best way to contribute them back so others can use it too. 


  • Generates Edit$$Partname$$ViewModel.cs
  • Part Driver has if/then branches for 3 common DisplayTypes: Summary, SummaryAdmin, and a default case
  • Driver.Editor() passes Edit$$PartName$$ViewModel to shape (instead of Content part)
  • Driver.Editor(..., IUpdateModel,...) passes ViewModel to TryUpdateModel(), and if successful, maps all properties from VM to part
  • generated Driver now includes Importing() and Exporting() methods, that imports/exports all the part's properties


Here's an example command I passed in: 

codegen part TheMonarch Gatt /Properties:"Name:string, Description:string, InstId:int, Cost:decimal"

And here is the GattDriver.cs class that gets generated: 

using System; 
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using Orchard.Localization;
using Orchard.UI.Notify;
using TheMonarch.Models;
using TheMonarch.ViewModels;

namespace TheMonarch.Drivers {
    public class GattDriver : ContentPartDriver<Gatt> {
        private readonly INotifier _notifier;
        private const string TemplateName = "Parts/Gatt.Edit";

        public Localizer T { get; set; }

        public GattDriver(INotifier notifier) {
            _notifier = notifier;
            T = NullLocalizer.Instance;

        protected override DriverResult Display(Gatt part, string displayType, dynamic shapeHelper) {
            if (displayType.Equals("Summary", StringComparison.OrdinalIgnoreCase)) {
				return ContentShape(
					, () => shapeHelper.Parts_Gatt(ContentItem: part.ContentItem)
            else if (displayType.Equals("SummaryAdmin", StringComparison.OrdinalIgnoreCase)) {
				return ContentShape(
					, () => shapeHelper.Parts_Gatt(
						ContentItem: part.ContentItem
            else {
				return ContentShape(
					, () => shapeHelper.Parts_Gatt(
						ContentItem: part.ContentItem

        protected override DriverResult Editor(Gatt part, dynamic shapeHelper) {
            return ContentShape(
                , () => shapeHelper.EditorTemplate(
					TemplateName: TemplateName
					, Model: BuildViewModel(part)
					, Prefix: Prefix

        protected override DriverResult Editor(Gatt part, IUpdateModel updater, dynamic shapeHelper) {
			var vm = new EditGattViewModel(); 
			if (updater.TryUpdateModel(vm, Prefix, null, null)) {
                _notifier.Information(T("Gatt edited successfully"));

                part.Name = vm.Name;
                part.Description = vm.Description;
                part.InstId = vm.InstId;
                part.Cost = vm.Cost;
            else {
                _notifier.Error(T("Error during Gatt update!"));

            return Editor(part, shapeHelper);

        private EditGattViewModel BuildViewModel(Gatt part) {
            return new EditGattViewModel {

				Name = part.Name,
				Description = part.Description,
				InstId = part.InstId,
				Cost = part.Cost,

        protected override void Importing(Gatt part, ImportContentContext context) {
            string partName = part.PartDefinition.Name;

			context.ImportAttribute(partName, "Name", value => part.Name = value);
			context.ImportAttribute(partName, "Description", value => part.Description = value);
			context.ImportAttribute(partName, "InstId", value => part.InstId = System.Convert.ToInt32(value));
			context.ImportAttribute(partName, "Cost", value => part.Cost = System.Convert.ToDecimal(value));

        protected override void Exporting(Gatt part, ExportContentContext context) {
			string partName = part.PartDefinition.Name;

			context.Element(partName).SetAttributeValue("Name", part.Name);			
			context.Element(partName).SetAttributeValue("Description", part.Description);			
			context.Element(partName).SetAttributeValue("InstId", part.InstId);			
			context.Element(partName).SetAttributeValue("Cost", part.Cost);
Mar 3, 2012 at 5:09 PM

I'd like to see some better code-gen and/or scaffolding for Orchard. In the spirit of DRY it would be nice to have codegen read a single definition and then output everything. Szmyd's version is pretty useful but there are some issues: 

  • I have to save the codegen commands in case I decide to alter my part and want to regenerate all the related code. 
  • No support for DataAnnotations -> would be nice to define once, and then have the annotations flow to part/viewmodel/record/data migration schema
  • No support for generating Migration commands. I'm thinking for now to have it output into a scratch directory and then you could copy the code from that output into your Migrations.cs
  • Maybe some support for 1:N and N:N stuff, mainly getting a droplist or checkboxes onto the editor, and then having IList or IEnumerable for the properties in the viewmodel. 

I might be able to work on some of these. For now let me know if anyone wants to check out the code for the changes mentioned in the OP. 

Mar 5, 2012 at 5:45 AM

That's nice!

Definitely codegen/scaffolding needs to be extended - I agree with your point. 

Please file a pull request to the Codegen Extensions module (http://orchardcodegen.codeplex.com/) with your recent changes - I will incorporate them. The best option would be to create a switch on the "codegen part" - eg. UseViewModel:true|false

I was recently thinking about building a codegen command that would take an XML file that describes the code to be generated and build everything based on it. First - single commands can get really long if we'll be adding more and more switches. Second - as you said - right now one needs to save all the commands for furter reuse. Schema of such file should be carefull discussed about, of course.