RoleEntryPoint discovery
-
Wednesday, May 19, 2010 9:06 AM
Hi all,
I'm new to Azure, so please excuse me if this has already been answered, I've tried a few searches and can't find anything related
Our company has a web site application that is both shipped in a MSI as a "self install / self host" to our customers and which we also host ourselves. We want migrate our own installation of the application to Azure, but we need to keep the ability to ship the MSI to our customers.
I am fully aware that there may be azure specific changes that need to be made to the app, but am confident that I can use IoC to factor out Azure and SelfHost changes to separate assemblies and inject the environment specific behaviour at runtime. (I will have 3 DLLs: one "behaviour" interface DLL that the web app has a direct dependency on; one SelfHost DLL that contains the self host implementations of the behaviours and one Azure DLL that contains the azure behavior implementations - the correct behavior implementation DLL will be injected at startup)
For the most part the app appears to run in the dev fabric with no changes - I have not added a RoleEntryPoint class yet, but I do want to add one. It cannot be within our website as that would straight away put a dependency on the azure DLLs. I added it to the Azure specific DLL, but the dev fabric does not see the implementation within that DLL.
So the question is, how is the RoleEntryPoint class discovered? I had thought it may be that the fabric would just reflect over all DLLs and search for the class, but that does not appear to be the case. I did find a file called "__entrypoint.txt" within the bin\debug tree of my CloudService project which contains the assembly name of my compiled web application, how is the creation of that file controlled (or is it even relevant to my question)?
Is there anyway to achieve what I am trying to do? Will I have to butcher the CloudService.targets file to achieve this?
Thanks in advance and apologies if this has already been answered
Pete
- Moved by Brian AurichMicrosoft Employee Thursday, March 24, 2011 7:54 PM Moving to active forum. (From:Windows Azure - Archive)
All Replies
-
Wednesday, May 19, 2010 12:20 PM
After delving into the internals of CloudServices.targets, I believe I can answer my own question. It seems that the assembly that contains the RoleEntryPoint class is indeed the one that is referenced in the __entrypoint.txt. However, it seems that within cloudservice.targets, that this EntryPoint is _forced_ to be the DLL that is the primary output assembly of the web application project. There is no way to easily override this behaviour and give a different EntryPoint (yes, with WorkerRoles, you _do_ seem to be able to add <EntryPoint> item metadata within the ccproj file, but for WebRole projects, this is not honoured, and the EntryPoint is reset to the primary output.
However, I was able to inject a msbuild target into the cloudservice.targets process that will override the EntryPoint that is set by "ResolveWebRoleReferences" target. I added the following to my .ccproj file, right after the import of CloudServices.targets line:
<PropertyGroup> <ResolveRoleReferencesDependsOn> $(ResolveRoleReferencesDependsOn); OverrideEntryPoint; </ResolveRoleReferencesDependsOn> </PropertyGroup> <Target Name="OverrideEntryPoint"> <ItemGroup> <CopyOfWebRoleReferences Include="@(WebRoleReferences)" /> <WebRoleReferences Remove="@(WebRoleReferences)" /> <WebRoleReferences Include="@(CopyOfWebRoleReferences)" Condition=" '%(Name)' == 'MyWebApplication' "> <EntryPoint>MyWebApplication.AzureSpecific.dll</EntryPoint> </WebRoleReferences> <WebRoleReferences Include="@(CopyOfWebRoleReferences)" Condition=" '%(Name)' != 'MyWebApplication' ">
<EntryPoint>%(EntryPoint)</EntryPoint> </WebRoleReferences> </ItemGroup> </Target>When I start my web app in the dev fabric, the RoleEntryPoint class that lives within MyWebApplication.AzureSpecific.dll is executed, and an entry point class is NOT needed within MyWebApplication.
Cheers
Pete
- Marked As Answer by Peter McEvoy Wednesday, May 19, 2010 12:21 PM
-
Wednesday, May 19, 2010 12:22 PM
(it seems that a <br/> was added by the code editor above - so please remove it if you use my snippet)
Pete
-
Wednesday, May 19, 2010 1:59 PM
Hi Peter,
A RoleEntryPoint derived class is not necessary for a Web Role. This should allow you to keep you Web project completely Azure agnostic. You can place all your initialization code in Application_Start and inject Azure specific behaviors when hosted in Azure.
-
Wednesday, May 19, 2010 3:08 PM
Hi Fernando,
I agree - RoleEntryPoint is optional for web apps and so is not entirely necessary for my web project. However, I do want to solve the problem as I believe I am going to need WorkerRoles as well for other parts of the system (for example we have backend internal webserivces applications that will want to convert to workerroles), but I guess you weren't to know that.
The problem is not entirely limited to making sure the EntryPoint metadata is set, but also that I build the project that contains the RoleEntryPoint, without a direct project reference from MyWebApplication to the AzureSpecific project - so I need to add another target that will build and merge the WebApp and AzureSpecific together.
I'll post my updated targets here later just in case anyone else has the same issue
Pete
-
Thursday, May 20, 2010 8:59 AM
Just to close this item out, please find below my custom Cloud.targets file. To use it, you need to import it into your .ccproj right after the import of Microsoft.CloutService.targets:
<Import Project="$(MSBuildProjectDirectory)\..\..\tools\inc\MSBuild\Cloud.targets" />
You are then need to add an additional metadata element to any of your Web role ProjectReferences that appear in your .ccproj file. So:
<ProjectReference Include="..\MyWebApp\MyWebApp.csproj"> <Name>MyWebApp</Name> <Project>{05b2e540-52f6-4dea-870d-ca005023e807}</Project> <Private>True</Private> <RoleType>Web</RoleType> <RoleName>MyWebApp</RoleName> </ProjectReference>Becomes:
<ProjectReference Include="..\MyWebApp\MyWebApp.csproj"> <Name>MyWebApp</Name> <Project>{05b2e540-52f6-4dea-870d-ca005023e807}</Project> <Private>True</Private> <RoleType>Web</RoleType> <RoleName>MyWebApp</RoleName> <!--customization--> <_EntryPointProject>..\MyWebApp.Azure\MyWebApp.Azure.csproj</_EntryPointProject> <!--END customization--> </ProjectReference>Then, when the ccproj is built, it will build the web application project and the azure specific project, merge them together and update the EntryPoint to be the assembly name output of the EntryPoint project.
At the moment, this only works for Web roles, but it should be trivial to get it to support Worker roles as well (which I will be doing in a few weeks, so if you want to seem my updates when I do them, then just bump this thread.
Here is the Cloud.Targets that I have written:
<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <PropertyGroup> <CorePackageComputeServiceDependsOn> $(CorePackageComputeServiceDependsOn); OverrideEntryPoint; BuildEntryPointAssembly; </CorePackageComputeServiceDependsOn> </PropertyGroup> <!-- Note, these targets use item batching to call the targets foreach item in an item group In the first case, we want to execute OverrideEntryPoint foreach item in the ProjectReference item group In the second case, we want to execute over each item in the RoleReference group and cause the EntryPointProjects to be built See http://sstjean.blogspot.com/2006/09/how-to-get-msbuild-to-run-complete.html for explanation how this is achieved --> <Target Name="OverrideEntryPoint" Outputs="%(ProjectReference.Identity)"> <PropertyGroup> <ProjectReferenceName>%(ProjectReference.Name)</ProjectReferenceName> <EntryPointProject>%(ProjectReference._EntryPointProject)</EntryPointProject> </PropertyGroup> <!--get the Assembly name of the EntryPointProject--> <MSBuild Projects="$(EntryPointProject)" Targets="GetTargetPath" Properties="Configuration=$(Configuration); Platform=$(Platform)" ContinueOnError="false" Condition=" '$(EntryPointProject)' != '' "> <Output TaskParameter="TargetOutputs" ItemName="EntryPointProjectTargetPath" /> </MSBuild> <PropertyGroup> <EntryPointAssembly>%(EntryPointProjectTargetPath.Filename)%(EntryPointProjectTargetPath.Extension)</EntryPointAssembly> </PropertyGroup> <!-- Now, some itemgroup trickery - we'll re-add all the entries to RoleReference itemgroup all the while checking if we want to re-write the EntryPoint metadata --> <ItemGroup> <!--Make sure that the CopyOf itemgroup is empty - could be items in it from previous iteration of this target--> <CopyOfRoleReferences Remove="@(CopyOfRoleReferences)" /> <!--Make a backup of the rolereferences into the CopyOf itemgroup--> <CopyOfRoleReferences Include="@(RoleReferences)" /> <!--Empty the RoleReferences group - we'll repopulate it--> <RoleReferences Remove="@(RoleReferences)" /> <!--iterate over the CopyOf group, and if the Name is the same as the ProjectReference that we are currently processing, then replace the EntryPoint. We'll also keep track of the EntryPointProject path, cos we'll build it in a later target--> <RoleReferences Include="@(CopyOfRoleReferences)" Condition=" '%(Name)' == '$(ProjectReferenceName)' And $(EntryPointAssembly) != '' "> <EntryPoint>$(EntryPointAssembly)</EntryPoint> <_EntryPointProject>$(EntryPointProject)</_EntryPointProject> </RoleReferences> <!--Otherwise copy the item verbatim--> <RoleReferences Include="@(CopyOfRoleReferences)" Condition=" '%(Name)' != '$(ProjectReferenceName)' " /> </ItemGroup> </Target> <Target Name="BuildEntryPointAssembly" Outputs="%(RoleReferences.Identity)"> <PropertyGroup> <EntryPointProject>%(RoleReferences._EntryPointProject)</EntryPointProject> <RoleReferenceOutputDir>%(RoleReferences.OutputDir)</RoleReferenceOutputDir> <RoleType>%(RoleReferences.RoleType)</RoleType> </PropertyGroup> <!-- Build the EntryPoint project - use the default OutputDir and build in place - we'll want to copy the DLLs to the Role out dir after - this _should_ work on TeamBuild cos we are not passing in a new OutDir, so we should not end up copying the entire Binaries folder :-S --> <MSBuild Condition=" '$(EntryPointProject)' != '' " Projects="$(EntryPointProject)" Targets="Build" Properties="Configuration=$(Configuration); Platform=$(Platform); UseHostCompilerIfAvailable=false" ContinueOnError="false" /> <!-- We need to copy all the assemblies that were built as part of building the entry point project. Simplest thing to do is copy the contents of it's OutDir. We built it in place so it should just be the bin\Debug dir. But we'll ask the project where it actually is just in case. (If it's NOT bin\Debug under the project, then we'll need to add more complicated logic to figure out the CopyLocal referenced assemblies etc. So good luck with that. --> <!--Get the TargetPath - we'll use that to determine the folder to copy all assemblies from--> <MSBuild Condition=" '$(EntryPointProject)' != '' " Projects="$(EntryPointProject)" Targets="GetTargetPath" Properties="Configuration=$(Configuration); Platform=$(Platform)" ContinueOnError="false"> <Output TaskParameter="TargetOutputs" ItemName="EntryPointProjectTargetPath" /> </MSBuild> <PropertyGroup> <EntryPointProjectOutputDir>%(EntryPointProjectTargetPath.RootDir)%(EntryPointProjectTargetPath.Directory)</EntryPointProjectOutputDir> </PropertyGroup> <!--We need to copy the contents of the EntryPoint project bin folder--> <ItemGroup> <EntryPointProjectOutputs Include="$(EntryPointProjectOutputDir)**\*.*" /> </ItemGroup> <Message Text="EntryPointAssembly files: @(EntryPointProjectOutputs)" /> <!-- Merge the entrypoint DLLs into the bin dir of the RoleReference out dir. At the moment, we only support web projects. But a simple addition could be done to support Worker roles. (I'll add that when I need it) --> <!--Copy the EntryPoint assembly and dlls to the web bin folder for Web roles--> <Copy SourceFiles="@(EntryPointProjectOutputs)" DestinationFiles="$(roleReferenceOutputDir)\bin\%(RecursiveDir)%(Filename)%(Extension)" Condition=" '$(RoleType)' == 'Web' " /> <!-- Register EntryPoint Assembly binaries for Clean --> <ItemGroup Condition=" '$(RoleType)' == 'Web' And '@(EntryPointProjectOutputs)' != '' "> <FileWrites Include="@(EntryPointProjectOutputs -> '$(RoleReferenceOutputDir)\bin\%(RecursiveDir)%(Filename)%(Extension)' )" /> </ItemGroup> </Target> </Project>
- Marked As Answer by Peter McEvoy Thursday, May 20, 2010 9:00 AM
-
Wednesday, September 07, 2011 1:38 PM
For the record, I could not get this approach to work (errors in the .targets fie that I could not resolve). However, I discovered an alternative using the cspack utility to create the azure package. It allows you to specify the name of the dll that contains the RoleEntryPoint class (among other extended functionality not present in the VS IDE azure project type).
Admittedly, the cspack approach does not allow for the easy debugging of the package like a VS-integrated approach, but if you know your RoleEntryPoint functions work, then it's a simple alternative.
There are certainly situations where RoleEntryPoint is required vs. Application_Start. Application_Start will not fire until a request is made to the WebRole. In our scenario, we need to do a bunch of things to "prime" the web server before it's ready to begin servicing requests. This can keep the server busy for a few minutes, so RoleEntryPoint/OnStart() is really the only way to go, or an Azure startup task.
-
Wednesday, September 07, 2011 2:03 PM
Hi CAelevy,
I'm not surprised this didn't work - it was a heavy handed way of specifying the RoleEntrypoint DLL in the old Azure SDK 1.1. When I moved our stuff to SDK 1.2, and then 1.3 I had to undo a lot of the overrides that my above edits had done. My policy now is to override as little as possible in the cloud.targets or else I'll end up suffering at a later date...
With that in mind, I still wanted to be able to specify a different DLL that the RoleEntryPoint lives in. So the simplest thing that works is to add this to my cloud.ccproj:
<!-- Built-in Cloud.targets assumes that RoleEntryPoint class is in the same project as WebRole but in our case, this is not true. We have our RoleEntryPoint class in a single common DLL. We'll override EntryPoint metadata that was set in ResolveWebRoleReferences with the $(EntryPointDll) property --> <Target Name="OverrideRoleEntryPoint" AfterTargets="ResolveWebRoleReferences"> <ItemGroup> <WebRoleReferences> <EntryPoint>$(EntryPointDll)</EntryPoint> </WebRoleReferences> </ItemGroup> </Target>
Where $(EntryPointDll) is actually the name of the DLL that contains the RoleEntryPoint.With this very simple customization, the default MS Cloud.targets work and use the declared entrypoint DLL.
HTH
Pete
-
Wednesday, September 07, 2011 3:58 PMYes, this is a much more elegant solution. Thanks!
-
Monday, September 19, 2011 2:22 AM
SDK 1.5 now exposes the entry point in the service model.
<ServiceDefinition name="MyService" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="MyRole" vmsize="Small"> ... <Runtime executionContext="limited"> <EntryPoint> <NetFxEntryPoint assemblyName="myassembly.dll" targetFrameworkVersion="4.0" /> </EntryPoint> </Runtime> </WebRole> </ServiceDefinition>
-
Thursday, September 22, 2011 11:33 AM
Fernando, are you sure? I can't find any logic in the 1.5 Microsoft.WindowsAzure.targets that would indicate that what you say is correct...
-
Thursday, September 22, 2011 9:13 PM
Hi Peter,
Reasonably sure. :-)
If you configure the entry point in the service model, when you build the service package, it will set the __entrypoint.txt file to the value that you specify for assemblyName. You should be able to use this for the scenario that you described at the start of this thread. That is, having a regular Web application with no references to the Azure libraries, and creating a separate assembly with the role entry point that you only deploy when hosting the site in Azure.
-
Wednesday, January 04, 2012 5:30 PM
TargetFrameworkVersion must be "v4.0" and not "4.0". Otherwise deployment hangs in a loop. In general I confirm this is working as of SDK 1.6SDK 1.5 now exposes the entry point in the service model.
<ServiceDefinition name="MyService" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="MyRole" vmsize="Small"> ... <Runtime executionContext="limited"> <EntryPoint> <NetFxEntryPoint assemblyName="myassembly.dll" targetFrameworkVersion="4.0" /> </EntryPoint> </Runtime> </WebRole> </ServiceDefinition>
-
Wednesday, July 04, 2012 9:57 AM
An update on Peter's solution for Windows Azure SDK 1.7. The targets he was using have changed slightly. The ResolveWebRoleReferences target no longer exists. Instead you can use a BeforeTargets for the overall ResolveRoleReferences.
<!-- Built-in Cloud.targets assumes that RoleEntryPoint class is in the same project as WebRole but in our case, this is not true. We have our RoleEntryPoint class in a single common DLL. We'll override EntryPoint metadata that was set in ResolveWebRoleReferences with the $(EntryPointDll) property --> <Target Name="OverrideRoleEntryPoint" BeforeTargets="ResolveRoleReferences"> <ItemGroup> <WebRoleReferences> <EntryPoint>$(EntryPointDll)</EntryPoint> </WebRoleReferences> </ItemGroup> </Target> </Project>
- Edited by Siobhan Connell Wednesday, July 04, 2012 9:58 AM Typo

