Home > .NET, Developing Using CodeFluent Entities, Producers > Creating a custom sub producer to only generate resources

Creating a custom sub producer to only generate resources


Generating a CodeFluent Entities model looks at all the producers, and produce each one of them, assuming they have been enabled. The BOM producer is the most important producer and generates classes from your entities. If we break down that producer, we realize that it produces different things, such as templates, resources, membership providers, membership roles, constants…

What if you wanted to only produce resources, and nothing else?
One reason would be that your project has a lot of entities, your model is pretty much finished, but you still need to add resources. Moreover, your project is hosted on TFS online, and you do not want to check out, and check in, all the new generated files, especially if they have not changed!

By default, TFS checks out all the modified files (by a human or a code generator). Then, you need to check them back in. Hosted online, this means that the check in process would contain almost all the files of your BOM and it could be time consuming.

In this post we will create a custom sub producer that will only generate resources, and nothing else.

1. Creating the project

First, you need to create a new class library project, add some of the CodeFluent Entities references as well as 2 classes (BuildOnlyResources.cs and Constants.cs):

class library project

2. Writing classes

The BuildOnlyResources class extends the ICodeDomSubProducer interface to plug itself into the OnCodeDomProduction event. The first thing to do is to cancel the production of everything besides Resources. To do this, we check the eventType retrieved from the parameter of the OnCodeDomProduction method, and set the cancel property to true.

At this stage, the sub producer will stop generating all files, except the resources. Meanwhile, it will also delete the existing files, which is not what we want! We want to keep the existing files, without modifying them. This part is handled when the eventType corresponds to the UnitProducing value. There, we go through each class, and pretend they have been generated. That way, they will not be deleted if they exist. The method that does this is the AddToGeneratedFiles(path) method from the baseProducer class.

The Constants class simply contains 2 constants, used int the BuildOnlyResources class.

3. Adding options

We need to add a couple of options for our sub producer. The first is a boolean option that describes whether the sub producer is enabled or not. If not enabled, it will not be used during the generation. The second option is a boolean that describes whether the resources are produced or not.

4. Compiling the solution

We can now compile the solution in release mode and obtain a dll, called SubProducerResources.dll. In order to use that sub producer in our CodeFluent Entities model, we need to add this dll to the folder where CodeFluent Entities was installed, located at C:\Program Files (x86)\SoftFluent\CodeFluent\Modeler by default.

5. Displaying the subproducer in the modeler.

The last step is to be able to display that sub producer in the modeler. CodeFluent Entities looks at a config file located in: C:\Users\{user}\AppData\Roaming\CodeFluent.Modeler.Design\Custom.config. You need to create it because it does not exist. Its content needs to look  like this:

<codeFluent.Modeler>
 <producerDescriptors>
 <producerDescriptor name="SubProducerResources" displayName="SubProducer Resources" category="My Custom Producers" typeName="SubProducerResources.BuildOnlyResources, SubProducerResources" />
 </producerDescriptors>
</codeFluent.Modeler>

6. Ready

Your sub producer is ready to be used! Open your CodeFluent Entities project, right-click on your BOM producer, and add a new sub producer. You should see your sub producer in the list:

custom sub producer

Once added, it will appear under your BOM producer:

Custom Sub Producer

Here is what you are really looking for, the code for both BuildOnlyResources and Constants classes:

using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml;
using CodeFluent.Model;
using CodeFluent.Model.Common.Design;
using CodeFluent.Model.Design;
using CodeFluent.Producers.CodeDom;
using CodeFluent.Runtime.Utilities;

namespace SubProducerResources
{
    [Category("Business Layer Producers")]
    [DisplayName("SubProducer Resources")]
    [Producer(Constants.CustomResourcesProducerNamespaceUri, Constants.CustomResourcesProducerNamespacePrefix)]
    public class BuildOnlyResources : ICodeDomSubProducer
    {
        private CodeDomBaseProducer _baseProducer;
        private CodeFluent.Producers.CodeDom.SubProducer _subProducer;

        [Description("Determines if sub producer is enabled.")]
        [Category("Configuration")]
        [DefaultValue(true)]
        [DisplayName("Is Enabled")]
        [ModelLevel(ModelLevel.Normal)]
        public virtual bool Enabled
        {
            get
            {
                return XmlUtilities.GetAttribute(Element, "enabled", true);
            }
            set
            {
                XmlUtilities.SetAttribute(Element, "enabled", value.ToString().ToLowerInvariant());
            }
        }

