Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Project Build Failure with T4 template with partial class and interface
johnpreed
#1 Posted : Thursday, April 17, 2014 9:03:27 PM(UTC)
Rank: Newbie

Groups: Registered
Joined: 4/17/2014(UTC)
Posts: 2
Location: United States of America

I'm running NCrunch 2.5.0.12. In my C# project, I added a Runtime Template. The runtime template generates a partial class. To customize this class, I added another file that further defines the partial and implements an interface. one of the methods of the interface is the TransformText() method. TransformText is implemented in the autogenerated class, but not in my partial with my additions. NCrunch fails to build, thinking that TransformText is not implemented. However, this compiles in visual studio and works when i debug it. Any clue what's going on or how to fix?

Here's the interface:

Code:

using MyApplication.Promotions;

namespace MyApplication
{
    /// <summary>
    /// Provides an interface for landing page templates
    /// </summary>
    public interface ILandingPageTemplate
    {
        /// <summary>
        /// Replaces tags and returns the landing page
        /// </summary>
        /// <returns></returns>
        string TransformText();

        /// <summary>
        /// Data used for tag replacement
        /// </summary>
        LandingPageMetaData Metadata { get; set; }
    }
}


Here's my customization of the template in a separate .cs file:

Code:

using System;
using System.Globalization;
using MyApplication.Promotions;

namespace MyApplication
{
    public partial class BaseLandingPage : ILandingPageTemplate
    {
        /// <summary>
        /// Data used for tag replacement
        /// </summary>
        public LandingPageMetaData Metadata { get; set; }

        /// <summary>
        /// Determines if a number is odd or even
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public bool IsOdd(int num)
        {
            return num%2 != 0;
        }

        /// <summary>
        /// Returns the html element for an image
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public string GetElementImage(PromoImage image)
        {
            if (image == null) return String.Empty;
            
            var source = GetAttribute("src", image.Source);
            var alt = GetAttribute(" alt", image.AlternateText);
            var classAttribute = GetAttribute(" class", image.Class);

            return String.Format(CultureInfo.InvariantCulture, "<img {0}{1}{2} />", source, alt, classAttribute);
        }

        /// <summary>
        /// Returns the attribute string for an html element
        /// </summary>
        /// <param name="attributeName"></param>
        /// <param name="attributeValue"></param>
        /// <returns></returns>
        public string GetAttribute(string attributeName, string attributeValue)
        {
            if (String.IsNullOrEmpty(attributeName)) return String.Empty;
            
            return String.IsNullOrEmpty(attributeValue)
                ? String.Empty
                : String.Format(CultureInfo.InvariantCulture, "{0}=\"{1}\"", attributeName, attributeValue);
        }
    }
}


Here's the autogenerated template class:

Code:

