Argument 1: cannot convert from 'Callbacks.OnConsoleLogHandler' to 'Kotlin.Jvm.Functions.IFunction2' - xamarin

I created a binding library, via a .arr file.
And in one of the methods I need to pass a callback.
What I was trying to do:
this is my delegate:
public class Callbacks
{
public delegate void OnConsoleLogHandler(string message, Sometype type);
}
this is my Configuration class:
public class Configuration
{
public Callbacks.OnConsoleLogHandler onConsoleLog;
public Configuration()
{
}
public Configuration(Action<Configuration> action)
{
var config = new Configuration();
action?.Invoke(config);
onConsoleLog = config.onConsoleLog;
}
}
here's how I'm trying to call a native method:
public static void Start(Configuration configuration)
{
var onConsoleLog = configuration.onConsoleLog;
Package.Name.Configuration config = new Package.Name.Configuration();
config.SetOnConsoleLog(onConsoleLog);
}
But on this row config.SetOnConsoleLog(onConsoleLog);
I get error:
Argument 1: cannot convert from 'Callbacks.OnConsoleLogHandler' to 'Kotlin.Jvm.Functions.IFunction2'
If I try something like this:
config.SetOnConsoleLog(() => { });
I get:
Cannot convert lambda expression to type 'IFunction2' because it is not a delegate type
How can I put the delegate as a callback? Any suggestions?

Related

Setting [BindNever] during the action execution filter flow

Does anyone know how I can mark an argument on ActionDescriptor.Parameters to behave in a similar way the [BindNever] is behaving?
I want to always exclude a specific argument from a specific type without keep decorating it on the Controller.
Essentially I would like to be able to add my injected to my functions somehow how similar to the way its done with CancellationToken
public class TestController : ControllerBase
{
[HttpGet(Name = "Get")]
public IActionResult Get([BindNever] IInjectedInterface injected)
{
//Injected can be used in this method
return Ok();
}
[HttpPost(Name = "Post")]
public IActionResult Post([BindNever] IInjectedInterface injected, FormModel formModel)
{
//Injected doesn't work here. There is an error that
/*System.InvalidOperationException: 'Action 'WebApplication3.Controllers.TestController.Post (WebApplication3)'
has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body.
Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route,
and 'FromBodyAttribute' for parameters to be bound from body:
IInjectedInterface injected
FormModel formModel'
*/
return Ok();
}
}
public class ActionExecutionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var injectedParam = context.ActionDescriptor.Parameters.SingleOrDefault(x => x.ParameterType == typeof(IInjectedInterface));
if (injectedParam != null)
{
context.ActionArguments[injectedParam.Name] = new Injected(99);
}
await next.Invoke();
}
private class Injected : IInjectedInterface
{
public Injected(int someData)
{
SomeData = someData;
}
public int SomeData { get; }
}
}
I was able to solve it. Apparently you need to add the following lines on your program.cs to avoid the model binder related errors.
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(IInjectedInterface)));
options.ModelMetadataDetailsProviders.Add(
new BindingSourceMetadataProvider(typeof(IInjectedInterface), BindingSource.Special));

Xamarin Forms with WorkManger

