Automatic versioning of web site projects in Team City - continuous-integration

At my current client we have some legacy ASP.Net web site projects. I am in the process of introducing automatic versioning for our builds and was wondering of how to best do this with web site projects in Team City?
I am currently using Team City's %build.number% variable (set through project build template) as the authoritative version number for a build. For any .NET project that produces assemblies it's hassle-free to use "AssemblyInfo Patcher" Build Feature in Team City but this does not work for web site projects since they do not produce assemblies.
So, any suggestions? I am already using Powershell and psake in my builds so creating scripts that use %build.number% is not a problem, it is more a question of how to inject this into the web site project in a "nice" manner.

I attempted several solutions but ended up with using the version number of a dependent assembly that gets set by Team City during the build. I added a class to the assembly and it looks something like this:
public class VersionUtils
{
private readonly ILog _logger;
public VersionUtils()
{
_logger = LogManager.GetLogger("VersionLogger");
}
public string GetWebAppVerison()
{
string version = "unknown version";
var assembly = Assembly.GetAssembly(typeof(VersionUtils));
try
{
version = assembly.GetName().Version.ToString();
}
catch(Exception ex)
{
_logger.Warn("Could not find or read version number from " + assembly.GetName(), ex);
}
return version;
}

Related

Unbound breakpoints when debugging in Blazor Webassembly when using certain attributes/classes

I'm developing a modular blazor application (5.0.2) using VS 2019 (16.8.4), which is structured as follows:
a "Main" Solution, which consists of
RCL
Wasm project to startup the application
several "Sub" solutions which reference the Main RCL (Base components, etc) which consist of
.net5 libraries (Models, Web-service access, etc)
RCL with components, referencing the .net5 libraries (via project reference)
All projects have a post-build event to copy the DLL and PDB files to a certain path, e.g. D:\TMP.
The SubSolution references the MainRCL library via this path.
The Main Wasm project references the SubRCL library also via this path (for adding services at startup/Program.cs).
The MainRCL does not have a reference to SubRCL (components are rendered via reflection/BuildRenderTree() according to configurable UI definition).
Debugging the Main Solution worked perfectly (IIS Express/Application Debugging).
Then I tried to debug the SubModules -> I started debugging from the MainSolution and opened files from the SubModules projects in this VS instance.
At some libraries, debugging was working, but not for the SubRCL ("Unbound Breakpoint"). Then I was able to reproduce the (very strange) issue with sample solutions:
The "MainRCL" provides 2 Attributes:
[AttributeUsage(AttributeTargets.Class)]
public sealed class TestNoEnumAttribute : Attribute
{
public string Name { get; set; }
public string Mode { get; set; }
public TestNoEnumAttribute(string name, string mode)
{
Name = name;
Mode = mode;
}
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class TestEnumAttribute : Attribute
{
public string Name { get; set; }
public EventExecutionMode Mode { get; set; }
public TestEnumAttribute(string name, EventExecutionMode mode)
{
Name = name;
Mode = mode;
}
}
public enum EventExecutionMode
{
AutomaticAll = 0,
ManualConfiguration = 2
}
The SubRCL uses these attributes at a test-method:
[TestNoEnum("Test", "EventExecutionMode.ManualConfiguration")]
//[TestEnum("Test", EventExecutionMode.ManualConfiguration)]
public class Module1Test
{
public int IncreaseNum(int num)
{
var x = new Part1();
var part1Num = x.DoStuff(num);
var newNum = part1Num + 1;
return newNum;
}
}
The class "Part1()" which is called, is located at another library of the SubSolution
The breakpoint at the "DoStuff()" method in Part1 class is always hit (in separate .net5 library).
The breakpoint at the "IncreaseNum()" method is only called when the [TestEnum] attribute is NOT used.
As soon as the [TestEnum] attribute is used, there is an "Unbound Breapoint"; the breakpoint in "DoStuff()" method in another library is still hit.
Then I tried to "add existing project" to SubSolution and added the MainWasm project and started debugging directly from SubSolution -> same behavior.
Is there anything I oversee (e.g. regarding DLL-references or PDB file copy)?
This is already my second approach of trying to debug these modular-structured solutions - first I tried to debug via IIS (How to debug Blazor Webassembly on IIS with VS by attaching to Chrome?), but this was also not successful.
Found out there is an issue with debugging when using attribues with enum parameters:
https://github.com/dotnet/aspnetcore/issues/25380
-> I replaced the enum parameters and debugging is working fine now - Didn't get any feedback when this will be fixed so far
I had the same issue with my Blazor WASM not able to be debugged in VS due to 'Unbound breakpoint'. I have multiple projects running under the same solution and while initially the debugging worked for the WASM, it stopped after a while.
Eventually I was able to find a work around by waiting until all projects loaded and then I could disable the 'Unbound' breakpoint and re-select it. It then worked as expected.
It's not an ideal solution (especially if you have multiple breakpoints while troubleshooting) but it is workable.
I had this problem in .NET 6 and Visual Studio 2022.
I made a codebehind-file component.razor.cs but I also had code in the razor-file itself. Moving the code to the codebehind-file solved the issue and enabled the breakpoints.

SSRS reports with .Net Core 3.1 MVC application

I am trying to display the SSRS report in a .Net Core 3.1 MVC application.
I tried to implement the approach mentioned in
https://alanjuden.com/2016/11/10/mvc-net-core-report-viewer/?unapproved=58532&moderation-hash=321d5350c96d2fcf83baa4c939bbdf53#comment-58532
public class ReportsController : AlanJuden.MvcReportViewer.ReportController
{
protected override ICredentials NetworkCredentials
{
get
{
//Custom Domain authentication (be sure to pull the info from a config file)
return new System.Net.NetworkCredential("username", "password");
//Default domain credentials (windows authentication)
//return System.Net.CredentialCache.DefaultNetworkCredentials;
}
}
protected override string ReportServerUrl
{
get
{
//You don't want to put the full API path here, just the path to the report server's ReportServer directory that it creates (you should be able to access this path from your browser:
return "https://YourReportServerUrl.com/ReportServer/ReportExecution2005.asmx";
}
}
public IActionResult ProcessReport()
{
var model = this.GetReportViewerModel(Request);
model.ReportPath = "reportPath";
return RedirectToAction("ReportViewer", model);
}}
but it is not working with the latest framework.
I am getting following error while running the project - Error screenshot
Any help is appreciated.
Thanks!
The same thing happened to me, in my case I needed to install the same package that tells you to install
Install-Package System.ServiceModel.Http -Version 4.1.0
or in the nuget look for the package System.ServiceModel.Http
I tried different workarounds with latest .NET Core including the one you mentioned from Alan Juden. However the easiest thing that worked for me is to create a plain .NET WebForms site using the Report Viewer control from Microsoft. It was still a lot of code but this is solid because the Report Viewer control has been around for many years.
In my case it is showing SSRS Report from Angular UI, but the same will work with MVC or any other Web UI because you will actually redirect/navigate to another url (WebForms aspx page).
More details here.

How can I deploy a Blazor server-hosted application from Visual Studio 2019

I am using VS2019 Preview.
I have created a "server-hosted" Blazor application using the latest Blazor extension (16.0.19227). This is the variant that contains 3 separate projects...
MyApp.Client
MyApp.Server
MyApp.Shared
I can debug this by making MyApp.Server the active project and all works fine but I'm struggling to publish/deploy this to Azure. I have tried the following...
Right-click on MyApp.Server in Solution-Explorer
Choose "Publish"
Go through the wizard to create a new publish profile
Change the deployment mode to "self-contained"
Hit publish
At this point I get an error during deployment...
CSC(0,0): Error CS0006: Metadata file 'D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\win-x86\MyApp.Client.dll'
could not be found
This appears to be because the "Target Runtime" in the web-deploy profile is set to win-x86. The client application is actually being built as
"D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\MyApp.Client.dll"
(without the additional win-x86 subfolder) so the deployment process seems to be making an incorrect assumption about the paths used by the build process. There's no way in the publish dialog to specify a blank/don't care target runtime.
Is there a workaround for this or perhaps I am using the wrong approach for deployment?
There is some official documentation but it's not very helpful.
Update It seems that the deployment is using the output path of the Client project and then just appending netstandard2.0{Target Runtime} to it so changing the output path in the Client project is not enough to work around the issue.
Update 2 Removing the RuntimeIdentifier tag in the publish profile by editing the xml simply results in deploy-time error stating that an empty RuntimeIdentifier is incompatible with a self-contained deployment. Unfortunately the self-contained deployment is necessary because Azure does not yet host .net core 3 directly.
because Azure does not yet host .net core 3 directly.
But it does.
In the Azure Portal, go to your WebApp after deployment (or create one beforehand).
Go to Extensions and click Add [+] and select ASP.NET Core 3 (x86 for the free hosting).
Also go to Settings, General and enable WebSockets, they're Off by default.
Temporary:
Note that Preview-6 is not available as an extension, so either use Preview-5 or deploy as self-contained.
Couldnt put a picture in the comment, so I thought i'd show it here. This is my current publish wizard.
Just did it with a brand new project via new project -> Asp.net core web application -> blazor (Asp.net core hosted) built and published fine to azure app service fine.
My answer is:
Configure the publish profile to "Self-contain" deployment mode.
Edit all .csproj files to change <TargetFramework>...</TargetFramework> node name to <TargetFrameworks>...</TargetFrameworks>. (see also: https://stackoverflow.com/a/42855070 )
Fix the web root folder path string at runtime in Startup class like below.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using System.IO;
using System.Linq;
namespace BlazorHostedOnAzure.Server
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
// ---- APPEND PART.1 BEGIN ----
var clientBlazorWebRootPath = default(string);
// ---- APPEND PART.1 END ----
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
// ---- APPEND PART.2 BEGIN ----
else
{
if (env.WebRootPath != null)
{
var pathOfIndex = Path.Combine(env.WebRootPath, "index.html");
var pathOfContent = Path.Combine(env.WebRootPath, "_content");
if (!File.Exists(pathOfIndex) && Directory.Exists(pathOfContent))
{
clientBlazorWebRootPath = Directory.GetDirectories(pathOfContent).FirstOrDefault();
if (clientBlazorWebRootPath != null)
{
env.WebRootPath = clientBlazorWebRootPath;
}
}
}
}
// ---- APPEND PART.2 END ----
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
// ---- APPEND PART.3 BEGIN ----
if (clientBlazorWebRootPath != null)
{
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(clientBlazorWebRootPath)
});
}
// ---- APPEND PART.3 BEGIN ----
}
}
}
I published my sample code and README on the GitHub my repository.
https://github.com/sample-by-jsakamoto/BlazorHostedV3Preview6OnAzureWebApp#how-to-configure-client-side-blazor-v300-preview-6-that-is-hosted-on-an-aspnet-core-server-to-deploy-it-to-azure-at-13-jul-2019