// ------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version: 12.0.0.0
//  
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace MyApplication
{
    using System.Linq;
    using System.Text;
    using System.Collections.Generic;
    using System;
    
    /// <summary>
    /// Class to produce the template output
    /// </summary>
    
    #line 1 "C:\workspace\onlinetech\WildTangent.com\src\WildTangent.Website.Models\Views\Marketing\Promotions\Templates\BaseLandingPage.tt"
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "12.0.0.0")]
    public partial class BaseLandingPage : BaseLandingPageBase
    {
#line hidden
        /// <summary>
        /// Create the template output
        /// </summary>
        public virtual string TransformText()
        {
            return this.GenerationEnvironment.ToString();
        }
    }
    
    #line default
    #line hidden
    #region Base class
    /// <summary>
    /// Base class for this transformation
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "12.0.0.0")]
    public class BaseLandingPageBase
    {
        #region Fields
        private global::System.Text.StringBuilder generationEnvironmentField;
        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
        private global::System.Collections.Generic.List<int> indentLengthsField;
        private string currentIndentField = "";
        private bool endsWithNewline;
        private global::System.Collections.Generic.IDictionary<string, object> sessionField;
        #endregion
        #region Properties
        /// <summary>
        /// The string builder that generation-time code is using to assemble generated output
        /// </summary>
        protected System.Text.StringBuilder GenerationEnvironment
        {
            get
            {
                if ((this.generationEnvironmentField == null))
                {
                    this.generationEnvironmentField = new global::System.Text.StringBuilder();
                }
                return this.generationEnvironmentField;
            }
            set
            {
                this.generationEnvironmentField = value;
            }
        }
        /// <summary>
        /// The error collection for the generation process
        /// </summary>
        public System.CodeDom.Compiler.CompilerErrorCollection Errors
        {
            get
            {
                if ((this.errorsField == null))
                {
                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
                }
                return this.errorsField;
            }
        }
        /// <summary>
        /// A list of the lengths of each indent that was added with PushIndent
        /// </summary>
        private System.Collections.Generic.List<int> indentLengths
        {
            get
            {
                if ((this.indentLengthsField == null))
                {
                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();
                }
                return this.indentLengthsField;
            }
        }
        /// <summary>
        /// Gets the current indent we use when adding lines to the output
        /// </summary>
        public string CurrentIndent
        {
            get
            {
                return this.currentIndentField;
            }
        }
        /// <summary>
        /// Current transformation session
        /// </summary>
        public virtual global::System.Collections.Generic.IDictionary<string, object> Session
        {
            get
            {
                return this.sessionField;
            }
            set
            {
                this.sessionField = value;
            }
        }
        #endregion
        #region Transform-time helpers
        /// <summary>
        /// Write text directly into the generated output
        /// </summary>
        public void Write(string textToAppend)
        {
            if (string.IsNullOrEmpty(textToAppend))
            {
                return;
            }
            // If we're starting off, or if the previous text ended with a newline,
            // we have to append the current indent first.
            if (((this.GenerationEnvironment.Length == 0) 
                        || this.endsWithNewline))
            {
                this.GenerationEnvironment.Append(this.currentIndentField);
                this.endsWithNewline = false;
            }
            // Check if the current text ends with a newline
            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
            {
                this.endsWithNewline = true;
            }
            // This is an optimization. If the current indent is "", then we don't have to do any
            // of the more complex stuff further down.
            if ((this.currentIndentField.Length == 0))
            {
                this.GenerationEnvironment.Append(textToAppend);
                return;
            }
            // Everywhere there is a newline in the text, add an indent after it
            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
            // If the text ends with a newline, then we should strip off the indent added at the very end
            // because the appropriate indent will be added when the next time Write() is called
            if (this.endsWithNewline)
            {
                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
            }
            else
            {
                this.GenerationEnvironment.Append(textToAppend);
            }
        }
        /// <summary>
        /// Write text directly into the generated output
        /// </summary>
        public void WriteLine(string textToAppend)
        {
            this.Write(textToAppend);
            this.GenerationEnvironment.AppendLine();
            this.endsWithNewline = true;
        }
        /// <summary>
        /// Write formatted text directly into the generated output
        /// </summary>
        public void Write(string format, params object[] args)
        {
            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
        }
        /// <summary>
        /// Write formatted text directly into the generated output
        /// </summary>
        public void WriteLine(string format, params object[] args)
        {
            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
        }
        /// <summary>
        /// Raise an error
        /// </summary>
        public void Error(string message)
        {
            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
            error.ErrorText = message;
            this.Errors.Add(error);
        }
        /// <summary>
        /// Raise a warning
        /// </summary>
        public void Warning(string message)
        {
            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
            error.ErrorText = message;
            error.IsWarning = true;
            this.Errors.Add(error);
        }
        /// <summary>
        /// Increase the indent
        /// </summary>
        public void PushIndent(string indent)
        {
            if ((indent == null))
            {
                throw new global::System.ArgumentNullException("indent");
            }
            this.currentIndentField = (this.currentIndentField + indent);
            this.indentLengths.Add(indent.Length);
        }
        /// <summary>
        /// Remove the last indent that was added with PushIndent
        /// </summary>
        public string PopIndent()
        {
            string returnValue = "";
            if ((this.indentLengths.Count > 0))
            {
                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
                if ((indentLength > 0))
                {
                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Remove any indentation
        /// </summary>
        public void ClearIndent()
        {
            this.indentLengths.Clear();
            this.currentIndentField = "";
        }
        #endregion
        #region ToString Helpers
        /// <summary>
        /// Utility class to produce culture-oriented representation of an object as a string.
        /// </summary>
        public class ToStringInstanceHelper
        {
            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;
            /// <summary>
            /// Gets or sets format provider to be used by ToStringWithCulture method.
            /// </summary>
            public System.IFormatProvider FormatProvider
            {
                get
                {
                    return this.formatProviderField ;
                }
                set
                {
                    if ((value != null))
                    {
                        this.formatProviderField  = value;
                    }
                }
            }
            /// <summary>
            /// This is called from the compile/run appdomain to convert objects within an expression block to a string
            /// </summary>
            public string ToStringWithCulture(object objectToConvert)
            {
                if ((objectToConvert == null))
                {
                    throw new global::System.ArgumentNullException("objectToConvert");
                }
                System.Type t = objectToConvert.GetType();
                System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
                            typeof(System.IFormatProvider)});
                if ((method == null))
                {
                    return objectToConvert.ToString();
                }
                else
                {
                    return ((string)(method.Invoke(objectToConvert, new object[] {
                                this.formatProviderField })));
                }
            }
        }
        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
        /// <summary>
        /// Helper to produce culture-oriented representation of an object as a string
        /// </summary>
        public ToStringInstanceHelper ToStringHelper
        {
            get
            {
                return this.toStringHelperField;
            }
        }
        #endregion
    }
    #endregion
}
Remco
#2 Posted : Thursday, April 17, 2014 11:15:58 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 6,976

