So I have this code to send email. The code tries to find the .cshtml for the email template.
This is my folder hierarchy:
Project
-- Email
---- EmailConfirmation.cshtml
---- ResetPassword.cshtml
This is the code to find the template:
public static string GetEmailTemplate(string templateName)
{
string path = Path.Combine(Config.Env.ContentRootPath, "Email", templateName);
string content = "";
// This text is added only once to the file.
if (File.Exists(path))
{
// Create a file to write to.
content = File.ReadAllText(path);
}
else
{
throw new Exception("The email template could not be found");
}
return content;
}
In Debug, this runs perfectly, but in production, the code cannot find the template.
How can I include the template in publish package?
I tried this in my .csproj:
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Migrations\" />
<Views Include="Email\**" />
</ItemGroup>
It is not working.
Edit
I tried to set this:
But still not working.
So my problem was the Email folder was not copied to the published package. Add this code to .csproj:
<ItemGroup>
<None Include="Email\EmailConfirmation.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Email\ResetPassword.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Or you can set it in the project explorer like this:
For some reason, the build action has to be set to None, because .NetCore will treat Content differently and the folder will not be copied to the publishing package (maybe the content will be merge into .dll, I do not know).
Could you try this ?
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory=Directory.GetParent(workingDirectory).Parent.Parent.FullName;
It will give you the Path to the project directory. Then you complete with the subdirectory which leads to your file.
Related
I am trying to setup a unit test in F# with asp.net core. with the code below. but the code to the server fails with a 404 Not found.
Is there a reason why the controller is being discovered by the framework ?
module Tests
open System
open Xunit
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type Startup () =
member __.ConfigureServices(services: IServiceCollection) =
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2) |> ignore
member __.Configure(app: IApplicationBuilder, env: IHostingEnvironment) =
app.UseMvc() |> ignore
[<Route("api")>]
[<ApiController>]
type TestController() =
inherit ControllerBase()
[<HttpGet("testGet")>]
member __.TestGet() =
121221
[<HttpPost("testPost")>]
member __.TestPost() =
String.Empty
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
let baseAdress = "https://localhost:9000"
use host = buildHost(baseAdress)
host.RunAsync() |> ignore
let client = new System.Net.Http.HttpClient(BaseAddress = new System.Uri(baseAdress))
let result = client.GetAsync("api/testGet").Result
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
Here is the content of the fsproj File
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Tests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.2" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
</Project>
Except the reasons commented above by #Kirk Larkin, you need put your controller into a namespace instead of module directly.
namespace App //<------------- put your controller into a namespace
// ... open
type Startup () =
...
[<Route("api")>]
[<ApiController>]
type TestController() =
...
module Tests=
// ... open
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
...
Also note you're starting the server by an asynchronous method host.RunAsync() |> ignore. Although the above test code works fine for me, there's a chance your HttpClient will send requests before the server is ready. A good way is to add a reference to the Microsoft.AspNetCore.TestHost package and using the built-in TestHost instead:
let hostBuilder =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
use server = new Microsoft.AspNetCore.TestHost.TestServer(hostBuilder)
use client = server.CreateClient()
let result = client.GetAsync("api/testGet").Result // a quick & dirty test
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
[Edit]:
I'm now sure it is a behavior by design. See source code on GitHub:
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic)
{
return false;
}
Since the class within a F# module will be compiled into a nested class, it won't be considered as a Controller at all.
On top of the answer that #itminus provided. the issue can be resolved by configuring the application manager in configureServices like so
member this.ConfigureServices(services: IServiceCollection) =
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApplicationPartManager(fun m ->
m.FeatureProviders.Add
{
new Microsoft.AspNetCore.Mvc.Controllers.ControllerFeatureProvider() with
member __.IsController (typeInfo:System.Reflection.TypeInfo) =
(typeof<ControllerBase>.IsAssignableFrom(typeInfo)) || base. IsController(typeInfo)
}
) |> ignore
services.AddRouteAnalyzer() |> ignore
RaceConditionTest.fs
namespace FConsole
module RaceConditionTest =
let test x =
...
Program.fs
open System
open FConsole
[<EntryPoint>]
let main argv =
RaceConditionTest.test 1000
0 // return an integer exit code
Then I run my console app (linux)
$ dotnet run
error FS0039: The namespace or module 'FConsole' is not defined.
There is only one test method in RaceConditionTest.fs
Is the order of files the problem? if so, How do I indicate the order of *.fs files?
as #boran suggested in his comments there is this FConsoleProject.fsproj
I just added my file before Program.fs
<ItemGroup>
<Compile Include="RaceConditionTest.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
I have a custom MSBuild Task that generates a file based on files that have no build action. The generated files need to be embedded into the final assembly. The task looks something like this:
public class MyTask : Task
{
public string OutputDirectory { get; set; }
public string[] NoneIncluded { get; set; }
private IEnumerable<ITaskItem> _generatedFiles;
[Output]
public ITaskItem[] GeneratedFiles => _generatedFiles.ToArray();
public override bool Execute()
{
_generatedCssFiles = new List<ITaskItem>();
foreach(var item in NoneIncluded)
{
if(someCondition)
{
var contents = DoFoo(item);
var outputPath = Path.Combine(OutputDirectory, $"{item}.txt");
File.WriteAllText(outputPath, contents);
_generatedFiles.Add(new TaskItem(ProjectCollection.Escape(outputFile)));
}
}
}
}
In my targets file, I then have a target defined like the following:
<PropertyGroup>
<CoreCompileDependsOn>MyTarget;$(CoreCompileDependsOn);</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="MyTarget"
BeforeTargets="CoreCompile;Build">
<MyTask OutputDirectory="$(IntermediateOutputPath)"
NoneIncluded="#(None)">
<Output ItemName="FilesWrite"
TaskParameter="GeneratedFiles"/>
<Output ItemName="EmbeddedResource"
TaskParameter="GeneratedFiles" />
</MyTask>
</Target>
I ultimately end up with two issues that I can't seem to resolve:
Although the files are generated prior to the compile task, it isn't embedded into the assembly, unless I rebuild the project without cleaning the outputs. On the subsequent build the file is embedded.
If I generate the files in the IntermediateOutputPath, the embedded resource id includes that path. So instead of MyProject.SomeResource.txt I get MyProject.obj.netstandard2._0.SomeResource.txt
Note:
- If I replace the Path.Combine and simply generate the output file in the project, it fixes the issue of the resource id, but not the first issue with it not being embedded on the first compile.
How can I ensure the my generated files are embedded on the first compilation, and that I can generate them in the IntermediateOutputPath rather than the Project directory with a resource id as if it were in the project.
You need to run your target earlier in the pipeline. CoreCompile is too late.
You can add the following to your .csproj file. This target will run before ResolveReferences and add Image.png as an embedded resource.
<Target Name="IncludeDynamicResources" BeforeTargets="ResolveReferences">
<ItemGroup>
<EmbeddedResource Include="Image.png">
<Type>Non-Resx</Type>
</EmbeddedResource>
</ItemGroup>
</Target>
Your second issue can be solved by simply setting LogicalName metadata to the created task item.
TaskItem taskItem = new TaskItem(ProjectCollection.Escape(outputFile));
taskItem.SetMetadata("LogicalName", $"{AssemblyName}.{item}.txt");
_generatedFiles.Add(taskItem);
Now to access the assembly name inside your task just add AssemblyName string property to MyTask and AssemblyName="$(AssemblyName)" to your targets. This should produce the desired result.
My code is as follows:
...
propertyGroup1.AddProperty("DeployOnBuild", "true");
propertyGroup1.AddProperty("DeployTarget", "MSDeployPublish");
propertyGroup1.AddProperty("MSDeployServiceUrl", "localhost");
propertyGroup1.AddProperty("DeployIisAppPath", "local.projects.com");
propertyGroup1.AddProperty("MSDeployPublishMethod", "InProc");
propertyGroup1.AddProperty("AllowUntrustedCertificate", "true");
...
cSharpProject.ProjectCollection.RegisterLogger(cSharpLogger);
try {
buildResult = cSharpProject.Build();
...
buildResult is set to false. However, it does not raise any error to the logger. And the project is not deployed.
I'm running my exe in Administrator mode.
Any help is apprecited.
Thanks
Chris
hint: CSAutoParameterize.parameters and other subfolders have not been created.
<Target Name="WebPublish">
<MsBuild Projects="MyProject.csproj" Properties="DeployOnBuild=true;DeployTarget=MSDeployPublish;MSDeployServiceUrl=localhost;DeployIisAppPath=local.website.com;MSDeployPublishMethod=RemoteAgent;AllowUntrustedCertificate=true;Username=someuser;password=somepassword" />
</Target>
I was able to add this target to the project and call just that specific target.
Is there a way to automatically increment the "minimum required version" fields in a ClickOnce deployment to always equal the current build number? Basically, I always want my deployment to be automatically updated at launch.
I suspect I'm going to need a some pre-/post-build events, but I hope there's an easier way.
I may be a little late with answering this one but I found it difficult to find the solution on google but eventually figured it out so thought I would share.
With MSBuild version 4 (VS2010 and VS2012) this can be achieved by inserting the following target:
<Target Name="AutoSetMinimumRequiredVersion" BeforeTargets="GenerateDeploymentManifest">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="MinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="_DeploymentBuiltMinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
</Target>
The $(ApplicationVersion) is the same setting that you can set manually in the project's Publish window in the VS IDE, with the revision part set to an asterisk. The $(ApplicationRevision) is the actual revision being used for the published version. The FormatVersion task is a built-in MSBuild task that formats the two into a single full version number.
This will set the 'Minimum Required Version' to be the same as the 'Publish Version' therefore ensuring that the new deployment will always be installed by users, ie no option to Skip the update.
Of course, if you don't want to set the minimum required version to the publish version and want to use a different source property then it is straight-forward to amend the target, but the principle is the same.
I ended up actually rolling an AddIn to VS that synchronizes all the version numbers, and then builds and publishes with a single click. It was pretty easy.
Public Sub Publish()
Try
Dim startProjName As String = Nothing
Dim targetProj As Project = Nothing
Dim soln As Solution2 = TryCast(Me._applicationObject.DTE.Solution, Solution2)
If soln IsNot Nothing Then
For Each prop As [Property] In soln.Properties
If prop.Name = "StartupProject" Then
startProjName = prop.Value.ToString()
Exit For
End If
Next
If startProjName IsNot Nothing Then
For Each proj As Project In soln.Projects
If proj.Name = startProjName Then
targetProj = proj
Exit For
End If
Next
If targetProj IsNot Nothing Then
Dim currAssemVersionString As String = targetProj.Properties.Item("AssemblyVersion").Value.ToString
Dim currAssemVer As New Version(currAssemVersionString)
Dim newAssemVer As New Version(currAssemVer.Major, currAssemVer.Minor, currAssemVer.Build, currAssemVer.Revision + 1)
targetProj.Properties.Item("AssemblyVersion").Value = newAssemVer.ToString()
targetProj.Properties.Item("AssemblyFileVersion").Value = newAssemVer.ToString()
Dim publishProps As Properties = TryCast(targetProj.Properties.Item("Publish").Value, Properties)
Dim shouldPublish As Boolean = False
If publishProps IsNot Nothing Then
shouldPublish = CBool(publishProps.Item("Install").Value)
If shouldPublish Then
targetProj.Properties.Item("GenerateManifests").Value = "true"
publishProps.Item("ApplicationVersion").Value = newAssemVer.ToString()
publishProps.Item("MinimumRequiredVersion").Value = newAssemVer.ToString()
publishProps.Item("ApplicationRevision").Value = newAssemVer.Revision.ToString()
End If
End If
targetProj.Save()
Dim build As SolutionBuild2 = TryCast(soln.SolutionBuild, SolutionBuild2)
If build IsNot Nothing Then
build.Clean(True)
build.Build(True)
If shouldPublish Then
If build.LastBuildInfo = 0 Then
build.Publish(True)
End If
End If
End If
End If
End If
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Out of the box, I don't belive there is a way. It's not too much effort to spin your own however.
The approach I use is as follows:
1) create a Version.Properties file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Util-VersionMajor>1</Util-VersionMajor>
<Util-VersionMinor>11</Util-VersionMinor>
<Util-VersionBuild>25</Util-VersionBuild>
<Util-VersionRevision>0</Util-VersionRevision>
<Util-VersionDots>$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)</Util-VersionDots>
<Util-VersionUnders>$(Util-VersionMajor)_$(Util-VersionMinor)_$(Util-VersionBuild)_$(Util-VersionRevision)</Util-VersionUnders>
<MinimumRequiredVersion>$(Util-VersionDots)</MinimumRequiredVersion>
<ApplicationVersion>$(Util-VersionDots)</ApplicationVersion>
<ApplicationRevision>$(Util-VersionRevision)</ApplicationRevision>
</PropertyGroup>
</Project>
2) Import the Version.Properties file into your project files
3) Create a task to increment the version on Build. Here's mine
<Target Name="IncrementVersion" DependsOnTargets="Build" Condition="'$(BuildingInsideVisualStudio)'==''">
<ItemGroup>
<Util-VersionProjectFileItem Include="$(Util-VersionProjectFile)" />
</ItemGroup>
<PropertyGroup>
<Util-VersionProjectFileFullPath>#(Util-VersionProjectFileItem->'%(FullPath)')</Util-VersionProjectFileFullPath>
</PropertyGroup>
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Version Major="$(Util-VersionMajor)" Minor="$(Util-VersionMinor)" Build="$(Util-VersionBuild)" Revision="$(Util-VersionRevision)" RevisionType="None" BuildType="Increment">
<Output TaskParameter="Major" PropertyName="Util-VersionMajor" />
<Output TaskParameter="Minor" PropertyName="Util-VersionMinor" />
<Output TaskParameter="Build" PropertyName="Util-VersionBuild" />
<Output TaskParameter="Revision" PropertyName="Util-VersionRevision" />
</Version>
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMajor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMajor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMinor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMinor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionBuild" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionBuild)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionRevision" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-VersionProjectFileFullPath)"" />
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-AssemblyInfoFile)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-AssemblyInfoFile)"" Outputs="" />
<AssemblyInfo CodeLanguage="CS" OutputFile="$(Util-AssemblyInfoFile)" AssemblyConfiguration="$(Configuration)" AssemblyVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" AssemblyFileVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-AssemblyInfoFile)"" />
</Target>
Some additional clickonce tricks here http://weblogs.asp.net/sweinstein/archive/2008/08/24/top-5-secrets-of-net-desktop-deployment-wizards.aspx
Here's how I handled this one. First I created a custom task that wraps string replacement:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
namespace SynchBuild
{
public class RemoveAsterisk : Task
{
private string myVersion;
[Required]
public string Version
{
set{myVersion = value;}
}
[Output]
public string ReturnValue
{
get { return myVersion.Replace("*", ""); }
}
public override bool Execute()
{
return true;
}
}
}
So that gets built into SynchBuild.dll which you see referenced in the UsingTask below. Now I tried just overwritting the MinimumRequiredVersion property, but it didn't seem to get picked up, so I just overwrote the GenerateApplicationManifest target by adding the following lines to the end of my csproj file:
<UsingTask AssemblyFile="$(MSBuildExtensionsPath)\WegmansBuildTasks\SynchBuild.dll" TaskName="SynchBuild.RemoveAsterisk" />
<Target Name="GenerateDeploymentManifest" DependsOnTargets="GenerateApplicationManifest" Inputs="
$(MSBuildAllProjects);
#(ApplicationManifest)
" Outputs="#(DeployManifest)">
<RemoveAsterisk Version="$(ApplicationVersion)$(ApplicationRevision)">
<Output TaskParameter="ReturnValue" PropertyName="MinimumRequiredVersion" />
</RemoveAsterisk>
<GenerateDeploymentManifest MinimumRequiredVersion="$(MinimumRequiredVersion)" AssemblyName="$(_DeploymentDeployManifestIdentity)" AssemblyVersion="$(_DeploymentManifestVersion)" CreateDesktopShortcut="$(CreateDesktopShortcut)" DeploymentUrl="$(_DeploymentFormattedDeploymentUrl)" Description="$(Description)" DisallowUrlActivation="$(DisallowUrlActivation)" EntryPoint="#(_DeploymentResolvedDeploymentManifestEntryPoint)" ErrorReportUrl="$(_DeploymentFormattedErrorReportUrl)" Install="$(Install)" MapFileExtensions="$(MapFileExtensions)" MaxTargetPath="$(MaxTargetPath)" OutputManifest="#(DeployManifest)" Platform="$(PlatformTarget)" Product="$(ProductName)" Publisher="$(PublisherName)" SuiteName="$(SuiteName)" SupportUrl="$(_DeploymentFormattedSupportUrl)" TargetCulture="$(TargetCulture)" TargetFrameworkVersion="$(TargetFrameworkVersion)" TrustUrlParameters="$(TrustUrlParameters)" UpdateEnabled="$(UpdateEnabled)" UpdateInterval="$(_DeploymentBuiltUpdateInterval)" UpdateMode="$(UpdateMode)" UpdateUnit="$(_DeploymentBuiltUpdateIntervalUnits)" Condition="'$(GenerateClickOnceManifests)'=='true'">
<Output TaskParameter="OutputManifest" ItemName="FileWrites" />
</GenerateDeploymentManifest>
</Target>
The end result is we take the app version and revision, combine them, remove the asterisk, then set the minimum required version. I have the auto increment app version in my publish properties set so that's how incrementing takes place, then I'm just setting the minimumrequiredversion to always match.I don't use team build, this is just designed so that a developer using visual studio can make all clickonce deployments required. Hope this helps.
If you are publishing your ClickOnce application from Visual Studio then just install the AutoUpdateProjectsMinimumRequiredClickOnceVersion NuGet Package in your project and you're good to go.
If you are publishing from a build server or other script, then you can use the Set-ProjectFilesClickOnceVersion PowerShell script. My blog describes in more detail how to setup your build server to accommodate publishing ClickOnce applications.
Are you looking for Application Updates?
Right clicking on the project in the Solution Explorer and then clicking Publish... is the wrong way to get Application Updates. You have to right-click your project and the click Properties, then click the Publish tab. Click the Updates... button and then check the "The application should check for updates" check box. There you can also specify a minimum required version for the application. (I haven't used that functionality but the Updates functionality is the core reason I use ClickOnce and it works great.)