Azure Functions, Entity Framework and Oracle DB - basic POC fails

I'm having a lot of trouble getting a basic proof-of-concept working, in which I am accessing an Oracle DB (11g) through Azure Functions via Entity Framework (6.2).
Prerequisites:
ODT For Visual Studio 2017 is installed, as well as Azure Functions CLI/Core Tools. Everything mentioned below is done entirely via Visual Studio 2017, not through Azure portal.
Take 1:
Created a new project with the Azure Functions template.
Installed NuGet packages EntityFramework (6.2.0), Oracle.ManagedDataAccess (12.2.1100) and Oracle.ManagedDataAccess.EntityFramework (12.2.1100). Note: When installing NuGet packages in projects using the Azure Functions template, the packages are added under Dependencies -> NuGet, rather than under References.
Added ADO.NET Entity Data Model to project.
Problem: After setting my connection string, choosing Entity Framework 6.x is unavailable, with the following error message:
An Entity Framework database provider compatible with the latest
version of Entity Framework could not be found for your data
connection. If you have already installed a compatible provider,
ensure you have rebuilt your project before performing this action.
Otherwise, exit this wizard, install a comaptible provider, and
rebuild your project befre performing this action.
As the simplest of workarounds, I have tried to just go ahead with EF5, but it throws an exception while creating the DB model (after selecting the objects to include in model, including some stored procedures).
Take 2:
Created project and installed NuGet packages as above.
Created class library project to facilitate the Oracle interactions.
Installed the same NuGet packages as above in the class library project.
Added ADO.NET Entity Data Model to class library project and added some database objects to the database model. Also added custom constructor to the model for specific connection string, because managing connection strings in Azure Functions was a seperate set of headaches that I'll deal with later.
Added a simple wrapper method to the class library project that calls a stored procedure from the database model:
public static string NameByEmpNo(int empNo)
{
string result;
MyEntities entities = new MyEntities("metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|res://*/MyEntities.msl;provider=Oracle.ManagedDataAccess.Client;provider connection string='DATA SOURCE=127.0.0.1:1521/ORCL;PASSWORD=tiger;USER ID=SCOTT'");
ObjectParameter name = new ObjectParameter("o_empname", typeof(string));
entities.GET_EMP_NAME_PROC(empNo, name);
result = (string)name.Value;
return result;
}
Added reference to the class library in the Azure Functions project.
Added function that calls NameByEmpNo:
[FunctionName("GetNameByEmpNo")]
public static async Task<HttpResponseMessage> GetNameByEmpNo([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage req, TraceWriter log)
{
int empNo = Int32.Parse(req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "empno", true) == 0)
.Value);
string empName = ScottAccess.NameByEmpNo(empNo);
return req.CreateResponse(HttpStatusCode.OK, "Employee name: " + empName);
}
Problem: At runtime, calling the function fails with this error
message:
Exception while executing function: GetNameByEmpNo -> The ADO.NET
provider with invariant name 'Oracle.ManagedDataAccess.Client' is
either not registered in the machine or application config file, or
could not be loaded. See the inner exception for details. -> Unable to
find the requested .Net Framework Data Provider. It may not be
installed.
Bonus info: My class library works perfectly when called through a console application. Also, my Azure Functions app works perfectly when calling functions that do not use my class library...
I am stumped. Has anyone got experience with getting this combination of techs working together and can offer some insight into where I'm going wrong / provide steps to get a basic connection working?
Entity Framework within Azure Functions defaults the providers to System.Data.SqlClient so SQL connections will work without any configuration changes, but that means you have to do something special for Oracle connections. The problem seems to come from the config values that the Oracle.ManagedDataAccess.Client library assumes are available within the App.Config or Web.Config file in the project, which are inserted whenever you install the Oracle.ManagedDataAcess.EntityFramework Nuget package. Azure Functions don't have config files, and I wasn't able to find any way to specify the Oracle provider in the settings json files.
I found a solution in this post
It suggests bypassing this mechanism and creating a DbConfiguration for Oracle, then using DbConfigurationType to tell the DbContext which configuration you're using.
public class OracleDbConfiguration : DbConfiguration
{
public OracleDbConfiguration()
{
SetDefaultConnectionFactory(new OracleConnectionFactory());
SetProviderServices("Oracle.ManagedDataAccess.Client", EFOracleProviderServices.Instance);
SetProviderFactory("Oracle.ManagedDataAccess.Client", new OracleClientFactory());
}
}
[DbConfigurationType(typeof(OracleDbConfiguration))]
public partial class MyEntities : IGISContext
{
//Expose Connection String Constructor
public MyEntities(string connectionString, int commandTimeoutInSeconds = 30) : base(connectionString)
{
this.Database.CommandTimeout = commandTimeoutInSeconds;
}
}
Note: I used EF 6 Database First to generate my EDMX; MyEntities here is a partial class for providing a constructor that takes in a connection string.
The oracle connection wil use the specified DbConfiguration class, and any SQL database connections will continue to work using the defaults.
My solution is using the Nuget Packages:
EntityFramework 6.2.0
Oracle.ManagedDataAccess 12.2.1100
Oracle.ManagedDataAccess.EntityFramework 12.2.1100