Thanks: 931 times
Was thanked: 1257 time(s) in 1170 post(s)
Hi,

Thanks for sharing this issue. NCrunch itself doesn't care about the language structure being used here - as far as NCrunch is concerned, it's just a call to MSBuild to sort it out. I think that most likely whats happening here is that there is a dependency that hasn't been properly captured or a file that isn't being included in the NCrunch build.

If you examine the workspace constructed by NCrunch for this project (right click the failing project, go to Advanced->Browse to workspace), do you see any files missing? If you invoke MSBuild against the generated .proj file in this workspace, does the project build correctly?

Something else worth trying is setting NCrunch in compatibility mode. This will help to establish if the problem is being caused by any NCrunch features.
johnpreed
#3 Posted : Tuesday, April 22, 2014 9:47:18 PM(UTC)
Rank: Newbie

Groups: Registered
Joined: 4/17/2014(UTC)
Posts: 2
Location: United States of America

I think the problem is with msbuild and text transformation.. or at least the way i'm doing this. When I look at my workspace, my partial class has the correct namespace, but the autogenerated code from the template is the root namespace of the assembly. In my code samples above i fudged my actual namespaces for the sake of this example. So, the namespaces are different between my partial class files BaseLandingPage.cs and BaseLandingPage.Customized.cs and since TransformText() is implemented in one of the files, the compiler thinks the class doesn't implement the interface. I'm not sure how to fix this other than to put these classes in the root namespace.
Remco
#4 Posted : Tuesday, April 22, 2014 11:37:51 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 6,976

Thanks: 931 times
Was thanked: 1257 time(s) in 1170 post(s)
Can you share any more detail around how the autogeneration works? Is this a custom build step of some kind?
Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

YAF | YAF © 2003-2011, Yet Another Forum.NET
This page was generated in 0.056 seconds.
Trial NCrunch
Take NCrunch for a spin
Do your fingers a favour and supercharge your testing workflow
Free Download