        [Description("Determines if resources are produced.")]
        [Category("Configuration")]
        [DefaultValue(true)]
        [DisplayName("Produce Resources")]
        [ModelLevel(ModelLevel.Normal)]
        public virtual bool ProduceResources
        {
            get
            {
                return XmlUtilities.GetAttribute(Element, "produceResources", true);
            }
            set
            {
                XmlUtilities.SetAttribute(Element, "produceResources", value.ToString().ToLowerInvariant());
            }
        }

        public virtual void Initialize(CodeDomBaseProducer baseProducer, CodeFluent.Producers.CodeDom.SubProducer subProducer, IDictionary context)
        {
            _baseProducer = baseProducer;
            _subProducer = subProducer;
            baseProducer.CodeDomProduction += OnCodeDomProduction;
        }

        public virtual void Produce(IDictionary context, CodeCompileUnit unit)
        {
        }

        public virtual void Terminate(IDictionary context)
        {
        }

        private void OnCodeDomProduction(object sender, CodeDomProductionEventArgs e)
        {
            if (!Enabled)
                return;

            if (e.EventType == CodeDomProductionEventType.ResourcesProducing)
            {
                if (!ProduceResources)
                {
                    e.Cancel = true;
                }
            }
            if (e.EventType == CodeDomProductionEventType.UnitsProducing)
            {
                e.Cancel = true;
                IEnumerable<CodeCompileUnit> units = (CodeCompileUnit[])e.Argument;
                foreach (CodeCompileUnit unit in units)
                {
                    FakeProduceUnit(unit);
                }
            }
            // adapt to your needs
            if (e.EventType == CodeDomProductionEventType.ConstantsProducing ||
                e.EventType == CodeDomProductionEventType.SRProducing ||
                e.EventType == CodeDomProductionEventType.BasicAuthenticationModuleProducing ||
                e.EventType == CodeDomProductionEventType.BitsServerProducing ||
                e.EventType == CodeDomProductionEventType.MembershipProviderProducing ||
                e.EventType == CodeDomProductionEventType.MembershipUserProducing ||
                e.EventType == CodeDomProductionEventType.ProfileProviderProducing ||
                e.EventType == CodeDomProductionEventType.RoleProviderProducing)
            {
                e.Cancel = true;
            }
        }

        private void FakeProduceUnit(CodeCompileUnit unit)
        {
            if (unit == null)
                throw new ArgumentNullException("unit");

            BaseType baseType = UserData.GetBaseType(unit); // get the type related to this unit, if any
            if (baseType == null)
                return;

            // check if the type was to be produced or not

            if (!_baseProducer.MustProduce(baseType, CodeFluent.Producers.CodeDom.Constants.ModelProducerNamespaceUri))
                return;

            Set set = baseType as Set;
            if (set != null)
            {
                if (!_baseProducer.MustProduce(set.ItemEntity, CodeFluent.Producers.CodeDom.Constants.ModelProducerNamespaceUri))
                    return;
            }

            // determine the final target path where the file should have gone, and pretend it's been generated
            string path = ((CodeDomProducer)_baseProducer).GetTargetPath(baseType);
            path = _baseProducer.GetProductionTargetPath(baseType, path, false, false);

            if (File.Exists(path))
            {
                _baseProducer.AddToGeneratedFiles(path);
            }
        }

        public XmlElement Element
        {
            get
            {
                if (_subProducer == null)
                    throw new CodeFluentCodeDomProducerException(GetType().FullName);

                return _subProducer.Element;
            }
            set
            {
            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SubProducerResources
{
    public sealed class Constants
    {
        public const string CustomResourcesProducerNamespaceUri = "http://www.softfluent.com/codefluent/producers.customResources/2013/1";
        public const string CustomResourcesProducerNamespacePrefix = "cfcr";

        private Constants()
        {
        }
    }
}

Vincent Patry

  1. @obayani
    May 16, 2013 at 12:43 pm

    Hi,
    Thanks for this article. Why don’t you post the VS solution? It will much more readable and time saving.

  2. @obayani
    May 16, 2013 at 1:59 pm

    It works pretty well but a sub-producer can not be disabled. It means that when I don’t want to enable it I have to remove it completely. And then add it again when I need it.

  3. SoftFluent
    May 16, 2013 at 2:01 pm
  4. SoftFluent
    May 16, 2013 at 2:02 pm

    We just updated the post (and the solution) to include a couple of options (point 3) that will let you disable the sub producer, without removing it 🙂

  5. @obayani
    May 16, 2013 at 2:21 pm

    Excellent, it’s much better.
    Thanks.

  6. Dan
    May 22, 2013 at 6:31 pm

    Can you please briefly explain what a resource file is and possible give a couple of examples?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s