I am building a windows service that will act as a client to an existing site that's employing SignalR.
I have IHubProxy.On<> and IHubProxy.Invoke methods working when passing around concrete classes.
For example, this works:
hubProxy.On<MigrationRequest>("iSWindowsServiceRequest", request =>
MigrateData.Invoke(request));
And the MigrationRequest looks like this:
public class MigrationRequest : IISWindowsServiceRequest
{
public MigrateWhat What { get; set; }
public MigrationFor For { get; set; }
public Guid EntityFor_Id { get; set; }
}
Now, If i try this:
hubProxy.On<IISWindowsServiceRequest>("iSWindowsServiceRequest", request =>
Handshake.HandleRequest(request));
my request is never picked up.
what I was hoping to achieve was creating single pub-sub methods, rather than one for each concrete class that this service would accept.
Is it possible to pass in an interface into the On<>() methods?
The same goes for the Invoke<>() - if the object I am passing contains any properties that are of an interface, the call never makes it.
so this will not work:
public class ISWindowsServiceResponse
{
public IISWindowsServiceRequest OriginalRequest { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
but this will
public class ISWindowsServiceResponse
{
public MigrationRequest OriginalRequest { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
Where I work, we spent a lot of time trying to figure out a way to get SignalR hubs to serialize interfaces. In the end, we wound up having to extract real objects (real classes and structs, not behind an interface) to send over the wire. There is no way to tell SignalR how to serialize an interface.
Related
I have two classes below, Form and FormFieldOption<T>. The latter is intended to represent a set of <option> inside of a <select> in a web form that the Form object represents.
public class Form
{
public int Id { get; set; }
public FormFieldOption<string> Status { get; set; }
public FormFieldOption<string> Category { get; set; }
public FormFieldOption<int> Severity { get; set; }
}
public class FormFieldOption<T>
{
public int Id { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
public T Value { get; set; }
}
I'm working on configuring these entities for Code-First like below:
public class FormFieldOptionStringConfiguration : EntityTypeConfiguration<FormFieldOption<string>>
{
public FormConfiguration()
{
//configure here
}
}
Unfortunately at this point I've discovered that I would like there to be a difference between the configuration for the Status and Category properties. I've not been able to figure out a way top configure the properties that utilize the FormFieldOption class individually. How could I do this?
Solutions I've come up with:
Create a new class that inherets FormFieldOption<T> for each property and configure individually.
Create an interface IFormFieldOption<T> and implement classes individually for each use.
I don't particularly care for either of these options as it feels like a lot of duplicated code just to create a different configuration for each table.
It turns out that you can't do this. What I ended up doing was creating a generic interface that I implemented for each property.
Could someone explain why this is happening, I have a C# backend that I'm connecting to via WCF. In the back end, i have two classes in the same namespace that have two properties that have the same name. These classes are used in a separate object. The types of the properties are different, one is a string and one is an object but there seems to be some sort of collision when deserializing the object?
It's returning this random error when I call to return the object.
This could be due to the service endpoint binding not using the HTTP
protocol. This could also be due to an HTTP request context being aborted by
the server (possibly due to the service shutting down). See server logs for
more details.
Here are the classes, the property causing the problem is BCIssued
public class Activities
{
public string ApplicationReceived { get; set; }
public string PIMGranted { get; set; }
public Bcgranted[] BCGranted { get; set; }
public object CCCGranted { get; set; }
// public object BCIssued { get; set; }
public object CCCIssued { get; set; }
}
public class CCC
{
public string BCIssued { get; set; }
public string FinalIns { get; set; }
public string LapsedMonths { get; set; }
public object WorkStarted { get; set; }
public object Notified { get; set; }
public object Lapsed { get; set; }
public object Extension { get; set; }
}
Thanks to Rene's post about WCF logging, i was able to turn on logging and found the error on the server side
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract
which is not supported. Consider modifying the definition of collection
'Newtonsoft.Json.Linq.JToken' to remove references to itself.
This question is very similar to this one: Not all parameters in WCF data contract make it through the web service call. However, the answer provided there did not solve my problem.
This is my service-code:
using System.Runtime.Serialization;
using System.ServiceModel;
namespace SoapUiToWcf
{
[ServiceContract]
public interface IWcfTest
{
[OperationContract]
WcfData ReturnRequest(WcfData wcfData);
}
[DataContract]
public class WcfData
{
[DataMember(Order = 1)]
public WcfDataNested NestedData { get; set; }
public class WcfDataNested
{
[DataMember(Order = 2)]
public string Foo { get; set; }
[DataMember(Order = 3)]
public string Bar { get; set; }
}
}
public class WcfTest : IWcfTest
{
public WcfData ReturnRequest(WcfData wcfData)
{
return wcfData;
}
}
}
The service should return the identical data which was sent to it. When i send a request through SoapUI with the data below, everything works fine and i get all the values back which have been sent:
<soapenv:Envelope><soapenv:Header/><soapenv:Body><tem:ReturnRequest><tem:wcfData>
<soap:NestedData>
<soap:Bar>bar</soap:Bar>
<soap:Foo>foo</soap:Foo>
</soap:NestedData>
</tem:wcfData></tem:ReturnRequest></soapenv:Body></soapenv:Envelope>
but if i swap the Bar- and Foo-fields in the request, the wcf-service only receives the foo-value, the other one is null:
<a:Bar i:nil="true"/>
<a:Foo>foo</a:Foo>
EDIT
Screenshot 1 (request and respone in SoapUI):
Screenshot 2 (Debugging in Visual Studio):
How do i have to implement the wcf-service in order not to be dependent of the sort-order of the fields in the request?
WCF wants you to send them in Order. If you don't specify an order, it defaults to some complex rules that end at alphabetical. I would think that when you remove order, it would demand Bar first. The fact that it didn't might mean something was not refreshed before it was called by SoapUI. Take a look at this other thread: https://stackoverflow.com/a/2520005/2540156
If you simply remove the Order property from the DataMember decorators on your WcfData object, it shouldn't require any certain order.
[DataContract]
public class WcfData
{
[DataMember]
public WcfDataNested NestedData { get; set; }
public class WcfDataNested
{
[DataMember]
public string Foo { get; set; }
[DataMember]
public string Bar { get; set; }
}
}
EDIT: From the sound of this question, it appears that if you don't serialize alphabetically (an XML convention), the fields that are not in order may be discarded when WCF deserializes the class.
I'm currently learning about various application design approaches and there's one thing I can't really get my head around.
Let's say I have a physical device in my network, an IP surveillance camera. I need a model that represents 'IP surveillance cameras' to work with them in my application, which might look like this:
public class IPCamera {
public string Token { get; set; }
public IPAddress IPAddress { get; set; }
public string RtspUri { get; set; }
}
Now, if I want to store the IPCamera into a database (e.g. with Entity Framework), I need maybe other/additional properties and thus, another model with:
public int Id { get; set; }
Oh.. I want to access my application with via a WCF service. I can't use the object "IPAddress" here because it's not very serialization friendly, hence I need another model with a custom IP class that stores the IP address as string.
And last, an Android clients wants to access my data. I design a RESTful API using WebApi2. The client isn't interested in the cameras RTSPUri, but wants to know about the current state (is online, is offline).
I'm wondering: How to name all these models that they don't come in conflict? Should I design a model per each purpose/layer/service? And how to link/map them? With the adapter pattern?
I would include everything in your entity and then create view models that only expose the properties that matter to the domain you're accessing your entities through.
Your entity:
public class IpCamera
{
public int Id { get; set; }
public string Token { get; set; }
public IPAddress IPAddress { get; set; }
public string RtspUri { get; set; }
public bool IsOnline { get; set; }
}
In your WCF service:
public class IpCameraViewModel
{
public int Id { get; set; }
public string IpAddress { get; set; }
public string Token { get; set; }
public string RtspUri { get; set; }
}
In your api project:
public class IpCameraViewModel
{
public int Id { get; set; }
public string IpAddress { get; set; }
public string Token { get; set; }
public bool IsOnline { get; set; }
}
And you can just set the IpAddress as a string to send to a receiving client. You can shed away any properties you don't want to expose. Or you can add properties that don't belong to the IpCamera entity and just add them to your view model from another entity.
As #Smith.h.Neil suggested, you should create one base normalized model (entity if you will) to store and several view models (projections).
You can easily map to/from view models using tools like AutoMapper.
As for the naming, I wouldn't use technical suffixes (like *ViewModel) in API layer. Think hard and figure out correct domain (business oriented) name of each projection. Like IpCameraStatus or perhaps just IpCamera (but in another namespace).
I have a class and there are some nested classes within it. I serialize it and send it to the wcf service using a method with no problems. Here's the class;
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
}
Here's the method;
public void Check()
{
Computer c = new Computer();
ISystemInfoOperations cli = GetServiceClient();
Mapper.CreateMap<Monitor, MonitorDTO>();
Mapper.CreateMap<IHardwarePart, IHardwarePartDTO>();
Mapper.CreateMap<Computer, ComputerDTO>()
.ForMember(s => s.Hardware, m => m.MapFrom(q => Mapper.Map<List<IHardwarePart>, List<IHardwarePartDTO>>(q.Hardware)));
Mapper.AssertConfigurationIsValid();
ComputerDTO dto = Mapper.Map<Computer, ComputerDTO>(c);
string sendComputerInfo = cli.SendComputerInfo(dto);
}
But I have also a list of interface to be sent. So I change the code like below and get an error.
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
[DataMember]
public List<IHardwarePartDTO> Hardware { get; set; }
}
public interface IHardwarePartDTO
{
[DataMember]
string Name { get; set; }
[DataMember]
HardwarePartTypeDTO PartType { get; set; }
}
Inside of hardware is getting filled in the project. But if I try to send it, I get this famous error :
Type
'Proxy'
with data contract name
'_x0030__Culture_x003D_neutral_PublicKeyToken_x003D_null_x003E_:http://schemas.datacontract.org/2004/07/Proxy%3CSystemInfo.DTO.IHardwarePartDTO_SystemInfo.DTO_Version=1.0.0'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
The DataContractSerializer needs to know about the concrete types that is might return. An interface cannot be serialized, as it cannot be deserialized (how can you create an instance of an interface without a concrete implementation).
The simple resolution is to add KnownTypes attribute like below:
[KnownType(typeof(your hardware dto concrete type here))]
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
[DataMember]
public List<IHardwarePartDTO> Hardware { get; set; }
}
You can add as many known type attributes as you like.
Slightly more complex is the ServiceKnownTypes attribute. This is very similar but you would add it to your service class.
Other than that you can use a data contract resolver - but this is very complicated and would take a while to explain.
EDIT: 18/02/2013 15:11
You may also need to look at you Automapper as its currently going to create proxies in your Hardware list - and proxies cannot be serialized. You need to tell automapper what to serialize them to - for example:
Mapper.CreateMap<Monitor, MonitorDTO>();
Mapper.CreateMap<Monitor, IHardwarePartDTO>().As<MonitorDTO>();
Mapper.CreateMap<Audio, AudioDTO>();
Mapper.CreateMap<Audio, IHardwarePartDTO>().As<AudioDTO>();
Mapper.CreateMap<CDROMDrive, CDROMDriveDTO>();
Mapper.CreateMap<CDROMDrive, IHardwarePartDTO>().As<CDROMDriveDTO>();
//you need entries like these for everythin that implements IHardwarePartDTO
This way automapper knows what it needs to create.