C# i async

Poques ganes:

bool method(){
   var result = classasync.method().Result;
   return result;
}

Venga va, una oportunitat:

async bool method(){
   var result = await classasync.method();
   return result;
}

Però es que no vull fer-lo asíncron:

bool method(){
   var resultTask = Task.Run(async () => await classasync.method());
   resultTask.Wait();
   return resultTask.Result;
}

Va, donem-li una oportunitat a l’asíncron:

async bool method(){
   var resultTask = Task.Run(async () => await classasync.method());
   resultTask.WaitAsync();
   return resultTask.Result;
}

Ajuda: https://ianvink.wordpress.com/2021/12/12/c-running-an-async-await-task-inside-a-non-async-method-code-block/

Sharepoint i Net 4.5

La API de sharepoint ha canviat i això requereix utilitzar oauth. Podem fer-ho amb el exemple que propociona al tutorial al docs:

https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/using-csom-for-dotnet-standard

  1. Ves al Portal de Azure AD: https://aad.portal.azure.com
  2. Selecciona Azure Active Directory i registres d’aplicacions a l’esquerra
  3. Selecciona New registration
  4. Introdueix un nom per a la aplicació i selecciona Register
  5. Aneu a Permisos de l’API per concedir permisos a la aplicació, seleccioneu “Add a permission”, tria SharePoint, Delegated permissions i selecciona AllSites.Manage
  6. Selecciona Concedir consentiment de l’administrador per consentir els permisos sol·licitats per l’aplicació
  7. Seleccioneu Authentication a l’esquerra
  8. Canvia “Allow public client flows” a Sí
  9. Selecciona Overview i guarda Application ID

Nota: si no seguíssim pas per pas les instruccions de configurar una aplicació a Azure ens podem trobar els següents errors:

A més a més poden sorgir altres errors:

  • El usuari no és vàlid, o no te renovada la contrasenya, o està inactiu: aquestes gestions no seràn possibles ja que no tenim previst fer prompt de la pàgina de AD per fer canvi de contrasenya, etc.

Després cal personalitzar la classe AuthenticationManager que donen al exemple ja que està pensada per a Net Core o superiors, però no NET Framework cal fer un seguit d’adaptacions. Aquí fetes, res del altre món:

Clickonce amb Net6

Funcions de System.Deployment.Application

Aquestes ja no estan disponibles amb NET6 [i als docs se fa referència] però afortunadament es poden simular via lectura de les variables d’entorn [informació en aquesta request a github] , dels fitxers de clickonce i dels directoris propis del desplegament.

Fent servir part del codi d’exemple amb de desplegament de una aplicació NET6 WPF com aquest luncher he fet una llibreria tipificant les constants del desplegament que més fem servir així com simular el update silenciós de clickonce que en realitat no és un altre cosa que instal·lar per enrere i apagar la aplicació un cop està instal·lada la nova versió (el restart de NET Framework ara no funcionarà):

https://github.com/Ruekov/ClickOnceNET6

MSBuild

La preferència de les aplicacions amb NET6 és preferible utilitzar la comanda dotnet msbuild el problema més important en aquest cas és que clickonce es fonamenta bàsicament en NET Framework 3.5 així que haurem d’utilitzar MSBuild.

Com passava ja amb MSBuild i clickonce, els perfils o les ordes de publicació es comporten diferent amb Visual Studio que executades des MSBuild. A més a més, tenim el handicap de que les eines de msbuildtasks de loresoft no acaben de funcionar bé.

Per aquesta raó utilitzant els perfils de publicació (Propierties/PublicationProfiles/ClickOnceProfile.pubxml) amb paràmetres postcompilació afegirem la tasca què:

  • Separi els fitxers de desplegament (setup.exe, xxxxx.application, launcher.exe i carpeta ApplicationFiles)
  • Copi un template del index.html per personalitzar-lo (de nou MSBuild no és capaç de fer-lo posant-hi el WebPageFileName a true)
  • Faci un ZIP amb els continguts del deplegament
	<Target Name="ZipPublishOutput" AfterTargets="ZipDeployment">
		<ItemGroup>
			<LauncherFile Include="$(PublishDir)\Launcher.exe"/>
			<ApplicationFile Include="$(PublishDir)\$(MSBuildProjectName).application"/>
			<SetupFile Include="$(PublishDir)\setup.exe"/>
			<ApplicationFiles Include="$(PublishDir)\Application Files\**\*.*"/>
		</ItemGroup>
		<Copy SourceFiles="$(ProjectDir)\index.html" DestinationFiles="$(PublishDir)\..\..\index.html"/>
		<WriteLinesToFile File="$(PublishDir)\..\..\index.html" Lines="$([System.Text.RegularExpressions.Regex]::Replace($([System.IO.File]::ReadAllText('$(PublishDir)\..\..\index.html')), ''{AppVersion}'', ''$(AssemblyVersion)''))" Overwrite="true" Encoding="Unicode" />
		<WriteLinesToFile File="$(PublishDir)\..\..\index.html" Lines="$([System.Text.RegularExpressions.Regex]::Replace($([System.IO.File]::ReadAllText('$(PublishDir)\..\..\index.html')), ''{AppName}'', ''$(MSBuildProjectName)''))" Overwrite="true" Encoding="Unicode" />
		<Copy
			SourceFiles="@(LauncherFile)"
			DestinationFolder="$(PublishDir)\..\..\"
        />
		<Copy
            SourceFiles="@(SetupFile)"
            DestinationFolder="$(PublishDir)\..\..\"
        />
		<Copy
			SourceFiles="@(ApplicationFile)"
			DestinationFolder="$(PublishDir)\..\..\"
        />
		<Copy
            SourceFiles="@(ApplicationFiles)"
            DestinationFolder="$(PublishDir)\..\..\Application Files\%(RecursiveDir)"
        />
		<RemoveDir Directories="$(OutputPath)" />
		<ZipDirectory SourceDirectory="$(PublishDir)\..\..\" DestinationFile="$(PublishDir)\..\..\..\PRO_$(MSBuildProjectName)_$(AssemblyVersion).zip" Overwrite="true" />
	</Target>

Aquest Target haurà de estar especificat al csproj del projecte:

	<Target Name="ZipDeployment">
	</Target>

Al mateix csproj podrem afegir una tasca per mantenir actualitzat als prefils de publicació el número de versió:

<Target Name="updateVersionCLickOnce">
    <PropertyGroup>
        <FilePath>.\Properties\PublishProfiles\ClickOnceProfile.pubxml</FilePathPRO>
    </PropertyGroup>
    <WriteLinesToFile File="$(FilePath)" Lines="$([System.Text.RegularExpressions.Regex]::Replace($([System.IO.File]::ReadAllText('$(FilePath)')), '&gt;\d+\.\d+\.\d+\.\d+&lt;', ''&gt;$(AssemblyVersion)&lt;''))" Overwrite="true" Encoding="Unicode" /> 
</Target>

Al ser una tasca que hauria de ser executada abans de començar la compilació de la publicació haurem de especificar-la al inici del XML del csproj:

<Project Sdk="Microsoft.NET.Sdk" InitialTargets="updateVersionCLickOnce">

Ara ja podem executar el msbuild generant la sortida correcta i esperada:

msbuild /t:Publish,ZipDeployment /p:DeployOnBuild=true /p:PublishProfile=ClickOnceProfile.xml /restore

Recursos