WCF MessageHeaderArray not working correctly - c#

I have a relatively simple service that takes in a Message contract for uploading a file. Because it is a file upload, only the file stream can be in the message body so I am using the message header for meta data.
My problem is that although I've added a string[] as a MessageHeaderArray when I add a service reference to the service in a test web app client, the variable has been generated as just a string.
Here is part of my MessageContract:
[MessageContract]
public class FileInformation : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; }
[MessageHeaderArray]
public string[] RequiredEntityNames { get; set; }
[MessageHeaderArray]
public string[] RequiredEntityIds { get; set; }
[MessageHeader(MustUnderstand = true)]
public string EntityName { get; set; }
This is driving me mad and I have spent almost a day trying to figure out what's going on. Any ideas on why the RequiredEntityNames and RequiredEntityIds are being generated as string instead of string[]?

Have you tried with "MessageHeader" attribute on arrays instead of "MessageHeaderArray" ?
MSDN, "Using Arrays Inside Message Contracts" : http://msdn.microsoft.com/en-us/library/ms730255.aspx

Related

Making json configs for applications c#

i need a config file for my applications and i've looked through internet without really finding what I want, I want to set my config to a var and use it like config.somethingInTheConfig.
I tried some things but it didn't work,
the config file :
{
"id": 00,
"somethings": true,
"yes": "idkjustsomething"
}
the Config class :
class Config
{
public static int id { get; set; }
public static bool somethings { get; set; }
public static string yes { get; set; }
}
Code to read it
using (StreamReader streamReader = new StreamReader("config.json"))
{
string json = streamReader.ReadToEnd();
Config config = JsonConvert.DeserializeObject<Config>(json);
Console.WriteLine(config.id);
}
I want it to show the id in the config in the console but it doesn't work nd gives me an error, anyone could help ?
The properties are marked static.
JsonConvert will not be able to read values into the static properties.
And since you are not defining values for them at design time, the properties will be set to their default values unless you manually change them.

C# Iterating through list of objects