VB6 App using COM Interop works fine in IDE, but compiled EXE crashes

I am currently working on an app in VB6 that uses COM Interop libraries written in C# using the .NET 2.0 framework.
I used regasm.exe to register the tlb files from the .NET dlls using the /codebase switch. I was then able to successfully build and run the application in the VB6 IDE with no issues. The .NET code uses a config file, so I added it to the VB6 directory and it read from the configurations fine.
However, I then compiled an EXE file from the project an ran it on the same machine as the IDE is running on. I coupled the EXE with the config file just as I had done in debugging with the VB6.EXE, but when the app executes the first call to a method in one of the .NET classes, it throws a run-time error indicating an "Automation Error".
In my Declarations, I instantiate the following objects from the .NET classes, which seems to work fine.
Private objSession As New Session
Private curFolder As Folder
Private colFolderTemplates As New FolderTemplateCollection
Private objLicense As New License
However, the Automation Error comes up at runtime when the first line is executed:
Call objSession.Configuration.Configure(connectionString)
I tried adding the .NET dlls to the same directory as the Release EXE and re-registering the tlb files, but it did not help. Any suggestions on what I could check?
Ok, shot in the dark. Things to try:
Explicitly new up the Session object (as well as License and FolderTemplateCollection):
Private objSession as Session
Set objSession = new Session
Automation error indicates that the GUIDs from the .NET assembly are not persisting. To do so, do this in your C# code - this then guarantees that all Interfaces/Classes/Virtual tables persist, no matter how many times you compile your c# code:
[Guid("9AC71CA7-6F82-44A3-9ABE-75354B514A46")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IManager
{
[DispId(1)]
void Display(ADODB.Recordset recordSet);
[DispId(2)]
void Close();
[DispId(3)]
string UserName { get; set; }
[DispId(4)]
string Password { get; set; }
[DispId(5)]
string Database { get; set; }
[DispId(6)]
string Server { get; set; }
[DispId(7)]
ICriteria Criteria { get; set; }
}
[Guid("B9BB5B84-8FBD-4095-B846-EC072163ECD3")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyApp.Manager")]
public class Manager : IManager
{
void Display(ADODB.Recordset recordSet)
{
}
...
}

Resources