I am trying to use the Xamarin implementation of WorkManager, in the nuget package Xamarin.Android.Arch.Work.Runtime.
The questions is: how to pass "complex" parameters to the worker class?
I have a Xamarin Forms application with DI and others classes, but the job only receives a Java.Lang.Object.
My code:
// Method to Schedule the Job
// See the dataParam? That line throw an exception
// I can manage to pass a simple string or int to the job this way, but not
// complex classes
public void ScheduleAppJobs(IContainerProvider containerRegistry)
{
//here the code throw an exception
var dataParam = new Data.Builder().Put("param", new JobParameter());
var syncWorkerReuest = PeriodicWorkRequest.Builder.From<SyncChecklistWorker>(TimeSpan.FromMinutes(5))
.SetInputData(dataParam.Build())
.Build();
WorkManager.Instance.Enqueue(syncWorkerReuest);
}
//this was my try to create a custom class and populate with my objects
//But didn't worked
public class JobParameter : Java.Lang.Object
{
}
//my job implementation
public class SyncChecklistWorker : Worker
{
public SyncChecklistWorker(Context context, WorkerParameters workerParameters) : base(context, workerParameters)
{
}
public override Result DoWork()
{
if (InputData.KeyValueMap.TryGetValue("param", out Java.Lang.Object #object))
{
var jobParam = (JobParameter)#object;
// here I would like to get my DI container to resolve services and execute business logic
// var diResolver = jobParam.GetDIContainer();
return Result.InvokeSuccess();
}
return Result.InvokeRetry();
}
}
}
The problem is:
The only way to pass input data to a jobs the Data.Builder only accepts Java.Lang.Object. Even trying to approach of having JobParameter : Java.Lang.Object I get the following error when trying to execute the: new Data.Builder().Put("param", new JobParameter()); Error: Java.Lang.IllegalArgumentException: 'Key param has invalid type class crc648d221dddf00bc7fb.JobParameter'
On the official Microsoft Docs the FireBase Job Dispatcher nuget is deprecated. So how to work with the new WorkManager one?
FireBase Job Dispatcher Doc:
https://learn.microsoft.com/en-us/xamarin/android/platform/firebase-job-dispatcher
Deprecated nuget:
https://www.nuget.org/packages/Xamarin.Firebase.JobDispatcher
Any idea of how to solve this?
Work request from Data builder accepts only the premitive types. You can pass the object by serializing to JSON string format and in DoWork() you can deserialize it.
public void ScheduleAppJobs(IContainerProvider containerRegistry)
{
var dataParam = new Data.Builder().PutString("param",serializeToJson(new
MyClass()));
var syncWorkerReuest = PeriodicWorkRequest.Builder.From<SyncChecklistWorker>
(TimeSpan.FromMinutes(5))
.SetInputData(dataParam.Build())
.Build();
WorkManager.Instance.Enqueue(syncWorkerReuest);
}
public override Result DoWork()
{
var jsonString = InputData.GetString("param");
var myClassObj = deserializeFromJson(jsonString );
}
using Newtonsoft.Json;
public string serializeToJson(MyClass myClassObj)
{
var resultString = JsonConvert.SerializeObject(myClassObj);
return resultString;
}
// Deserialize to single object.
public MyClass deserializeFromJson(string jsonString)
{
var serializer = new JsonSerializer();
var resultObject = serializer.Deserialize<MyClass>(jsonString);
}

Structure Map parameterless constructor error

I am trying to set up structure map ver 3.0.5.0 with Web API 2.
I have followed this implementation: Configuring dependency injection with ASP.NET Web API 2.1
However, I am getting this error when doing a get against my ComplexesController:
An error occurred when trying to create a controller of type 'ComplexesController'. Make sure that the controller has a parameterless public constructor.
Can anyone see what is wrong with my structuremap config? The Create method never gets called.
This is my implementation:
public class StructureMapControllerActivator : IHttpControllerActivator
{
private readonly IContainer _container;
public StructureMapControllerActivator(IContainer container)
{
if (container == null) throw new ArgumentNullException("container");
_container = container;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
try
{
var scopedContainer = _container.GetNestedContainer();
scopedContainer.Inject(typeof(HttpRequestMessage), request);
request.RegisterForDispose(scopedContainer);
return (IHttpController)scopedContainer.GetInstance(controllerType);
}
catch (Exception e)
{
// TODO : Logging
throw e;
}
}
}
This method is in my startup...
public void InitializeContainer()
{
// STRUCTURE MAP
Container container = new Container();
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));
container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());
}
.. and this is the controller:
public class ComplexesController : ApiController
{
private IComplexRepository _repo;
public ComplexesController(IComplexRepository repo)
{
_repo = repo;
}
// GET: api/Complexes
public IList<Complex> GetComplexes()
{
var complexes = _repo.GetList();
return complexes;
}
...
My full Startup class
[assembly: OwinStartup(typeof(AngularJSAuthentication.API.Startup))]
namespace AngularJSAuthentication.API
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
The problem here is that you are registering your service activator with a GlobalConfiguration object and not your HttpConfiguration object. In this scenario The GlobalConfiguration object is never used as it is replaced by the HttpConfiguration object. In order to solve your issue you should replace your InitializeContainer() method with the following.
public void InitializeContainer(HttpConfiguration config)
    {
        // STRUCTURE MAP
        Container container = new Container();
        config.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));
        container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
        container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());        
    }
