User Controls (asp.net, ascx-files) inside a C# class library, build error (aspnet_compiler.exe)

那年仲夏 提交于 2019-12-11 23:16:29

问题


Having trouble "passing" this error from ASPNET_Compiler.exe:

error CS0103: The name 'TestMath' does not exist in the current context.

The function called within TestMath from a user control (.cs) file, is outside of the context (scope/namespace/...). If I comment out the line that calls a method within TestMath the compilation works and so does my user control (referenced the DLL from within a WebApplication).

The "TestMath"-class is a static class which contains one static method "Get", which returns -1+3 (I have also tried creating it non-static... without luck).

The normal build in Visual Studio (MSBuild) works, but as we all know; it does not pack .ascx files into the DLL properly.

I've attached a .rar containing:

  1. TestLibrary

Contains 1 .ascx control and 1 math class

Contains a post-build event, that runs the "BuildAssembly.cmd", which in turn runs aspnet_compiler and aspnet_merge.

  1. WebSite - one .aspx page, to display the control from the library.

The line that gives the error is located inside Label.ascx.cs, line 18:

Lbl.Text += " MATH: " + TestMath.Get()

Any suggestions at all? Take a guess? Calling a method in a class outside of the .ascx.cs is prohibited? Oh... But there's something to those words! Which mean; if I copy the Math-class inside the User-Control class, like so:

public class Label : UserControl
{
    ..methods of label
    public class Math
    { 
      public static Get()
    }
}

The solution does compile and I am able to call methods within both classes... But, as we all want; I am no different: I want to also be able to call other objects (which I do already, they just live in GAC...)... so, hm... Basically; where does aspnet_compiler look for dll's...

Download example solution: http://s000.tinyupload.com/?file_id=76690332418132532036

If you un-pack the solution to C:\Solutions\Test, you can run it with little to no hassle, assuming Visual Studio 2010, Windows 7... .NET framwork 4 or higher.

Else, here's some code: ASCX

<%@ Control Language="C#" 
AutoEventWireup="true" 
ClassName="TestLibrary.Controls.Label"
CodeFile="Label.ascx.cs"
CodeBehind="Label.ascx.cs"
Inherits="TestLibrary.Controls.LabelCode" %>



Test

 <asp:Label runat="server" ID="Lbl"></asp:Label>

ASCX code behind

protected void Page_Load(object sender, EventArgs e)
    {
        Lbl.Text = "CodeBehindText...";
        Lbl.Text += " MATH: " + TestMath.Get();
    }

TestMath

 public static int Get()
    {
        return -1 + 3;
    }

BuildAssembly.cmd

SET compiler_path=%systemroot%\Microsoft.NET\Framework64\v4.0.30319
SET aspnet_merge=C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\aspnet_merge.exe
SET websiteproject=C:\Solutions\Test\TestLibrary\TestLibrary
SET compiled=C:\Solutions\Test\TestLibrary\TestLibrary\Compiled
SET outputfolder=C:\Solutions\Test\TestLibrary\TestLibrary\bin\Release
SET outputfilenamedll=TestLibrary.dll
%compiler_path%\aspnet_compiler.exe -p "%websiteproject%" -c -f -d -v / %compiled%
"%aspnet_merge%" %compiled% -o %outputfilenamedll%
COPY /Y %compiled%\bin\%outputfilenamedll% %outputfolder%\%outputfilenamedll%
RMDIR /s /q %compiled%

Wondering why all this "fuzz" with ASCX in 2015? Oh, hoped to reuse ascx files created several years ago, packing them in a library for eternity, signing the assembly, import it to my SharePoint-environment...


回答1:


After another 4 hours of trying and failing, this is the solution:

  1. Give your ascx control a classname (class name, attribute within .ascx file):

    Control Language="C#" AutoEventWireup="true" CodeFile="~/Control.ascx.cs" Inherits="Control" ClassName="UnqiueControlName [must be different than the real class name]" %>

  2. Copy all custom generated DLL's to the BIN folder of the class library.

    Or run cmd: gacutil /i

The route of registering custom dll's (for example: Common.dll) into gac, I have tried it, but it seem to be yet another time waster, due to apsnet_compiler does not seem to find assemblies within all gac folders (yes, there's more than just one location/folder). Let's not forget about the hell to update the GAC, MSIL, 32bit, 64bit... If you reference the DLL in a project, then it builds... later down the road you update the DLL, but forget to update GAC...and at runtime the application will use the DLL in GAC... Registering to GAC should only be through Setup/real installation...

  1. Compile your class library as you normally would in Visual Studio, which contains .ascx files.
  2. Change all .ascx files "CodeBehind" attribute to be "CodeFile" (cannot have both, which was one part of my error in the question)

  3. Compile your whole project through aspnet_compiler:

    aspnet_compiler.exe -v projectName -p "projectFolder (where csproj lives)" "outputFolder"

  4. Run aspnet_merge.exe on the "outputFolder" that aspnet_compiler created:

    aspnet_merge.exe "outputFolder" -o "nameOfDLLYouWant.dll"

  5. Now that you have a single dll, with .ascx files within it, we swap the CodeFile attribute of all ascx files back to CodeBehind, for intellisense and MSBuild to work properly.

  6. Create a new Web Application Project, delete everything in it, add a reference to your newly created DLL and add a new Default.aspx.

  7. Register the assembly and namesapce on your .aspx site (or in web.conf): <%@ Register Assembly="UserControl" NameSpace="NameSpaceOfWhereYourControlLives" TagPrefix="uc">

  8. Add your between body tags, remember to use the name of the ClassName-attribute defined in the library (first line in .ascx file)

