Search code examples
c#sqlcodefluent

CodeFluent Aspect for Full-Text Index


I'm trying to develop a CodeFluent aspect to set a property of a entity to be a full-text index.

I've found this link, which does something similar to what I'm aiming for. http://blog.codefluententities.com/2012/11/27/using-the-sql-server-template-producer-to-generate-clustered-indexes/

However this uses a SQL template producer. Are there anyway to set a property to be a full-text index entirely in the aspect itself, so I don't have to install/maintain both template producer and aspect for all projects?

Here's the C# aspect code I have so far:

    public class FullTextIndexing : IProjectTemplate
    {
        public static readonly XmlDocument Descriptor;
        public const string Namespace = "http://www.softfluent.com/aspects/samples/FullTextIndexing";

        static FullTextIndexing()
        {
            Descriptor = new XmlDocument();
            Descriptor.LoadXml(
@"<cf:project xmlns:cf='http://www.softfluent.com/codefluent/2005/1' defaultNamespace='FullTextIndexing'>
    <cf:pattern name='Full Text Indexing' namespaceUri='" + Namespace + @"' preferredPrefix='fti' step='Tables'>
        <cf:message class='_doc'>CodeFluent Full Text Indexing Aspect</cf:message>
        <cf:descriptor name='fullTextIndexing'
            typeName='boolean'
            category='Full Text Indexing'
            targets='Property'
            defaultValue='false'
            displayName='Full-Text Index'
            description='Determines if property should be full text indexed.' />
    </cf:pattern>
</cf:project>");
        }

        public Project Project { get; set; }

        public XmlDocument Run(IDictionary context)
        {
            if (context == null || !context.Contains("Project"))
            {
                // we are probably called for meta data inspection, so we send back the descriptor xml
                return Descriptor;
            }

            // the dictionary contains at least these two entries
            Project = (Project)context["Project"];

            // the dictionary contains at least these two entries
            XmlElement element = (XmlElement)context["Element"];
            Project project = (Project)context["Project"];

            foreach (Entity entity in project.Entities)
            {
                Console.WriteLine(">>PROPERTY LOGGING FOR ENTITY "+entity.Name.ToUpper()+":<<");
                foreach (Property property in entity.Properties)
                {
                    Log(property);
                    if(MustFullTextIndex(property))
                    {
                        Console.WriteLine("CHANGING PROPERTY");
                        property.TypeName = "bool";
                        Log(property);
                    }
                }
            }

            // we have no specific Xml to send back, but aspect description
            return Descriptor;
        }

        private static bool MustFullTextIndex(Property property)
        {
            return property != null && property.IsPersistent && property.GetAttributeValue("fullTextIndexing", Namespace, false);
        }

        private static void Log(Property property)
        {
            Console.WriteLine(property.Trace());
        }
    }

EDIT ONE:

Following Meziantou's answer, I'm trying to create a template producer, but it's giving me compilation errors when I try to add the new template producer to the project producers list, so I'm probably doing it wrong.

The error says:

Cannot convert type 'CodeFluent.Model.Producer' to 'CodeFluent.Producers.SqlServer.TemplateProducer'

Here's the code I have thus far:

public XmlDocument Run(IDictionary context)
{
    if (context == null || !context.Contains("Project"))
    {
        // we are probably called for meta data inspection, so we send back the descriptor xml
        return Descriptor;
    }

    // the dictionary contains at least these two entries
    XmlElement element = (XmlElement)context["Element"];
    Project project = (Project)context["Project"];

    CodeFluent.Producers.SqlServer.TemplateProducer producer = new CodeFluent.Producers.SqlServer.TemplateProducer();
    producer.AddNamespace("CodeFluent.Model");
    producer.AddNamespace("CodeFluent.Model.Persistence");
    producer.AddNamespace("CodeFluent.Producers.SqlServer");

    Console.WriteLine(producer.Element);
    //TODO: Need to figure out how to modify the actual template's contents

    project.Producers.Add(producer); //Error happens here


    // we have no specific Xml to send back, but aspect description
    return Descriptor;
}

Solution

  • In the sample code, the aspect is used only because it has a descriptor. Descriptors are used by CodeFluent Entities to populate the property grid:

    <cf:descriptor name="IsClusteredIndex" typeName="boolean" targets="Property" defaultValue="false" displayName="IsClusteredIndex" />
    

    So when you set the value of this property to true or false, the xml attribute ns:IsClusteredIndex is added or removed from the xml file.

    Then the SQL Template reads the value of the attribute to generate the expected SQL file:

    property.GetAttributeValue("sa:IsClusteredIndex", false)
    

    So the aspect is not mandatory, but provides a graphical interface friendly way to add/remove the attribute. If you don't need to integrate into the graphical interface, you can safely remove the aspect.

    If your goal is to integrate into the graphical interface, you need an aspect (XML or DLL) or a producer. If you don't want to create a producer, you can embed the template into your aspect. During the build, you can extract the SQL template and add the SQL Template producer to the project, this way everything is located in the aspect.