U4-3425 - App_Code XSLT Extensions aren't recognized

Created by Douglas Robar 06 Nov 2013, 11:34:54 Updated by Dan Evans 14 Jul 2014, 12:49:59

Relates to: U4-5220

Umbraco 7.0RC

(this effects XSLTsearch, which works with v4 and v6 flawlessly so this is a breaking change at the api level I suppose)

When using an app_code xslt extension he following error is thrown (seen in the UmbracoTraceLog): 2013-11-06 11:18:41,656 [12] INFO Umbraco.Core.PluginManager - [Thread 20] Starting resolution types of Umbraco.Core.Macros.XsltExtensionAttribute 2013-11-06 11:18:41,672 [12] INFO Umbraco.Core.PluginManager - [Thread 20] Completed resolution of types of Umbraco.Core.Macros.XsltExtensionAttribute, found 1 (took 16ms) 2013-11-06 11:18:41,714 [12] WARN umbraco.macro - <log4net.Error>Exception during StringFormat: Input string was not in a correct format. [Thread 20] Error parsing XSLT file. Exception: System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace 'urn:schemas-percipient-studios'. at System.Xml.Xsl.Runtime.XmlQueryContext.InvokeXsltLateBoundFunction(String name, String namespaceUri, IList1[] args) at System.Xml.Xsl.CompiledQuery.Query.<xsl:template match="/">(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current, Double {urn:schemas-microsoft-com:xslt-debug}position, Double {urn:schemas-microsoft-com:xslt-debug}last, IList1 namespaces) in c:\inetpub\training.7.0.0.rc\xslt\test2.xslt:line 19 at System.Xml.Xsl.CompiledQuery.Query.xsl:apply-templates(XmlQueryRuntime runtime, XPathNavigator , Double , Double ) at System.Xml.Xsl.CompiledQuery.Query.Root(XmlQueryRuntime runtime) at System.Xml.Xsl.CompiledQuery.Query.Execute(XmlQueryRuntime runtime) at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer) at System.Xml.Xsl.XslCompiledTransform.Transform(IXPathNavigable input, XsltArgumentList arguments, TextWriter results) at umbraco.macro.GetXsltTransformResult(XmlDocument macroXml, XslCompiledTransform xslt, Dictionary`2 parameters) at umbraco.macro.LoadMacroXslt(macro macro, MacroModel model, Hashtable pageElements, Boolean throwError)</log4net.Error>

It seems to have found the app_code file but then fails with not being able to find the namespace. Which is odd because:

  1. The file xslt file saves without error in the back office (I would have expected an error to show up)
  2. If the code from the .cs file is moved inline with the .xslt file it runs (but this is very sub-optimal)
  3. App_code xslt extensions have worked for years now

This XSLT macro works:

]>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:ps="urn:schemas-percipient-studios" xmlns:umbraco.library="urn:umbraco.library" exclude-result-prefixes="msxml ps umbraco.library" >

<xsl:output method="xml" omit-xml-declaration="yes" />

<xsl:param name="currentPage"/>
<xsl:template match="/">
        <xsl:value-of select="ps:doIt()" />
</xsl:template>

<!-- =========================================================== -->

<msxml:script language="CSharp" implements-prefix="ps">
    <![CDATA[
    public static String doIt()
    {
      return "Yippee! I've just done something from an <strong>in-line</strong> xslt extension!";
    }
    ]]>
</msxml:script>

</xsl:stylesheet>

However, this XSLT macro (and app_code extension below) does NOT work and gives the error above:

]>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:foo="urn:schemas-percipient-studios" xmlns:umbraco.library="urn:umbraco.library" xmlns:Examine="urn:Examine" exclude-result-prefixes="msxml umbraco.library Examine foo ">

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>
<xsl:template match="/">
            <xsl:value-of select="foo:doIt()" />
</xsl:template>

</xsl:stylesheet>

The associated /app_code/foo.cs file: using System; using System.Collections; using System.IO; using System.Web; using umbraco;

namespace foo { [XsltExtension]

public class Helpers
{
    public Helpers() { }

    public static String doIt()
    {
        return "Wahoo! I've just done something from an <strong>app_code</strong> xslt extension!";
    }
}

}

Background reading on inline extensions and why they are not good, how to use app_code xslt extensions can be found at: http://blog.percipientstudios.com/2009/9/21/advanced-xslt-with-net-namespaces.aspx http://blog.percipientstudios.com/2010/11/12/make-an-app_code-xslt-extension-for-umbraco.aspx http://www.nibble.be/?p=95

Comments

Stephan 06 Nov 2013, 11:50:38

Extension declared in XSLT with ''xmlns:foo="urn:schemas-percipient-studios"'' but declared in C# with ''[XsltExtension]'' in namespace ''foo''... not sure that's enough for Umbraco to to bind the two together. Suggesting you try [XsltExtension("schemas-percipient-studios")] or maybe it needs the "urn:" prefix too, can't remember.


andrew shearer 06 Nov 2013, 11:57:33

xslt support was dropped in umbraco 5 for good reasons. It should be phased out of 7 as a breaking change imo


Lee Kelleher 06 Nov 2013, 12:08:24

Stephan is correct, the XSLT extension is bound by the namespace/urn.

For Umbraco to wire it up the xmlns needs to be:

{{xmlns:foo="urn:foo"}}


Douglas Robar 06 Nov 2013, 13:03:28

Sorry for the false start. I was trying to simplify the example and went too far. Here's an update that avoids that obvious problem.

This works on 6.1.5 but not on 7.0.0rc:

]>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:foo.Helpers="urn:foo.Helpers" xmlns:umbraco.library="urn:umbraco.library" xmlns:Examine="urn:Examine" exclude-result-prefixes="msxml umbraco.library Examine foo.Helpers ">

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:template match="/">
            <xsl:value-of select="foo.Helpers:doIt()" />
</xsl:template>

</xsl:stylesheet>

The error is: 2013-11-06 12:54:32,821 [61] INFO Umbraco.Core.PluginManager - [Thread 59] Completed resolution of types of Umbraco.Core.Macros.XsltExtensionAttribute, found 1 (took 16ms) 2013-11-06 12:54:32,864 [61] WARN umbraco.macro - <log4net.Error>Exception during StringFormat: Input string was not in a correct format. [Thread 59] Error parsing XSLT file. Exception: System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace 'urn:foo.Helpers'. at System.Xml.Xsl.Runtime.XmlQueryContext.InvokeXsltLateBoundFunction(String name, String namespaceUri, IList1[] args) at System.Xml.Xsl.CompiledQuery.Query.<xsl:template match="/">(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current, Double {urn:schemas-microsoft-com:xslt-debug}position, Double {urn:schemas-microsoft-com:xslt-debug}last, IList1 namespaces) in c:\inetpub\training.7.0.0.rc\xslt\test2.xslt:line 19

As far as I know, the [XsltExtension] attribute doesn't take any parameters. Might have missed that one but it was never needed in the past and I don't see it with a google search. Would be handy to know if it does take a param and what it should be.

The important point in all of this is that XSLTsearch, which has been downloaded more than 20k times, blows up in 7.0.0rc. True, ezSearch is the future but such a long-standing and popular package should work in v7 just as it did in v6 and v4 shouldn't it? Or did I miss a memo?


Lee Kelleher 06 Nov 2013, 13:12:15

No worries about the namespace confusion - it's good to rule out anything trivial.

+1 to XSLTsearch working as is. As far as I'm aware there hasn't been any intentional breaking changes to the {{[XsltExtension]}} attribute/code.

As for the {{[XsltExtension]}} parameters - it does take an optional one for the namespace/alias. We use this on uComponents - e.g. {{[XsltExtension("custom.library")]}} - example here: https://ucomponents.codeplex.com/SourceControl/latest#uComponents.XsltExtensions/Strings.cs


Douglas Robar 06 Nov 2013, 13:19:29

@Lee - the link shows no file. But thanks for the summary above it. Didn't know about that!

I also think there are problems with package params in v7 but need to do more investigation before raising the alarm. It seems that XSLTsearch loads with all the params but no type associated with any param, which isn't groovy either (I use the contentTree macrotype in one case... that might be trouble since they are now datatypes and only have a contentpicker; but all others are text or number or bool so should be okay). The times, they are a changin' in spite of having the same 'engine' under the hood it seems.


Lee Kelleher 06 Nov 2013, 13:24:14

@Doug - not sure why the CodePlex link doesn't work - here's a [BitBucket mirror|https://bitbucket.org/ucomponents/ucomponents/src/b900a96ea8cf852cffcdf06abf7a27928e72aa01/uComponents.XsltExtensions/Strings.cs?at=6.0.0#cl-15] just in case.

I'll do some digging at the hackathon in London tomorrow - see if we can get to the bottom of it.


Douglas Robar 06 Nov 2013, 13:33:03

@Lee - thanks for the link; that one works. And thanks for some hackathon digging! I'll be around most of the day tomorrow so ping me anytime.


Per Ploug 06 Nov 2013, 13:42:53

So, the contentTree one, what does that normally return, just a node id, or an entire xml structure?


Douglas Robar 06 Nov 2013, 13:57:02

I'll have to go double-check but the macro saved the ID as the parameter and the 'contentTree' sent in (I think) a fragment of the umbraco.config from that starting node downward. I can certainly change XSLTsearch for that, to handle a single id and then get the nodes from there; I just remember thinking at the time I first wrote XSLTsearch (and didn't really know what I was doing) that umbraco had already done the work and so it was more efficient (and easier for me) to take what was passed in than having to use GetXmlNodeById().


Stephan 06 Nov 2013, 14:02:54

Sorry, was away for 1hr. @Doug... are we talking about two different things now, a) the xslt namespaces stuff and b) package params? Will have a look at the XLST stuff but I know very little about packages...


Douglas Robar 06 Nov 2013, 14:06:12

@Stephen - yes, sadly we are now muddling the issue. (my fault). I'm doing some tests to verify if there's a problem with params and packages such as XSLTsearch and will open a new issue if/when I can confirm a problem. EDIT: I've confirmed that bug as well. U4-3428

This issue is specifically about app_code xslt extensions. (in fact, I wonder if there's a similar problem with compiled extensions as well... hmmmm. @Lee should know quickly enough with uComponents since it uses them)


Stephan 07 Nov 2013, 10:38:41

OK, just to confirm... the following works on 6.2:

<xsl:stylesheet ... xmlns:foo="urn:foo.Helpers" exclude-result-prefixes="... foo"> ...

{code} With

namespace foo { [XsltExtension] public class Helpers { public Helpers()

    public static String doIt()
    {
        return "Wahoo! I've just done something from an <strong>app_code</strong> xslt extension!";
    }
}

} In App_Code.

And it fails on v7... now on to debug.


Stephan 07 Nov 2013, 11:50:13

Umbraco.Core.Macros.XsltExtensionsResolver.CreateInstances() does ''not'' know about foo.Helpers type. Because Umbraco.Web.PluginManagerExtensions.ResolveXsltExtensions() does not find it. Because Umbraco.Core.PluginManager.ResolveAttributedType<>() does not find it. Debug ResolveAttributedType:

In TypeFinder.FindClassesWithAttributes... assemblyList does contain AppCode. Then it is gone from referencedAssemblies. So shit happens in TypeHelper.GetReferencedAssemblies.

In there, when assembly is 'App_Code'... we call HasReferenceToAssemblyWithName(assembly, "Umbraco.Core") and assembly references mscorlib, System, and umbraco -- but not Umbraco.Core. So it is excluded. How come App_Code does not reference Umbraco.Core if it's using XsltExtensionAttribute?

? ? ?

TADA!!

Turns out there's umbraco.XsltExtensionAttribute ''and'' Umbraco.Core.Macros.XsltExtensionAttribute. The XSLT extension above contains

using umbraco; So it's using the ''wrong'' attribute. So is it just a matter of replacing it with

using Umbraco.Core.Macros; ? Works! Now obviously this is a breaking change that should be documented?


Douglas Robar 07 Nov 2013, 12:00:53

@Stephen - amazing detective work! Makes sense technically. Irks me practically. What a silly reason to force two different versions of a package. Hmmm.

Beyond making 2 different packages for a 1 line change to something that has worked in every version of Umbraco since... forever... is there any other potential solution to this? I know I'm one of the few that uses app_code extensions in a package but even so, is there any way to resolve this to not require a breaking change?


Stephan 07 Nov 2013, 12:03:32

Has to be discussed with whoever made the change. Not sure really. Reassigning to Sebastiaan so he can take care of it.


Shannon Deminick 11 Nov 2013, 04:18:55

The attribute has been moved because all of these things that have 'worked' forever are not declared in the correct projects, poorly implemented, etc... It is good for people to keep an eye on all obsoleted code and migrate to non-obsolete code before it is removed in future versions. Also good to keep an eye on the breaking change list for v7: http://our.umbraco.org/contribute/releases/700

That said, this should still work, the code for the legacy attribute just inherits from the new one:

/// <summary>
/// Allows App_Code XSLT extensions to be declared using the [XsltExtension] class attribute.
/// </summary>
/// <remarks>
/// An optional XML namespace can be specified using [XsltExtension("MyNamespace")].
/// </remarks>
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("Use Umbraco.Core.Macros.XsltExtensionAttribute instead")]
public class XsltExtensionAttribute : Umbraco.Core.Macros.XsltExtensionAttribute
{
    public XsltExtensionAttribute() : base()
    {
        
    }

    public XsltExtensionAttribute(string ns) : base(ns)
    {
        
    }

}

In fact there is a unit test: XsltExtensionsResolverTests.Find_All_Extensions to test that we can resolve extensions based on the old attribute and the new one. With that in mind, @Stephan you might be able to figure out why this particular problem exists? I'll assign back to you, if you don't have time please assign to me.


Stephan 12 Nov 2013, 15:40:38

OK I'll have a look at it.


Stephan 30 Jan 2014, 12:13:09

Notes for self: confirmed on 7.0.2, using the umbraco.XsltExtension attribute filas, whereas using the Umbraco.Core.Macros.XsltExtension attribute works. Now on to figure out why.


Stephan 30 Jan 2014, 13:40:50

Why: because when we look for types marked with a given attribute, in an assembly, we exclude from the list those assemblies that do not reference said attribute's assembly, for performance reasons. Takes place at TypeFinder:436.

So... the legacy attribute does inherit from the new attribute, but the App_Code code does ''not'' reference the assembly of the ''new'' attribute. Only the old assembly. So it is excluded from the search.

The workaround is to make sure the App_Code assembly does reference the proper assembly, eg by dropping a file named "ReferenceThatFxxxAssembly.cs" in App_Code, containing code such as:

using Umbraco.Core.Macros;

namespace Whatever { [XsltExtension] public class JustReferenceThatFxxxAssembly }

That will cause the App_Code assembly to reference the new attribute, so the assembly will not be excluded, so the extensions marked with the old assemblies ''will'' get detected. Tested. But it still requires work.

That being said. It is strange that App_Code does not recursively reference all assemblies... still looking into it.


Stephan 30 Jan 2014, 13:50:05

OK, did more tests. Create project Librarie1 containing attribute Attribute1 Create project Librarie2 containing attribute Attribute2 which inherits from Attribute1 In order to build, Librarie1 must be referenced by Librarie2 Create project Test containing class Attributed, marked with Attribute2 In order to build, both Librarie1 and Librairie2 must be referenced by Test

Check Librarie2 GetReferencedAssemblies() output: mscorlib, Librarie1 Check Test GetReferencedAssemblies() output: mscorlib, Librarie2

Note that Librarie1 is missing in that last line? So...

So I guess there ''is'' an issue with the TypeFinder actually. That may be a corner case, but anyway. Before I change anything, though, I'd really discuss it with Shannon => assigning the issue to Shannon.


Shannon Deminick 09 Feb 2014, 23:22:16

We recursively look-up referenced assemblies though so your test shouldn't be an issue. Perhaps this is an edge case with App_Code, not sure. Will have to run some tests.


Stephan 19 Apr 2014, 14:49:02

We don't.

TypeFinder does var referencedAssemblies = TypeHelper.GetReferencedAssemblies(attributeType, assemblyList); and then TypeHelper does return assemblies .Where(assembly => assembly == assignTypeFrom.Assembly || HasReferenceToAssemblyWithName(assembly, assignTypeFrom.Assembly.GetName().Name)) .ToArray(); and HasReferenceToAssemblyWithName does return assembly .GetReferencedAssemblies() .Select(a => a.Name) .Contains(expectedAssemblyName, StringComparer.Ordinal); Which is not recursive.

So if assembly.1 uses an attribute defined in assembly.2 by inheriting from an attribute defined in assembly.3, we don't consider that assembly.1 references assembly.3 so we exclude it from searches. Not limited to XSLT, could happen in other cases.

Fixing by doing: return assembly .GetDeepReferencedAssemblies() ... Where GetDeepReferencedAssemblies is defined in Umbraco.Core.AssemblyExtensions and recursively gets all the referenced assemblies.

Preparing commit...


Stephan 19 Apr 2014, 15:47:26

Pushed 12acdd8122a36c67255c14064e3bc20f4065b4f7 in 7.1.2 and backported as 536b8ee921ed7d626a2cbb3de81df5bd13c93ebf in 6.2.0. Shan, let me know what you think.


Sebastiaan Janssen 21 Apr 2014, 15:20:57

Now getting the error below. AssemblyExtensions.cs line 88.

Seems to happen when assembly is Umbraco.Web.UI and visAsm is System.Web.WebPages. Interestingly, I DO see a duplicate in allAssemblies but it's System.Web.WebPages.Deployment.

Adding a version check in the select seems to help but I don't know if that is a viable solution:

.Select(refAsmName => allAssemblies.SingleOrDefault(x => string.Equals(x.GetName().Name, refAsmName.Name, StringComparison.Ordinal) && x.GetName().Version == refAsmName.Version))

List of allAssemblies : allAssemblies.OrderBy(x => x.GetName().Name).ToList()

allAssemblies.ToList() Count = 80 [0]: {App_global.asax.fprybofb, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null} [1]: {AutoMapper, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null} [2]: {AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null} [3]: {businesslogic, Version=1.0.5224.29257, Culture=neutral, PublicKeyToken=null} [4]: {ClientDependency.Core, Version=1.7.1.2, Culture=neutral, PublicKeyToken=null} [5]: {ClientDependency.Core.Mvc, Version=1.7.0.4, Culture=neutral, PublicKeyToken=null} [6]: {cms, Version=1.0.5224.29257, Culture=neutral, PublicKeyToken=null} [7]: {controls, Version=1.0.5224.29258, Culture=neutral, PublicKeyToken=null} [8]: {CookComputing.XmlRpcV2, Version=2.5.0.0, Culture=neutral, PublicKeyToken=a7d6e17aa302004d} [9]: {Examine, Version=0.1.55.2941, Culture=neutral, PublicKeyToken=null} [10]: {HtmlAgilityPack, Version=1.4.6.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a} [11]: {ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73} [12]: {ImageProcessor, Version=1.8.7.0, Culture=neutral, PublicKeyToken=null} [13]: {ImageProcessor.Web, Version=3.2.1.0, Culture=neutral, PublicKeyToken=null} [14]: {interfaces, Version=1.0.5224.28286, Culture=neutral, PublicKeyToken=null} [15]: {log4net, Version=1.2.11.0, Culture=neutral, PublicKeyToken=null} [16]: {Lucene.Net, Version=2.9.4.1, Culture=neutral, PublicKeyToken=85089178b9ac3181} [17]: {Microsoft.ApplicationBlocks.Data, Version=1.0.1559.20655, Culture=neutral, PublicKeyToken=null} [18]: {Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [19]: {Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [20]: {Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [21]: {Microsoft.VisualStudio.Web.PageInspector.Loader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [22]: {Microsoft.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [23]: {Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [24]: {Microsoft.Web.Mvc.FixedDisplayModes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [25]: {MiniProfiler, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3} [26]: {mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [27]: {MySql.Data, Version=6.6.5.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d} [28]: {Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed} [29]: {SQLCE4Umbraco, Version=1.0.5224.29257, Culture=neutral, PublicKeyToken=null} [30]: {System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [31]: {System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [32]: {System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [33]: {System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [34]: {System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [35]: {System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [36]: {System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [37]: {System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91} [38]: {System.Data.SqlServerCe.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91} [39]: {System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [40]: {System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [41]: {System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [42]: {System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [43]: {System.Net.Http.Extensions, Version=2.2.15.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [44]: {System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [45]: {System.Net.Http.Primitives, Version=4.2.15.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [46]: {System.Net.Http.WebRequest, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [47]: {System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [48]: {System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [49]: {System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [50]: {System.ServiceModel.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [51]: {System.ServiceModel.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [52]: {System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [53]: {System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [54]: {System.Web.DynamicData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [55]: {System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [56]: {System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [57]: {System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [58]: {System.Web.Http.WebHost, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [59]: {System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [60]: {System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [61]: {System.Web.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a} [62]: {System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [63]: {System.Web.WebPages.Deployment, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [64]: {System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [65]: {System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [66]: {System.WorkflowServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35} [67]: {System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [68]: {System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089} [69]: {TidyNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null} [70]: {Txt, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null} [71]: {umbraco, Version=1.0.5224.29260, Culture=neutral, PublicKeyToken=null} [72]: {Umbraco.Core, Version=1.0.5224.29255, Culture=neutral, PublicKeyToken=null} [73]: {umbraco.DataLayer, Version=1.0.5224.29256, Culture=neutral, PublicKeyToken=null} [74]: {umbraco.editorControls, Version=1.0.5224.29262, Culture=neutral, PublicKeyToken=null} [75]: {umbraco.MacroEngines, Version=1.0.5224.29263, Culture=neutral, PublicKeyToken=null} [76]: {umbraco.providers, Version=1.0.5224.29259, Culture=neutral, PublicKeyToken=null} [77]: {Umbraco.Web.UI, Version=1.0.5224.29264, Culture=neutral, PublicKeyToken=null} [78]: {UmbracoExamine, Version=0.6.0.29259, Culture=neutral, PublicKeyToken=null} [79]: {UrlRewritingNet.UrlRewriter, Version=2.0.60829.1, Culture=neutral, PublicKeyToken=null}

Error: Server Error in '/' Application.

Sequence contains more than one matching element

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Sequence contains more than one matching element

Source Error:

Line 86: var visAsm = visiting.Pop(); Line 87: foreach (var refAsm in visAsm.GetReferencedAssemblies() Line 88: .Select(refAsmName => allAssemblies.SingleOrDefault(x => string.Equals(x.GetName().Name, refAsmName.Name, StringComparison.Ordinal))) Line 89: .Where(x => x != null && visited.Contains(x) == false)) Line 90: {

Source File: d:\Dev\Umbraco7\src\Umbraco.Core\AssemblyExtensions.cs Line: 88

Stack Trace:

[InvalidOperationException: Sequence contains more than one matching element] System.Linq.Enumerable.SingleOrDefault(IEnumerable1 source, Func2 predicate) +2533722 Umbraco.Core.<>c__DisplayClass5.b__0(AssemblyName refAsmName) in d:\Dev\Umbraco7\src\Umbraco.Core\AssemblyExtensions.cs:88 System.Linq.WhereSelectArrayIterator2.MoveNext() +66 System.Linq.WhereEnumerableIterator1.MoveNext() +152 Umbraco.Core.d__9.MoveNext() in d:\Dev\Umbraco7\src\Umbraco.Core\AssemblyExtensions.cs:87 System.Linq.WhereSelectEnumerableIterator2.MoveNext() +171 System.Linq.Enumerable.Contains(IEnumerable1 source, TSource value, IEqualityComparer1 comparer) +184 Umbraco.Core.TypeHelper.HasReferenceToAssemblyWithName(Assembly assembly, String expectedAssemblyName) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeHelper.cs:74 Umbraco.Core.<>c__DisplayClass1.<GetReferencedAssemblies>b__0(Assembly assembly) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeHelper.cs:54 System.Linq.WhereArrayIterator1.MoveNext() +49 System.Linq.Buffer1..ctor(IEnumerable1 source) +323 System.Linq.Enumerable.ToArray(IEnumerable1 source) +77 Umbraco.Core.TypeHelper.GetReferencedAssemblies(Type assignTypeFrom, IEnumerable1 assemblies) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeHelper.cs:52 Umbraco.Core.TypeFinder.GetClasses(Type assignTypeFrom, IEnumerable1 assemblies, Boolean onlyConcreteClasses, Func2 additionalFilter) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeFinder.cs:518 Umbraco.Core.TypeFinder.FindClassesOfType(IEnumerable1 assemblies, Boolean onlyConcreteClasses) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeFinder.cs:390 Umbraco.Core.TypeFinder.FindClassesOfType(IEnumerable1 assemblies) in d:\Dev\Umbraco7\src\Umbraco.Core\TypeFinder.cs:401 Umbraco.Core.<>c__DisplayClass411.<ResolveTypes>b__40() in d:\Dev\Umbraco7\src\Umbraco.Core\PluginManager.cs:777 Umbraco.Core.PluginManager.LoadViaScanningAndUpdateCacheFile(TypeList typeList, TypeResolutionKind resolutionKind, Func1 finder) in d:\Dev\Umbraco7\src\Umbraco.Core\PluginManager.cs:761 Umbraco.Core.PluginManager.ResolveTypes(Func1 finder, TypeResolutionKind resolutionType, Boolean cacheResult) in d:\Dev\Umbraco7\src\Umbraco.Core\PluginManager.cs:728 Umbraco.Core.PluginManager.ResolveTypes(Boolean cacheResult, IEnumerable1 specificAssemblies) in d:\Dev\Umbraco7\src\Umbraco.Core\PluginManager.cs:776 Umbraco.Core.PluginManager.ResolveApplicationStartupHandlers() in d:\Dev\Umbraco7\src\Umbraco.Core\PluginManager.cs:489 Umbraco.Core.CoreBootManager.InitializeApplicationEventsResolver() in d:\Dev\Umbraco7\src\Umbraco.Core\CoreBootManager.cs:172 Umbraco.Web.WebBootManager.InitializeApplicationEventsResolver() in d:\Dev\Umbraco7\src\Umbraco.Web\WebBootManager.cs:145 Umbraco.Core.CoreBootManager.Initialize() in d:\Dev\Umbraco7\src\Umbraco.Core\CoreBootManager.cs:86 Umbraco.Web.WebBootManager.Initialize() in d:\Dev\Umbraco7\src\Umbraco.Web\WebBootManager.cs:73 Umbraco.Core.UmbracoApplicationBase.StartApplication(Object sender, EventArgs e) in d:\Dev\Umbraco7\src\Umbraco.Core\UmbracoApplicationBase.cs:40 Umbraco.Core.UmbracoApplicationBase.Application_Start(Object sender, EventArgs e) in d:\Dev\Umbraco7\src\Umbraco.Core\UmbracoApplicationBase.cs:56

[HttpException (0x80004005): Sequence contains more than one matching element] System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9905705 System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +118 System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172 System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +336 System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

[HttpException (0x80004005): Sequence contains more than one matching element] System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9885060 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +101 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34009


Stephan 21 Apr 2014, 15:51:14

Damn. Forgot that the global list could contain two versions of the same assembly. Working on it.


Stephan 21 Apr 2014, 17:11:29

Would replacing line 88 by the following line fix the issue for you? .Select(refAsmName => allAssemblies.SingleOrDefault(x => string.Equals(x.GetName().FullName, refAsmName.FullName, StringComparison.Ordinal))) Works for me.


Shannon Deminick 22 Apr 2014, 00:21:22

I'd like to change the GetDeepReferencedAssemblies method to accept the list of 'all assemblies' rather than have a dependency on the TypeFinder, this also makes unit testing easier/possible.

How difficult would it be to write a unit test for this? I guess we'd need to find a situation with "So if assembly.1 uses an attribute defined in assembly.2 by inheriting from an attribute defined in assembly.3, we don't consider that assembly.1 references assembly.3 so we exclude it from searches" without having to create another test assembly.

Also, this is failing in medium trust in 6.2, will investigate.


Shannon Deminick 22 Apr 2014, 01:10:37

Also, we DO do a recursive search in the GetClasses method, perhaps the method that finds types with attributes (FindClassesWithAttribute) doesn't effectively perform this recursive search as is the case for xslt extensions

I am skeptical about this new change for performance reasons, I am going to investigate this all today.


Shannon Deminick 22 Apr 2014, 02:20:24

I've pushed the fixes for this in rev:

a88cb3f72458932f7015b23445c50c9a133f5d4d and 069ae9930a6d082a6cc22956cd784883da916148

This now properly performs the type finding in the FindClassesWithAttribute method with the recursive strategy used in GetClasses which has been optimized to not 'over search' which has helped speed up the TypeFinder immensely (those optimizations were made over a year ago)

I've tested this in latest 6 and 7 branches and the above 'foo:doIt()' examples are working using either the legacy or the new XsltExtension attribute.

Also, just in case you're curious the medium trust issue that was seen with the GetDeepReferencedAssemblies (now removed) is exactly this issue: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx/


Stephan 22 Apr 2014, 06:37:09

Looking into it... sounds great.


Stephan 22 Apr 2014, 07:52:41

OK with me.


Dan Evans 14 Jul 2014, 11:42:53

This problem has reappeared in 7.1.4. My App_Code extensions were working now I get an error when trying to save the XSLT file in the back office.

System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace 'urn:plan9.XSLTHelpers'.


Sebastiaan Janssen 14 Jul 2014, 12:42:52

@Dan.Evans Please create a new issue with some example code and steps to reproduce.


Priority: Normal

Type: Bug

State: Fixed

Assignee: Shannon Deminick

Difficulty: Normal

Category:

Backwards Compatible: True

Fix Submitted:

Affected versions: 7.0.0

Due in version: 6.2.0, 7.1.2

Sprint:

Story Points:

Cycle: