How do you handle multiple application classes in a xamarin android project - xamarin.android

There is a 3rd party library I need to use, that includes a class decorated with the [Application] attribute. This causes compiler errors since I have my own application class that uses an [Application] attribute. I would like my application class to inherit from the 3rd party lib's application class.
public class MyApplication : ThirdPartyApplication
{
}
however since I can't decorate my class with the [Application] attribute I have no way to specify in the Manifest that it should run "MyApplication" and not "ThirdPartyApplication".
If I manually add an entry into AndroidManifest.xml
<application
android:name="com.your.packagename.MyApplication"
android:icon="#drawable/luncher_icon"
android:label="#string/app_name">
It will get replaced after the project gets built with
<application
android:name="mdxxx.ThirdPartyApplication"
android:icon="#drawable/luncher_icon"
android:label="#string/app_name">
Does anyone know how to handle this situation in Xamarin Android?
Keep in mind that the 3rd party library can not be modified.
An alternate solution would be a way to disable all manifest generating attributes and manually create the AndroidManifest. There does not seem to be any way to do this either.
The below post is the exact situation I am having but in pure Android. Note that due to the above issues that this solution will not work for Xamarin.
how-to-handle-multiple-application-classes-in-android

So I was able to figure out a solution but it boarders on being a hack.
In short the android manifest is going to be modified using MSBuild.
Step 1: Have your custom application class inherit from the the Third Party lib's custom application and decorate your class with the "Register" tag.
[Register("com.namespace.MyApplication")]
public class MyApplication : ThirdPartyApplication
{
}
Step 2: Have your project include the RoslynCodeTaskFactory NuGet package
Step 3: Unload your project, then add the following in the Project tag
<UsingTask TaskName="UpdateManifest" TaskFactory="CodeTaskFactory" AssemblyFile="$(RoslynCodeTaskFactory)" Condition=" '$(RoslynCodeTaskFactory)' != '' ">
<ParameterGroup>
<AndroidManifestFilename ParameterType="System.String" Required="true" />
<ApplicationName ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.Xml" />
<Code Type="Fragment" Language="cs"><![CDATA[
XmlDocument doc = new XmlDocument();
doc.Load(AndroidManifestFilename);
XmlNode node = doc.DocumentElement.SelectSingleNode("/manifest/application");
node.Attributes["android:name"].InnerText = ApplicationName;
doc.Save(AndroidManifestFilename);
]]></Code>
</Task>
</UsingTask>
<Target Name="CleanManifest" AfterTargets="_GenerateJavaStubs">
<UpdateManifest AndroidManifestFilename="$(ProjectDir)\obj\$(Configuration)\android\AndroidManifest.xml" ApplicationName="com.namespace.MyApplication" />
</Target>
Step 4: Reload the project and build. The manifest should now point to custom application class. If you get a class not found runtime exception most likely you forgot to add [Register] from Step 1.

Related

Is it possible to use ASP.NET Core 2.x MVC on .NET Framework 4.6.1 console application

I'm attempting to port an ASP.NET MVC 4.x application over to ASP.NET Core 2.2. WebApi works great, but I'm stuck with porting over the MVC views. I've created a full "minimally viable" repro to show what I mean. The Program.cs looks like this:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace TestApp
{
public class Program
{
static void Main(string[] args)
{
string port = "9005";
var host = new WebHostBuilder()
.UseKestrel()
.UseStartup<Startup>()
.UseUrls($"http://0.0.0.0:{port}")
.Build();
host.Run();
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddRazorViewEngine()
.AddJsonFormatters();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}
}
I have one controller in Controllers\HomeController.cs that looks like this:
using Microsoft.AspNetCore.Mvc;
namespace TestApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
I have one view at Views\Home\Index.cshtml that looks like this:
#{
var x = "Test";
}
Home page
When I run the program, I can confirm I hit the breakpoint at return View(); - I get no exceptions or anything. However, the browser will return an error 500 and nothing is displayed.
My theory is I'm missing whatever the mechanism is that compiles the Index.cshtml into something the ASP.NET Core MVC framework can find. I've done a bit of research but it's very unclear how all this works. There is something called Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation which apparently does runtime compilation of cshtml files, but it has a .NET Core dependency. In the ASP.NET 4.x world, I believe there was an ASP.NET compiler that would run as a build target that handled this sort of thing, but it's a bit unclear how it works. Pretty much every Visual Studio template for ASP.NET Core is built around .NET Core, which seems to magically make this stuff work but I'm not exactly sure how. Anyone know what the next steps would be to get this to work? Thanks!
PS: The reason I cannot run on the .NET Core framework is because this project has a massive amount of dependencies on legacy .NET Framework (Windows) code which would take years to port over.
Edit: Here's my entire CSPROJ file (I've tried a few SDKs to no avail)
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>
When your project targes .NET Framework you should install Microsoft.AspNetCore.Mvc.Razor.ViewCompilation package.
It contains build-time references required to enable Razor view compilation.
Include its package reference to your .csproj file:
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.2.0" PrivateAssets="All" />
This extra step is only aplicable on .net core project targets
Execute the below command at the project root to prepare the app for a framework-dependent deployment:
dotnet publish -c Release

NLog extensibility - How to add custom field using ExtendValues?

I try to add some custom fields to NLog using extensibility.
Part of my nlog.config file looks like that : (simplified for exhibit)
<nlog>
<extensions>
<add assembly="Logzio.DotNet.NLog"/>
</extensions>
<variable name="currentUser" value="test" />
<targets async="true">
<target name="logzio" type="Logzio" token="myToken">
<contextproperty name="currentUser" layout="${currentUser}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logzio" />
</rules>
</nlog>
In every controller, I have something like that (I'm using ASP.NET MVC5)
private static Logger logger = LogManager.GetCurrentClassLogger();
Then I send my logs to logzio using
logger.Fatal("Something bad happens");
Right now, currentUser always have the value test, which is logical.
However, despite of the documentation, I don't understand how to dynamically change currentUser value by the ID of my current logged user.
Should I create a sort of factory ? (if yes, how ? I'm not at ease with factories)
Should I change my logger variable ? If so, how ?
A piece of code would be extremly welcome.
Thank you for pointing my out where I'm wrong
EDIT
After #Rolf's answer, I've created this custom layout renderer
[LayoutRenderer("custom-layout")]
public class CustomLayoutRenderer : LayoutRenderer
{
public string IdUser { get; set; }
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
logEvent.Properties.Add("currentUser", "HowToPassCustomDataHere?");
builder.Append("test from custom-layout");
}
}
and I changed the nlog.config accordingly, adding
layout="${message} ${custom-layout}"
to my <target>
However, I still don't understand how to pass custom value to currentUser. In logz.io, I have "HowToPassCustomDataHere?" as a value of currentUser.
(BTW, ${aspnet-user-identity} is great and works fine ; however I'd like to understand how to pass a custom value to my layout renderer. In my case, something like ${aspnet-user-id})
You can try one of these NLog layoutrenderers to acquire the current username:
${aspnet-user-identity} Wiki
${windows-identity} Wiki
You can also create your own custom NLog LayoutRenderer: https://github.com/NLog/NLog/wiki/How-to-write-a-custom-layout-renderer
Example of how to provide it as currentUser:
<target name="logzio" type="Logzio" token="myToken">
<contextproperty name="currentUser" layout="${aspnet-user-identity}" />
</target>

Visual Studio (Xamarin): DependencyService + Nuget + iOS = Fail

We are writing a Visual Studio (Xamarin) cross-platform application that will share quite a bit of functionality with the next application we write, so we wanted to put that shared functionality into a "library" so we could test it, share it easily, etc.
The only way we found to write a cross-platform library, is to create it using the standard Xamarin DependencyService paradigm, turn it into a NuGet package, and then load that package into our main app. For better or worse, we did this before Microsoft provided a template for generating NuGet libraries like this, so we had to roll it ourselves.
This works fine for Android, but now I'd like to get the same code working for iOS and it's simply not working. No errors, no warnings, but when I run the app and call
Client = DependencyService.Get<IClient>();
It simply returns null. If I look at the Output/Debug window, it is loading the 'Company.Client.Abstractions.dll' (the root DLL containing the definition of IClient) but not the iOS-specific 'Company.Client.dll' (which contains the iOS-specific implementation of IClient).
Here's my nuspec file:
<?xml version="1.0"?>
<package >
<metadata>
<id>Client</id>
<version>0.0.35</version>
<authors>Me</authors>
<owners>Company</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Stuff</description>
<releaseNotes>Initial release</releaseNotes>
<copyright>Copyright 2017 Company</copyright>
<dependencies>
<group targetFramework="MonoAndroid">
<dependency id="Xamarin.Forms" version="2.3.4.247" />
</group>
<group targetFramework="Xamarin.iOS10">
<dependency id="Xamarin.Forms" version="2.3.4.247" />
</group>
<group targetFramework="uap">
<dependency id="Xamarin.Forms" version="2.3.4.247" />
</group>
</dependencies>
</metadata>
<files>
<!-- Cross-platform reference assemblies -->
<file src="Company.Client.Abstractions\bin\Debug\Company.Client.Abstractions.dll" target="lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Company.Client.Abstractions.dll" />
<file src="Company.Client.Abstractions\bin\Debug\Company.Client.Abstractions.pdb" target="lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Company.Client.Abstractions.pdb" />
<!-- iOS reference assemblies -->
<file src="Company.Client.iOS\bin\iPhone\Debug\Company.Client.dll" target="lib\Xamarin.iOS10\Company.Client.dll" />
<file src="Company.Client.iOS\bin\iPhone\Debug\Company.Client.pdb" target="lib\Xamarin.iOS10\Company.Client.pdb" />
<!-- Android reference assemblies -->
<file src="Company.Client.Android\bin\Debug\Company.Client.dll" target="lib\MonoAndroid10\Company.Client.dll" />
<file src="Company.Client.Android\bin\Debug\Company.Client.pdb" target="lib\MonoAndroid10\Company.Client.pdb" />
<!-- UWP reference assemblies -->
<file src="Company.Client.UWP\bin\Debug\Company.Client.dll" target="lib\UAP10\Company.Client.dll" />
<file src="Nuvectra.Client.UWP\bin\Debug\Company.Client.pdb" target="lib\UAP10\Company.Client.pdb" />
</files>
</package>
I'm thinking there are two classes of possible problems:
I'm generating the DLL wrong
I'm generating the application wrong
Looking at the generated DLLs, the Android DLL is 28k long, the iOS DLL is 23k, so it doesn't look like the iOS DLL is empty. Is there a tool that would let me inspect the iOS DLL and make sure it has the necessary entry point(s)?
The Project that generates the iOS DLL has these settings:
Target framework: Xamarin.iOS
Output type: Class Library
Condition compilation symbols: __ UNIFIED__;__ MOBILE__;__ IOS__
Platform: Active (Any CPU)
The application that is using the DLL has these settings for the iOS Project:
SDK Version: Default
Linker Behavior: Don't Link
Platform: Active (iPhone)
Supported Architectures: ARMv7 + ARM64
Target framework: Xamarin.IOS
Output type: Console Application
Conditional compilation symbols: __ UNIFIED__;__ MOBILE__;__ IOS__
The interface definition in my 'Abstractions' project looks like this:
namespace Company.Client
{
public abstract class IClient
{
void abstract function();
}
}
and the implementation in my iOS project looks like this:
using Company.Client.iOS;
[assembly: Xamarin.Forms.Dependency (typeof (IosClient))]
namespace Company.Client.iOS
{
public class IosClient : IClient
{
void override function();
}
}
We tried many different ways to create a NuGet package with platform-specific code loaded using DependencyService and every single way had the same problem - worked fine under Android, failed under iOS.
The solution is to create a class in the iOS library that is not loaded using DependencyService, and then in the iOS application, create an instance of that class. That's enough to convince the iOS application that it actually needs to load the iOS library DLL, and then the rest of the DependencyService magic works correctly.

References to Separate Projects When Configuring Unity?

I read a really cool blog about using Autofac to completely decouple an application. But try as I might (and being horribly new to all this), I just couldn't get Autofac to gel.
I turned to Unity from the MS Patterns & Practices Enterprise Library and that went a whole lot better. To make things unnecessarily hard for myself, I separated out all my stuff into projects as:
UnityDi (Console app)
UnityDi.Contracts (Interfaces)
UntiyDi.Domain (Classes)
UnityDi.Repositories (Data Access)
UnityDi.Services (Access to repository through a service layer)
I used XML configuration to pony up Unity:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<assembly name="UnityDi.Contracts" />
<assembly name="UnityDi.Domain" />
<assembly name="UnityDi.Services" />
<assembly name="UnityDi.Repositories" />
<namespace name="UnityDi.Contracts" />
<namespace name="UnityDi.Domain" />
<namespace name="UnityDi.Services" />
<namespace name="UnityDi.Repositories" />
<container>
<register type="IUser" mapTo="User"></register>
<register type="IUserService" mapTo="UserService"></register>
<register type="IUserRepository" mapTo="UserRepository"></register>
</container>
</unity>
</configuration>
And got that into a running app, no worries:
private static readonly IUnityContainer Container = new UnityContainer();
...
Container.LoadConfiguration();
BUT in order to do so, I need a reference to all the above projects from my console app.
Is there a way to make the app only ever have a reference to UnityDi.Contracts (the interfaces)? Then the app is well and truly decoupled (admittedly with a sledgehammer).
I hope that is enough of an explanation, I'm totally new to this and I'm being extreme like this to facilitate better learning.
I suspect the reason it looks like you need project references is that without them, VS won't copy the assemblies into your apps bin folder when you hit F5. How would it, it has no way of knowing you need them!
The project references are the quickest solution to the problem. The other thing you could do is add a post-build step to copy the appropriate DLLs to end up in the right directory so you can run the app.

Using ConfigurableActiveDirectoryMembershipProvider in Spring.Net

I want to use ConfigurableActiveDirectoryMembershipProvider in my code. I have my current settings as
<add name="XXXXMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershi pProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="XXXXConnectionString"
connectionUsername="user"
connectionPassword="password"
connectionProtection="Secure" />
I have changed this to
<add connectionStringName=""
name="XXXXDomainADMembershipProvider"
type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web" />
and added in to my spring config file as
<object id="XXXXDomainADMembershipProvider"
type="Spring.Web.Providers.ConfigurableActiveDirec toryMembershipProvider">
<property name="connectionStringName" value="XXXXDomainConnectionString" />
<property name="connectionUsername" value="user" />
<property name="connectionPassword" value="password" />
</object>
But I am getting the following error
Error creating context 'spring.root': Could not load type from string value 'Spring.Web.Providers.ConfigurableActiveDirectoryM embershipProvider'.
I checked the Spring.WebQuickStart source code and the class Spring.Web.Providers.ConfigurableActiveDirectoryMembershipProvider has been commented out.
Is that the reason I am getting the above error?
Yes, I think you are correct. The error you are getting is exactly the error Spring returns when you are trying to configure an object using a type that can not be loaded. For instance if the class does not exist at all, which appears to be the case here.
You can double check if the ConfigurableActiveDirectoryMembershipProvider class exists by using the object browser to explore the Spring.Web.Providers namespace in the Spring.Web assembly you are using in your project.
You are right that the class is commented out in the current state of the trunk code. It has a small TBD note, so I think they are not sure if they want to implement this. But it could be that it wasn't commented out in the version of Spring.Web you are using, so you should still check it using the object explorer.
Strangely enough, the ConfigurableActiveDirectoryMembershipProvideris mentioned in the documentation - you might want to post this on the Spring.Net forum they're likely to help you.

Resources