First post on here so go easy on me!
I'm trying to deserialize a json document into a list of objects.
So far I've got two classes - one which contains the data structure:
class Server
{
public string hostname { get; set; }
public string ipAddress { get; set; }
public string monitoring { get; set; }
public int pollingInterval { get; set; }
}
The other that contains the collection
class ServerCollection
{
public List<Server> servers { get; set; }
}
In the applicaion I do a simple ReadAllText and I'm deserializing the object like so
private ServerCollection _servers;
string json = File.ReadAllText(#"c:\users\admin\desktop\server.monitoring\servers.json");
_servers = JsonConvert.DeserializeObject<ServerCollection>(json);
I'm struggling to iterate over this using a foreach... Can't think what I'm missing.
Severity Code Description Project File Line Suppression State
Error CS1579 foreach statement cannot operate on variables of type 'ServerCollection' because 'ServerCollection' does not contain a public instance definition for 'GetEnumerator' Server.Monitoring.Service.Core C:\Code\Admin\Server.Monitoring\Server.Monitoring.Service.Core\GetSystemHealth.cs 25 Active
Any ideas on what I've missed?
Thanks in advance!
class ServerCollection
{
public List<Server> servers { get; set; }
}
Is not a collection but Servers is, you need to iterate over that.
foreach (var server in _servers.servers)
{
//do something with server
}
Your ServerCollection class does not implement the IEnumerable interface. You have two options:
Have ServerCollection implement that interface (i.e., public class ServerCollection : IEnumerable<Server> and add the necessary methods)
Loop over _servers.servers instead
The second option is the easier one to get you going, but implementing the interface may have other benefits down the road

Is removing a MessageBodyMember in a MessageContract considered a breaking change?

Consider the following ServiceContract-Interface:
[ServiceContract]
public interface ITest
{
[OperationContract]
void MyMethod(MyClass obj);
}
With MyClass beeing:
[MessageContract]
public MyClass
{
[MessageBodyMember(Order = 0)]
public int A { get; set; }
[MessageBodyMember(Order = 1)]
public int B { get; set; }
[MessageBodyMember(Order = 2)]
public int C { get; set; }
}
MyClass is now changed to look like the following:
[MessageContract]
public MyClass
{
[MessageBodyMember(Order = 0)]
public int A { get; set; }
[MessageBodyMember(Order = 2)]
public int C { get; set; }
}
Would a client consuming this WCF-Service need to make additional changes to work with the new service definition?
Additionaly, what would happen if I were to additionally change C to have the new Order = 1?
If the client updates the WSDL File, it gets a syntax error in the code of the client, when the client calls the method.
The order element set on which position in the communication the bodymember sending to the server/client. You can see it into the svc log. Example.:
<ns2: myClass xmlns:ns2="yourNamespace">
<A xmlns=""></A>
<B xmlns=""></B>
<C xmlns=""></C>
</ns2:myClass>
After changed the ordner element:
<ns2: myClass xmlns:ns2="yourNamespace">
<C xmlns=""></C>
<A xmlns=""></A>
<B xmlns=""></B>
</ns2: myClass >
I have tried the example for you. I have used a WCF C# Web service and a C# client with a standard protocol: BasicHttpBinding. And I use the WCF Test client. In this combination I got no error in the client, provided that the client makes no WSDL update. In this case, you can change the order element without errors. But this is not a correct implementation; therefore you can’t assume that everything is working. For other clients see the result might be different. For example, a Java client is much more restrictive.
Adding to #naro's answer:
It is not a breaking change for the following frameworks:
.NET (C#, 4.5)
Java 8
I did not test anything else as it doesn't matter for our scenario.
This means every client still works (without regenerating the client) even after updating the web service definition.

ServiceStack C# strongly typed client DTO

Here: Recommended ServiceStack API Structure and here: https://github.com/ServiceStack/ServiceStack/wiki/Physical-project-structure are recommendations for how to structure your projects for C# clients to reuse DTOs.
Apparently this is done by including a dll of the DTO assembly. I have searched the web for one example, just Hello World that uses a separate assembly DTO for a C# client in ServiceStack. Perhaps I should be able to break this out myself but so far it has not proven that easy.
Almost all client descriptions are for generic and non-typed JSON or other non-DTO based clients. No one appears interested in typed C# clients like I am (even the ServiceStack documentation I have found). So I thought this would be a good question even if I figure it out myself in the end.
To be clear, I have built and run the Hello World example server. I have also used a browser to attach to the server and interact with it. I have also created a client empty project that can call
JsonServiceClient client = new JsonServiceClient(myURL);
Then I tried to copy over my DTO definition without the assembly DLL as I don't have one. I get ResponseStatus is undefined.
Clearly there is something missing (it appears to be defined in ServiceStack.Interfaces.dll) and if I could create a dll of the DTO I think it would resolve all references.
Can anyone give insight into how to create the DTO assembly for the simple Hello World?
Edited to add code:
using ServiceStack.ServiceClient.Web;
namespace TestServiceStack
{
class HelloClient
{ public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Request DTO
public class Hello
{
public string Name { get; set; }
}
HelloResponse response = client.Get(new Hello { Name = "World!" });
}
}
Where the ResponceStatus is undefined.
I was able to find the missing symbol ResponseStatus by adding:
using ServiceStack.ServiceInterface.ServiceModel;
Here is the full code that built. Keep in mind that I found out something else in the process. Once this built it then failed because I was using a DTO from a .NET 4.0 environment in a .NET 3.5 environment. But that is an unrelated issue. Also note that this test code does nothing with the response, it is just an example to get the build working.
using ServiceStack.ServiceClient;
using ServiceStack.ServiceInterface;
using ServiceStack.Text;
using ServiceStack.Service;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost;
using ServiceStack;
using ServiceStack.ServiceClient.Web;
using RestTestRoot; // This is the name of my DTO assembly. You will need to insert your own here.
using ServiceStack.ServiceInterface.ServiceModel;
namespace WebApplicationRoot
{
class HelloClient
{
JsonServiceClient hello_client;
//Request DTO
public class Hello
{
public string Name { get; set; }
}
//Response DTO
public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Can be called via any endpoint or format, see: http://mono.servicestack.net/ServiceStack.Hello/
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
//REST Resource DTO
[Route("/todos")]
[Route("/todos/{Ids}")]
public class Todos : IReturn<List<Todo>>
{
public long[] Ids { get; set; }
public Todos(params long[] ids)
{
this.Ids = ids;
}
}
[Route("/todos", "POST")]
[Route("/todos/{Id}", "PUT")]
public class Todo : IReturn<Todo>
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
public HelloClient(){
// ServiceStack gateway = new ServiceStack.ClientGateway(
// location.protocol + "//" + location.host + '/ServiceStack.Examples.Host.Web/ServiceStack/');
hello_client = new JsonServiceClient("http://tradetree2.dnsapi.info:8080/");
hello_client.Get<HelloResponse>("/hello/MyTestWorld!");
}
}
}

WCF OperationContract and dynamic parameter

I have a WCF Service that based on Writing Highly Maintainable WCF Services. Requests are processed using a CommandService:
[WcfDispatchBehaviour]
[ServiceContract(Namespace="http://somewhere.co.nz/NapaWcfService/2013/11")]
[ServiceKnownType("GetKnownTypes")]
public class CommandService
{
[OperationContract]
public object Execute(dynamic command)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(command);
return command;
}
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var coreAssembly = typeof(ICommandHandler<>).Assembly;
var commandTypes =
from type in coreAssembly.GetExportedTypes()
where type.Name.EndsWith("Command")
select type;
return commandTypes.ToArray();
}
}
Everything works great (thanks Steve) but now I need to add the ability to upload a file to the service. From what I've read and based on errors received during testing, WCF needs to use a [MessageContract] when uploading a file using a Stream. So I've decorated my command class and put the non-Stream members into the message header, and updated my binding definition to use streaming:
[MessageContract]
public class AddScadaTileCommand
{
[MessageHeader(MustUnderstand = true)]
public int JobId { get; set; }
[MessageHeader(MustUnderstand = true)]
public string MimeType { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Content { get; set; }
}
Unfortunately when I call the service with a file to upload I get an error:
There was an error while trying to serialize parameter
http://somewhere.co.nz/NapaWcfService/2013/11:command. The
InnerException message was 'Type 'System.IO.FileStream' with data
contract name
'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not
expected.
So I added a new method to the service specifically for the file upload request:
[OperationContract]
public void Upload(AddScadaTileCommand addScadaTileCommand)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(typeof(AddScadaTileCommand));
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(addScadaTileCommand);
}
This works perfectly, unless I change the AddScadaTileCommand parameter to dynamic in the method definition, in which case I get the same error as above. This appears to indicate that the [MessageContract] attributes are not applied or ignored when using dynamic as the type of the parameter. Is there any way to resolve this or will I need to create separate methods for requests that involve streams?

Categories

Resources