Элементы управления пользователя (asp.net, ascx-файлы) внутри библиотеки классов C#, ошибка сборки (aspnet_compiler.exe)

Возникли проблемы с "передачей" этой ошибки из ASPNET_Compiler.exe:

Ошибка CS0103: имя "TestMath" не существует в текущем контексте.

Функция, вызываемая в TestMath из файла пользовательского элемента управления (.cs), находится вне контекста (scope/namespace/...). Если я закомментирую строку, которая вызывает метод в TestMath, компиляция будет работать, как и мой пользовательский элемент управления (ссылка на DLL из WebApplication).

Класс "TestMath"- это статический класс, который содержит один статический метод "Get", который возвращает -1+3 (я также пытался создать его нестатично... без удачи).

Обычная сборка в Visual Studio (MSBuild) работает, но, как мы все знаем; он не упаковывает файлы.ascx в DLL должным образом.

Я приложил.rar, содержащий:

  1. TestLibrary

Содержит 1 элемент управления.ascx и 1 класс по математике

Содержит событие после сборки, которое запускает "BuildAssembly.cmd", которое, в свою очередь, запускает aspnet_compiler и aspnet_merge.

  1. WebSite - одна страница.aspx, для отображения элемента управления из библиотеки.

Строка, сообщающая об ошибке, находится внутри Label.ascx.cs, строка 18:

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

Есть предложения? Предположить? Вызов метода в классе вне.ascx.cs запрещен? Ох... Но есть что-то в этих словах! Что означает; если я скопирую Math-класс внутри класса User-Control, вот так:

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

Решение компилируется, и я могу вызывать методы в обоих классах... Но, как мы все хотим; Я ничем не отличается: я хочу также иметь возможность вызывать другие объекты (что я уже делаю, они просто живут в GAC...)... так, хм... В основном; где aspnet_compiler ищет DLL...

Загрузить пример решения: http://s000.tinyupload.com/?file_id=76690332418132532036

Если вы распакуете решение в C:\Solutions\Test, вы можете запустить его практически без хлопот, если предположить Visual Studio 2010, Windows 7... .NET framwork 4 или выше.

Иначе вот код: 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 позади

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%

Хотите знать, почему все это "fuzz" с ASCX в 2015 году? О, надеялся повторно использовать файлы ascx, созданные несколько лет назад, упаковывать их в библиотеку вечно, подписывать сборку, импортировать ее в мою среду SharePoint...

1 ответ

Решение

После еще 4 часов попыток и неудач это решение:

  1. Дайте вашему ascx-элементу управления имя класса (имя класса, атрибут в файле.ascx):

    Control Language = "C#" AutoEventWireup = "true" CodeFile = "~ / Control.ascx.cs" Inherits = "Control" ClassName = "UnqiueControlName [должно отличаться от реального имени класса]" %>

  2. Скопируйте все пользовательские сгенерированные библиотеки DLL в папку BIN библиотеки классов.

    Или запустите cmd: gacutil /i

Маршрут регистрации пользовательских dll (например, Common.dll) в gac, я пробовал, но, похоже, это еще одна трата времени, потому что apsnet_compiler не находит сборки во всех папках gac (да, есть еще чем только одно местоположение / папка). Давайте не будем забывать об аде, чтобы обновлять GAC, MSIL, 32-битные, 64-битные... Если вы ссылаетесь на DLL в проекте, то он собирается... позже в будущем вы обновите DLL, но забудете обновить GAC...и во время выполнения приложение будет использовать DLL в GAC... Регистрация в GAC должна быть только через установку / реальную установку...

  1. Скомпилируйте свою библиотеку классов, как обычно в Visual Studio, которая содержит файлы.ascx.
  2. Измените атрибут "Code Behind" всех файлов.ascx на "CodeFile" (не может иметь оба, что было одной из моих ошибок в вопросе)

  3. Скомпилируйте весь ваш проект через aspnet_compiler:

    aspnet_compiler.exe -v имя_проекта -p "projectFolder (где живет csproj)" "outputFolder"

  4. Запустите aspnet_merge.exe для "outputFolder", созданного aspnet_compiler:

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

  5. Теперь, когда у вас есть одна dll с файлами.ascx, мы поменяем атрибут CodeFile всех файлов ascx обратно на Code Behind, чтобы intellisense и MSBuild работали правильно.

  6. Создайте новый проект веб-приложения, удалите все в нем, добавьте ссылку на вновь созданную DLL и добавьте новый Default.aspx.

  7. Зарегистрируйте сборку и namesapce на своем сайте.aspx (или в web.conf):<% @ Register Assembly = "UserControl" NameSpace = "NameSpaceOfWhereYourControlLives" TagPrefix = "uc">

  8. Добавьте ваши теги между телами, не забудьте использовать имя атрибута ClassName, определенного в библиотеке (первая строка в файле.ascx)

Или простой cmd-файл, который "делает все" (копирует необходимые библиотеки DLL в корзину, переключаясь между Code Behind, CodeFile, компилируя и объединяя dll в новую 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

Добавьте это в файл ".cmd", затем запустите файл cmd после события Post Build. Сборка идет медленнее, чем обычно (конечно), поэтому вам не обязательно быть мной, чтобы понять, что если у вас слишком много пользовательских элементов управления, это складывается. Не стесняйтесь создавать несколько проектов тогда.:П

Вывод: теперь я просто запускаю сборку в Visual Studio, и все происходит автоматически, я наконец-то могу написать некоторый код! Если вы когда-либо используете это самостоятельно, все, что вам нужно, это выяснить ваши собственные пути к файлам и вашему проекту, но учтите: один или два пути записываются внутри самой функции.;)

Редактировать: список на самом деле начинается с 0, но я вижу, что есть два 2... ошибка!

Другие вопросы по тегам