Uno Platform WASM SignalR deserialization issues - signalr

I have a SignalR service and an Uno Platform WASM Client (with Prism). I want to call a hub method, which returns a model. The problem is, i have two identical models (Properties, Methods, etc.) but the client can only receive one of them upon calling the hub methods. With the other model a exception gets thrown on deserialization.
Hub:
using Microsoft.AspNetCore.SignalR;
using ReservationManagement.SignalRInterface.Model;
using System.Threading.Tasks;
namespace ReservationManagement.Service.Hubs
{
public class TestServiceHub: Hub
{
public async Task<LocationModel> LocationModel()
{
return new LocationModel() { Name = "Location" };
}
public async Task<TestModel> TestModel()
{
return new TestModel() { Name = "Test" };
}
}
}
Models:
namespace ReservationManagement.SignalRInterface.Model
{
public class TestModel
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
namespace ReservationManagement.SignalRInterface.Model
{
public class LocationModel
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
ViewModel:
using Microsoft.AspNetCore.SignalR.Client;
using Prism.Commands;
using ReservationManagement.SignalRInterface.Model;
namespace ReservationManagement.UnoPrism.ViewModels
{
class TestViewModel: ViewModelBase
{
private HubConnection HubConnection { get; set; }
public DelegateCommand Loaded { get; private set; }
public LocationModel LocationModel
{
get => _locationModel;
set { SetProperty(ref _locationModel, value); }
}
private LocationModel _locationModel;
public TestModel TestModel
{
get => _testModel;
set { SetProperty(ref _testModel, value); }
}
private TestModel _testModel;
public TestViewModel()
{
Loaded = new DelegateCommand(LoadedExecute);
}
private async void LoadedExecute()
{
HubConnection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/TestServiceHubAnyOrigin")
.WithAutomaticReconnect()
.Build();
await HubConnection.StartAsync();
LocationModel = await HubConnection.InvokeAsync<LocationModel>("LocationModel");
TestModel = await HubConnection.InvokeAsync<TestModel>("TestModel");
}
}
}
The call for LocationModel works fine and it is set to what the service returns. The call for TestModel results in an exception. If i switch the calling order (1. TestModel, 2. LocationModel) the exception will still be thrown on the TestModel-call.
Everything works perfectly fine when i build for Uwp.
Exception:
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'ReservationManagement.SignalRInterface.Model.TestModel'. Path: $ | LineNumber: 0 | BytePositionInLine: 1. ---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'ReservationManagement.SignalRInterface.Model.TestModel'.
I also tried this, as the exception suggests, with a singular parameterized constructor and a parameterized constructor annotated with 'JsonConstructorAttribute', but still the same exception.

This is a generic issue related to the IL Linker step, which removes members that are detected as not used, in some cases incorrectly when reflection is used.
You will need to add linker descriptors to fix this in the LinkerConfig.xml file in the WebAssembly project, likely in the assembly that contains the ReservationManagement namespace.
You can find additional documentation about the linker configuration here.

Related

An error occurred when trying to create a controller of type 'XXXXController'. Make sure that the controller has a parameterless public constructor

I have created a asp.net web api project and implemented the below HTTP GET method in AccountController and the related service method & repository method in AccountService & AccountRepository respectively.
// WEB API
public class AccountController : ApiController
{
private readonly IAccountService _accountService;
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
[HttpGet, ActionName("UserProfile")]
public JsonResult<decimal> GetUserSalary(int userID)
{
var account = _accountService.GetUserSalary(userID);
if (account != null)
{
return Json(account.Salary);
}
return Json(0);
}
}
Service / Business Layer
public interface IAccountService
{
decimal GetUserSalary(int userId);
}
public class AccountService : IAccountService
{
readonly IAccountRepository _accountRepository = new AccountRepository();
public decimal GetUserSalary(int userId)
{
return _accountRepository.GetUserSalary(userId);
}
}
Repository / Data Access Layer
public interface IAccountRepository
{
decimal GetUserSalary(int userId);
}
public class AccountRepository : IAccountRepository
{
public decimal GetUserSalary(int userId)
{
using (var db = new AccountEntities())
{
var account = (from b in db.UserAccounts where b.UserID == userId select b).FirstOrDefault();
if (account != null)
{
return account.Salary;
}
}
return 0;
}
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>();
container.RegisterType<IAccountRepository, AccountRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
But when I invoke the API method GetUserSalary() I get an error saying
An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor.
Check that you did not forget to register Unity IoC container itself:
if you use ASP.NET Framework it could be - Global.asax or Startap.cs (Owin) via UnityConfig.RegisterComponents() method.
if you use ASP.NET Core then in the Startup.cs file (I was unable to find official guides for its configuting)
Your current constructor has parameters (or args if you prefer).
see:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
All you need to do is add a "Parameter-less Constructor" into the controller as well.
public AccountController()
{
}
Parameter-less constructors are usually above the ones that have params, though as far as I am aware this is only due to standards not any actual effect(s) it may cause.
There is also an already existing issue/question similar to this I will link below that may provide further details.
Make sure that the controller has a parameterless public constructor error

Error sending object to .net signalr client

My understanding is that with signalr I can just send objects back and forth. I am trying to set up a .net client to receive notifications that orders have been placed on a web site. I am trying to set up a very simple example so that I understand the concepts. It works great when I am sending a string notification back to the client, but when I try to send an object I get an error:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled by user code
HResult=-2146233088
Message=The best overloaded method match for 'ConsoleHub.Program.DisplayOrder(ConsoleHub.Order)' has some invalid arguments
Source=Anonymously Hosted DynamicMethods Assembly
StackTrace:
at CallSite.Target(Closure , CallSite , Type , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
at ConsoleHub.Program.<Main>b__6(Object o) in c:\Working\OrderNotifier\ConsoleHub\Program.cs:line 23
at Microsoft.AspNet.SignalR.Client.Hubs.HubProxyExtensions.<>c__DisplayClass6`1.<On>b__4(JToken[] args)
at Microsoft.AspNet.SignalR.Client.Hubs.Subscription.OnData(JToken[] data)
at Microsoft.AspNet.SignalR.Client.Hubs.HubProxy.InvokeEvent(String eventName, JToken[] args)
at Microsoft.AspNet.SignalR.Client.Hubs.HubConnection.OnReceived(JToken message)
at Microsoft.AspNet.SignalR.Client.Connection.Microsoft.AspNet.SignalR.Client.IConnection.OnReceived(JToken message)
at Microsoft.AspNet.SignalR.Client.Transports.HttpBasedTransport.ProcessResponse(IConnection connection, String response, Boolean& timedOut, Boolean& disconnected)
InnerException:
My class:
public class Order
{
public int OrderId { get; set; }
public string Name { get; set; }
public string OrderItem { get; set; }
}
My hub:
using Microsoft.AspNet.SignalR.Hubs;
using OrderNotifier.Models;
using System.Linq;
using System.Threading.Tasks;
namespace OrderNotifier.Hubs
{
public class NotifierHub : Hub
{
OrderContext db = new OrderContext();
public void Hello()
{
Clients.Caller.Welcome("hello");
}
}
}
My controller action:
[HttpPost]
public ActionResult Create(Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.SaveChanges();
SendNotifier.SendOrderNotification(String.Format("{0} ordered {1}", order.Name, order.OrderItem), order);
return RedirectToAction("Index");
}
return View(order);
}
SendNotifier - which is a little weird because I am having it send both a string version and an object version for testing:
public class SendNotifier
{
public static void SendOrderNotification(string message, Order order)
{
var context = GlobalHost.ConnectionManager.GetHubContext<NotifierHub>();
context.Clients.All.Notify(message);
context.Clients.All.Order(order);
}
}
And my console application:
using Microsoft.AspNet.SignalR.Client.Hubs;
using OrderNotifier.Models;
using System;
namespace ConsoleHub
{
class Program
{
static void Main(string[] args)
{
var hubConnection = new HubConnection("http://localhost:60692");
var order = hubConnection.CreateHubProxy("NotifierHub");
//
// Set up action handlers
//
order.On("Welcome", message => Console.WriteLine(message));
order.On("Notify", message => Console.WriteLine(message));
order.On("Order", o => DisplayOrder(o));
hubConnection.Start().Wait();
order.Invoke("Hello").Wait();
Console.WriteLine("Initialized...");
Console.ReadLine();
}
public void DisplayOrder(Order o)
{
Console.WriteLine(String.Format("Order object received.../r/nOrderId: {0}/r/nName: {1}/r/nOrderItem: {2}", o.OrderId, o.Name, o.OrderItem));
//Console.WriteLine(o);
}
}
}
If I change the DisplayOrder parameter to be a string it works. I know I could probably manually deserialize it using Json.Net, but my understanding is that I should just be able to work with it as an object and let signalr deserialize. What am I missing?
You're using the dynamic object overload of On. You need to specify the type:
order.On<Order>("Order", DisplayOrder);

ASP.Net (3.5) MVC partial class for rule validation

I have 2 projects. A data project, which contains my database and my Entity Framework model. I have table called 'User', and then have a generated EF class for user.
I am trying to add a partial class:
using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Text;
namespace Data
{
public partial class user
{
public bool isValid
{
get {
return (GetRuleViolations().Count()==0);
}
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
yield break;
}
partialvoid OnValidate(ChangeAction action)
{
if (isValid)
throw new ApplicationException("Rule violation prevents saving");
}
}
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation (string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage = errorMessage;
PropertyName = propertyName;
}
}
}
This is following the MVC 1.0 NerdDinner example. However, I am getting a design time error on the OnValidate method:
partial void OnValidate(ChangeAction action)
{
if (isValid)
throw new ApplicationException("Rule violation prevents saving");
}
No definining declaration found for implimenting declaration of partial method 'void OnValidate(ChangeAction action)'
What am I doing wrong?
There should be another file containing the rest of the user class.
In this file you have to declare that there might be a partial method called OnValidate.
public partial class user
{
partial void OnValidate(ChangeAction action);
}
Some detail:
A partial method needs to have a signature specified somewhere. Once the signature has been defined then an optional implementation can be specified. Read http://msdn.microsoft.com/en-us/library/wa80x488.aspx for more information.

Unity 1.2 Dependency injection of internal types

I have a facade in a library that exposes some complex functionality through a simple interface. My question is how do I do dependency injection for the internal types used in the facade. Let's say my C# library code looks like -
public class XYZfacade:IFacade
{
[Dependency]
internal IType1 type1
{
get;
set;
}
[Dependency]
internal IType2 type2
{
get;
set;
}
public string SomeFunction()
{
return type1.someString();
}
}
internal class TypeA
{
....
}
internal class TypeB
{
....
}
And my website code is like -
IUnityContainer container = new UnityContainer();
container.RegisterType<IType1, TypeA>();
container.RegisterType<IType2, TypeB>();
container.RegisterType<IFacade, XYZFacade>();
...
...
IFacade facade = container.Resolve<IFacade>();
Here facade.SomeFunction() throws an exception because facade.type1 and facade.type2 are null. Any help is appreciated.
Injecting internal classes is not a recommended practice.
I'd create a public factory class in the assembly which the internal implementations are declared which can be used to instantiate those types:
public class FactoryClass
{
public IType1 FirstDependency
{
get
{
return new Type1();
}
}
public IType2 SecondDependency
{
get
{
return new Type2();
}
}
}
And the dependency in XYZFacade would be with the FactoryClass class:
public class XYZfacade:IFacade
{
[Dependency]
public FactoryClass Factory
{
get;
set;
}
}
If you want to make it testable create an interface for the FactoryClass.
If the container creation code is outside the assembly of the internal types, Unity can't see and create them and thus can't inject the dependecies.

IOC with multiple databases that use same interface (StructureMap or any other DI Framework)

We've been experimenting with StructureMap, and I'm having trouble grasping how to handle situations where a single interface has multiple implementations. The code below shows an example where we have two databases that are both accessible from a single service.
public class SomeController : Controller
{
private ISomeService _service;
private IClientRepository _repository;
protected IContext _masterContext;
protected IContext _clientContext;
public SomeController(ISomeService service, ISomeRepository repository
, IContext masterCon, IContext clientCon)
{
_service = service;
_repository = repository;
_masterContext = masterCon;
_clientContext = clientCon;
}
}
public class SomeService : ISomeService
{
private IContext _masterContext;
private IContext _clientContext;
public SomeService(IContext masterContext, IContext clientContext)
{
masterContext = _masterContext;
clientContext = _clientContext;
}
}
public class ClientRepository : IClientRepository
{
private IContext _clientContext;
public ClientRepository(IContext clientContext)
{
_clientContext = clientContext;
}
}
public class MasterContext : IContext
{
public MasterContext(String connString)
//<snip, snip> implement 3rd party data context
}
public class ClientContext : IContext
{
public ClientContext(String connString)
//<snip, snip> implement 3rd party data context
}
StructureMap worked GREAT when we had a single context (database), but how do I tell it how to resolve the 2nd? Note: in most situations we wouldn't have a service handling 2 databases (but may have a controller handling 2 connections, i.e. 2 repositories accessing 2 different databases), but it still doesn't seem to make it easier.
I'm half ready to just give up on using an IoC framework and go back to poor man's DI.
Is it not possible to have an IClientContext and an IMasterContext, possibly inheriting from IContext. My feeling is that the code would be doing one of two very different things depending on whether you were talking to the 'Master' or 'Client' database.
In Unity you can have named registrations, allowing you to effectively register more than a class for a given interface. So you could do (typing by heart, check the actual Unity documentation if interested):
container.RegisterType<IContext, MasterContext>("Master");
container.RegisterType<IContext, ClientContext>("Client");
and then the constructor for SomeService would be:
public SomeService(
[Dependency("Master")]IContext masterContext,
[Dependency("Client")]IContext clientContext)
{
//...
}
The drawback is that in this way your service class is no longer independent of the DI framework used, but depending on the project that may be ok.
This can be a little difficult if you're relying on StructureMap to resolve the dependencies automatically. The first solution (and what I'd err towards) is to make use of marker interfaces like Richard mentions in his answer then just register them. You can then explicitly specify whether you want your client or master context there.
The second way is to make use of named registrations, then specify the constructor params explicitly.
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
});
ForRequestedType<SomeController>().TheDefault.Is.ConstructedBy(
i => new SomeController(i.GetInstance<ISomeService>(),
i.GetInstance<IClientRepository>(),
i.GetInstance<IContext>("Master"),
i.GetInstance<IContext>("Client")));
Not particularly nice but it does the job and ultimately if it's only in one or two places it might be OK.
If you want to resolve differently on namespace / assembly you could try something like this:-
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
}).TheDefault.Is.Conditional(c => {
c.If(con => con.ParentType.Namespace.EndsWith("Client"))
.ThenIt.Is.TheInstanceNamed("Client");
c.If(con => con.ParentType.Namespace.EndsWith("Master"))
.ThenIt.Is.TheInstanceNamed("Master");
c.TheDefault.Is.OfConcreteType<ClientContext>();
});
Where the predicate on ParentType can refer to Assembly (or whatever you want really)
In case someone stumble in this problem, you can achieve it using factory pattern.
Service extension
public static class ServiceFactoryExtensions
{
public static void RegisterSqlFactory(this IServiceCollection serviceCollection)
{
serviceCollection.Configure<MsSqlOption>(option => option.ConnectionString = "Mssql connection string");
serviceCollection.Configure<MySqlOption>(option => option.ConnectionString = "Mysql connection string");
serviceCollection.Configure<PostgreOption>(option => option.ConnectionString = "Postgrel connection string");
serviceCollection.AddSingleton<ISqlDatabase, MsSql>();
serviceCollection.AddSingleton<ISqlDatabase, Postgre>();
serviceCollection.AddSingleton<ISqlDatabase, MySql>();
serviceCollection.AddSingleton<Func<IEnumerable<ISqlDatabase>>>(serviceProvider => () => serviceProvider.GetService<IEnumerable<ISqlDatabase>>());
serviceCollection.AddSingleton<ISqlDatabaseFactory, SqlDatabaseFactory>();
}
}
Factory class
public class SqlDatabaseFactory : ISqlDatabaseFactory
{
private readonly Func<IEnumerable<ISqlDatabase>> _factory;
public SqlDatabaseFactory(Func<IEnumerable<ISqlDatabase>> factory)
{
_factory = factory;
}
public ISqlDatabase CreateSql(SqlType sqlType)
{
var databases = _factory();
var sqlDatabase = databases.FirstOrDefault(x => x.DatabaseName == sqlType);
if (sqlDatabase == null)
throw new NotImplementedException($"Sql type {nameof(sqlType)} is not implemented");
return sqlDatabase;
}
}
Sql classes
public class MsSql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MsSql;
public string Connecionstring { get; private set; }
public MsSql(IOptions<MsSqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class Postgre : ISqlDatabase
{
public SqlType DatabaseName => SqlType.Postgre;
public string Connecionstring { get; private set; }
public Postgre(IOptions<PostgreOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class MySql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MySql;
public string Connecionstring { get; private set; }
public MySql(IOptions<MySqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public interface ISqlDatabase
{
string Connecionstring { get; }
SqlType DatabaseName { get; }
}
public enum SqlType
{
MsSql,
Postgre,
MySql
}
Usage
internal class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.RegisterSqlFactory();
var provider = serviceCollection.BuildServiceProvider();
var sqlFactory = provider.GetService<ISqlDatabaseFactory>();
var mySql = sqlFactory.CreateSql(SqlType.MySql);
var msSql = sqlFactory.CreateSql(SqlType.MsSql);
var postgre = sqlFactory.CreateSql(SqlType.Postgre);
Console.WriteLine($"Database Type : {mySql.DatabaseName}, Connectionstring: {mySql.Connecionstring}");
Console.WriteLine($"Database Type : {msSql.DatabaseName}, Connectionstring: {msSql.Connecionstring}");
Console.WriteLine($"Database Type : {postgre.DatabaseName}, Connectionstring: {postgre.Connecionstring}");
Console.ReadKey();
}
}
Output
Dependencies:
.Net Core 3.1
Microsoft.Extensions.DependencyInjection;
Microsoft.Extensions.Options;
System
System.Collections.Generic
System.Linq;

Resources