you should then pass the HttpConfiguration object from your Startup class to the new InitializeContainer() method.
Hope this helps.
-B
I am trying to gain a solid understanding of the complete lifecycle. I think my setup may be slightly different to the above. Here is what worked for me.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
var container = IocConfig.Setup();
// Allow a controller to be declared without a parameterless constructor
config.DependencyResolver = new DependencyResolver(container);
config.Services.Add( typeof(IExceptionLogger), new GlobalExceptionLogger( container.GetInstance<ILoggingService>()));
// Web API routes
config.MapHttpAttributeRoutes();
// Setup Authentication
ConfigureOAuth(app, container);
var corsOptions = CorsOptions.AllowAll;
app.UseCors(corsOptions);
// Add ASP.Net Web API to OWIN pipeline
app.UseWebApi(config);
}
}
It worked after I added this line:
// Allow a controller to be declared without a parameterless constructor
config.DependencyResolver = new DependencyResolver(container);
You have to get that my var container loads from a static class called IocConfig with a static Setup method. This is where the interfaces are mapped to their concrete implementations.
Also, you can probably ignore the GlobalExceptionLogger line if you want to use my complete example.

How to invoke TraceListener.Write(object) through TraceSource

We have a custom TraceListener implementation which only logs when a specific object (LogMessage) is received. This all works well when using directly with the Trace.Write(object) method.
Due to Performance reason, I want to separate the Listener, so all non-relevant Trace messages are not passed to the listener. Therefore I created a specific TraceSource whith only this listener attached.
Now I struggle to pass my custom log object (LogMessage) to the listener using the TraceSource. The TraceSource.TraceData(TraceEventType, int, object) always invokes the TraceListener.Write(string) method, not the TraceListener.Write(object) method.
Is there any way I can pass the custom object to the Listener using the TraceSource?
Sample code:
using System.Diagnostics;
namespace Sample
{
public class LogMessage
{
public byte[] Data { get; set; }
//...
}
public class Sample
{
public void Foo()
{
var ts = new TraceSource("Test");
var lm = new LogMessage();
//lm.Data = ...;
//this works: calls the Write(object) method in listener
Trace.Write(lm);
//this doesn't work: calls the Write(string) method in listener
ts.TraceData(TraceEventType.Information, 0, lm);
}
}
public class MyListener : TraceListener
{
public override void Write(string message)
{
//not in use
}
public override void WriteLine(string message)
{
//not in use
}
public sealed override void Write(object o)
{
if (o is LogMessage)
{
//do someting with the LogMessage
}
}
}
}
Thanks
Thomas
maybe it's too late for an answer but anyway :
By using a tool like JustDecompile you can easily see that TraceSource.TraceData uses TraceListener.TraceData method which itself basically calls WriteLine with object.ToString() for message.
So you'll have to override the ToString method for your class LogMessage in order to do as you want.

Change ProfileProvider connectionstring on the fly

If you read my previous post, you know I'm able to use a custom MembershipProvider and RoleProvider to use different datasource on each call on the fly. I want to to the same thing with Profile.
My profile's propteries are not store in the web config but in a custom class like this :
public class AccountProfile : ProfileBase
{
public override SettingsProviderCollection Providers
{
get
{
return base.Providers;
}
}
static public AccountProfile GetUserProfile(string userName)
{
return (AccountProfile)(ProfileBase.Create(userName));
}
[SettingsAllowAnonymous(false)]
public string MobilePhone
{
get { return ((string)(base["MobilePhone"])); }
set { base["MobilePhone"] = value; Save(); }
}
}
also like for the Membership and RoleProvider I have a class like this :
public class MyProfileProvider : SqlProfileProvider
{
public MyProfileProvider()
{
}
public MyProfileProvider(string SubDomainInstanceName)
{
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
ProfileSection section = (ProfileSection)config.GetSection("system.web/profile");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
Initialize(section.DefaultProvider, membershipParams);
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
if (!string.IsNullOrEmpty(instance))
{
// Update the private connection string field in the base class.
string connectionString = "";//my connection
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
}
the difference betwen my CustomProfileProvider is that I can't use itself because the "create" method is in the ProfileBase. And with ILSpy I have seen a singleton and I wonder if it's not the source of the problem.
The issue is that I only pass one time in the initialize method. I can't do another time to change the datasource.
I hope you can understand my poor english and can help me.
I find a - bad - solution
In the CustomProfileBase class I add some code to change the connectionstring of the singleton instance of the class.
public class AccountProfile : ProfileBase
{
string connectionString = myconnstring;
//1st call not really use but allow to get an instance of my custom AccountProfile
AccountProfile test = (AccountProfile)(ProfileBase.Create(userName));
//change connectionstring oh the instance
FieldInfo connectionStringField = test.Providers["AspNetSqlProfileProvider"].GetType().BaseType.GetField("_sqlConnectionString", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
connectionStringField.SetValue(test.Providers["AspNetSqlProfileProvider"], connectionString);
//new call on the good datasource
return (AccountProfile)AccountProfile.Create(userName);
}
It's not the most beautifull solution by it's work.
What do you think of t ?

Resources