Or a simple cmd file that "does it all" (copies needed DLLs to the Bin, changing back and forth between CodeBehind, CodeFile, Compiling and merging the dll into a new dll):

@ECHO ON
setlocal enabledelayedexpansion

::--------------------------------------------------------------------------------------------------------
::--------------------------------------------------------------------------------------------------------
::VARIABLES
::--------------------------------------------------------------------------------------------------------
::--------------------------------------------------------------------------------------------------------
SET project=C:\Solutions\UserControlLibrary
REM Notice that project variable above ends with project name below!
SET projectname=UserControlLibrary
SET projectcompiledfolder=C:\Temp\UserControlLibraryCompiled
SET outputdll=UserControlLibrary.dll
REM Configuration is either release or debug, depending on which "mode" you are building against in Visual studio.
SET projectconfiguration=Release

::--------------------------------------------------------------------------------------------------------
::--------------------------------------------------------------------------------------------------------
::LOGIC
::--------------------------------------------------------------------------------------------------------
::--------------------------------------------------------------------------------------------------------
call:ReplaceFunction %project% "CodeBehind", "CodeFile"

call:CopyLibraryReference Common, Release, %project%

call:ProjectCompile %projectname%, %project%, %projectcompiledfolder%

call:ProjectMerge %projectcompiledfolder%, %outputdll%, %project%, %projectconfiguration%

call:ReplaceFunction %project% "CodeFile", "CodeBehind"

RMDIR /s /q %projectcompiledfolder%

exit


::---------------------------------------------------------------------------`-----------------------------`
::---------------------------------------------------------------------------`-----------------------------`
::FUNCTIONS
::---------------------------------------------------------------------------`-----------------------------`
::---------------------------------------------------------------------------`-----------------------------`
:CopyLibraryReference <referenceName> <referenceConfiguration> <project>
SET referenceName=%~1
SET referenceConfiguration=%~2
SET project=%~3
REM Solution folder, containing a bunch of Library projects, such as "Common", "ActiveDirectory", "BusinuessLogic" and our UserControlLibrary...
SET lib=C:\Solutions\
COPY /Y "%lib%%referenceName%\bin\%referenceConfiguration%\%referenceName%.dll" "%project%\bin\%referenceName%.dll" 
goto:eof

:ProjectCompile <projectname> <project> <projectcompiledfolder>
SET projectname=%~1
SET project=%~2
SET projectcompiledfolder=%~3
REM Path to compiler might vary, also if you want 32 or 64 bit... this might need to change
SET compiler=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_compiler.exe
"%compiler%" -v %projectname% -p "%project%" "%projectcompiledfolder%"
goto:eof


:ProjectMerge <projectcompiledfolder> <outputdll> <project> <projectconfiguration>
SET projectcompiledfolder=%~1
SET outputdll=%~2
SET project=%~3
SET projectconfiguration=%~4
REM Path to merger might vary, also if you want 32 or 64 bit... this might need to change
SET merger=C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\aspnet_merge.exe
"%merger%" "%projectcompiledfolder%" -o %outputdll%
COPY /Y "%projectcompiledfolder%\bin\%outputdll%" "%project%\bin\%projectconfiguration%\%outputdll%"
goto:eof

:ReplaceFunction <projectFolder> <replaceValue> <replaceWith>
set projectFolder=%~1
REM tempfile name can be anything...
set tempfile=%~1\replaceascxbuildv1.txt
set replaceValue=%~2
set replaceWith=%~3
for /f %%f in ('dir /b /s %projectFolder%\*.ascx') do ( 
    for /f "tokens=1,* delims=¶" %%A in ( '"type %%f"') do (
        SET string=%%A
        SET modified=!string:%replaceValue%=%replaceWith%!
        echo !modified! >> %tempfile%
    )
    del %%f
    move %tempfile% %%f
)
goto:eof

Add this into a ".cmd" file, then run the cmd file upon Post Build Event. The build is slower than usual (of course), so you do not have to be me, to understand that if you have too many user controls, this adds up. Feel free to create several projects then. :P

Conclusion: Now I simply hit build in Visual Studio, and everything is automatic, I can finally write some code! If you ever use this yourself, all you need is to figure out your own paths to the files and project of yours, but do note: a path or two is written within the function themselves. ;)

Edit: The list actually starts at 0, but I do see there's two 2's... bug!



来源:https://stackoverflow.com/questions/30508846/user-controls-asp-net-ascx-files-inside-a-c-sharp-class-library-build-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!