3
votes

How can I force Orchard to rerun the Migrations.Create method to re-create my module's data? All the tutorials I've seen use the Migrations.UpdateFrom methods to make Orchard recognize module data changes, but this means I'll have to create a new method every time I make a change during development. These methods make sense for actual version updates, but not for initial development.

I've tried disabling and re-enabling the module, no dice. I've also tried uninstalling and reinstalling the module, but running the uninstall command permanently deleted the module from my computer, bypassing the recycling bin.

2

2 Answers

3
votes

I'm a little confused by you question, but I'll try to answer

Migrations update the database, and data in the database. If you want to make a change to a view you see in the dashboard, you shouldn't neet to re-run migrations.

however if you want to change the model and data, you do need to either run more migrations or reset the database and run the create migration.

When I rebuild a module I'm working on it will run any outstanding migration with out having to disable / enable the module on my local version,

That is to say if the current migration version is 1 and there is an UpdateFrom1 method, in the migration, this will be run when the project is built.

you can check the current version number in the following table Orchard_Framework_DataMigrationRecord

If you want to force rerun a migration, you can reset the value of table in the database. Or you can back up the database, and restore before running the entire suite of migrations.

While developing I use short migrations, creating a method each time I need to add data, I try and keep these small so I can identify any issues easily during development.

then before completing the module, amalgamate migrations into 2 or 3 logical blocks of code

Here's a migration for a custom type

public int Create()
        {
            // Define the project type 
            ContentDefinitionManager.AlterTypeDefinition("Project", cfg => cfg
                .WithSetting("Stereotype", "Content")
                .CommomPart()
                .AutoroutePart("our-work")
                .BodyPart()
                .WithPart("TitlePart")
                .WithPart("PublishLaterPart")
                .WithPart("MenuPart", builder => builder
                    .WithSetting("MenuPart.OnMenu", "true")
                    .WithSetting("MenuPart.CurrentMenu", "Project Menu"))
                .WithPart("Project")
                .Creatable()
                .Draftable());

            return 1;

        }

        public int UpdateFrom1()
        {
            // Define project part - having a part with the same name will create fields in the project type
            ContentDefinitionManager.AlterPartDefinition("Project", builder => builder
               .MediaPickerField("MainImage")
               .MediaPickerField("MediumImage")
               .MediaPickerField("SmallImage")
               .MediaPickerField("Logo")
               .TextField("ShortDescription", Flavour.TextArea)
               .TextField("Features", Flavour.Markdown)
               .TextField("ClientTitle", Flavour.DefaultFlavour)
               .TextField("ClientName", Flavour.DefaultFlavour)
               .TextField("ClientQuote", Flavour.Textarea)
               .BooleanField("MainProjectOnHomePage", false)
               .Attachable());

            return 2;
        }

I added some extension methods to make this less verbose.

here they are

 public static class MigrationExtentionHelpers
    {
        // part definitions 

        public static ContentPartDefinitionBuilder MediaPickerField(this ContentPartDefinitionBuilder builder, 
                                                                    string name, bool required = true, string hint = "")
        {
            var displayName = SplitCamel(name);

            // default implementation of Media picker field - create overloads for more options
            return  builder.WithField(name, fieldBuilder => fieldBuilder
                        .OfType("MediaPickerField")
                        .WithDisplayName(displayName)
                        .WithSetting("MediaPickerFieldSettings.Required", required.ToString(CultureInfo.InvariantCulture))
                        .WithSetting("MediaPickerFieldSettings.AllowedExtensions", "jpg png gif")
                        .WithSetting("MediaPickerFieldSettings.Hint", hint));

        }

        public static ContentPartDefinitionBuilder TextField(this ContentPartDefinitionBuilder builder, 
                                                             string name, Flavour flavor, bool required = true, string hint = "")
        {
            var strFlavor = SplitCamel(flavor.ToString());

            // default implementation of Media picker field - create overloads for more options
            return builder.WithField(name, fieldBuilder => fieldBuilder
                        .OfType("TextField")
                        .WithSetting("TextFieldSettings.Required", required.ToString(CultureInfo.InvariantCulture))
                        .WithSetting("TextFieldSettings.Flavor", strFlavor)
                        .WithSetting("TextFieldSettings.Hint", hint));

        }

        public static ContentPartDefinitionBuilder BooleanField(this ContentPartDefinitionBuilder builder,
                                                                string name, bool defalut, string hint = "")
        {
            // default implementation of Media picker field - create overloads for more options
            return builder.WithField(name, fieldBuilder => fieldBuilder
                        .OfType("BooleanField")
                        .WithSetting("BooleanFieldSettings.Hint", hint)
                        .WithSetting("BooleanFieldSettings.DefaultValue", defalut.ToString(CultureInfo.InvariantCulture)));

        }

        // type definitions 

        public static ContentTypeDefinitionBuilder AutoroutePart(this ContentTypeDefinitionBuilder builder, string pathPrefix = "")
        {
            var pattern = string.Format("[{{Name:'{0}/Title', Pattern: '{0}/{{Content.Slug}}', Description: 'my-page'}}]", pathPrefix);

            return builder.WithPart("AutoroutePart", partBuilder => partBuilder
                        .WithSetting("AutorouteSettings.PatternDefinitions", pattern)); 
        }


        public static ContentTypeDefinitionBuilder BodyPart(this ContentTypeDefinitionBuilder builder, 
            Flavour defaultFlavour = Flavour.Markdown)
        {
            return builder.WithPart("BodyPart", partBuilder => partBuilder
                        .WithSetting("BodyTypePartSettings.Flavor", defaultFlavour.ToString()));            
        }

        public static ContentTypeDefinitionBuilder CommomPart(this ContentTypeDefinitionBuilder builder)
        {
            return builder.WithPart("CommonPart")
                        .WithSetting("OwnerEditorSettings.ShowOwnerEditor", false.ToString(CultureInfo.InvariantCulture).ToLower());

        }

        private static string SplitCamel(string enumString)
        {

            StringBuilder sb = new StringBuilder();

            char last = char.MinValue;
            foreach (char c in enumString)
            {
                if (char.IsLower(last) && char.IsUpper(c))
                {
                    sb.Append(' ');
                    sb.Append(c.ToString(CultureInfo.InvariantCulture).ToLower());
                }
                else
                {
                    sb.Append(c);
                }
                last = c;
            }
            return sb.ToString();

        }
    }
0
votes

Basically there is no way to run a migration twice I am aware of unless you restore db from previously created backup or tweak it directly. As for me it's not a problem, because during the development in my team we use local instances of dbs and after combine all the migrations into initial one and deploy to test/staging environment.