I am creating a client for a server api I can't change. My client currently produces this format:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<StoreSomething xmlns="urn:aaa">
<Something>
<SomeText xmlns="urn:bbb">My text</SomeText>
</Something>
</StoreSomething>
</s:Body>
</s:Envelope>
However, the server expects Something to be in the urn:bbb namespace (i.e. move the xmlns attribute one level up). How can I achieve this?
OperationContractAttribute has no Namespace property.
Code:
using System;
using System.ServiceModel;
using System.Xml.Serialization;
[XmlType(Namespace="urn:bbb")]
public class Something
{
public string SomeText { get; set; }
}
[XmlSerializerFormat]
[ServiceContract(Namespace="urn:aaa")]
public interface IMyService
{
[OperationContract]
void StoreSomething(Something Something);
}
class Program
{
static void Main(string[] args)
{
var uri = new Uri("http://localhost/WebService/services/Store");
var factory = new ChannelFactory<IMyService>(new BasicHttpBinding(), new EndpointAddress(uri));
IMyService service = factory.CreateChannel();
service.StoreSomething(new Something
{
SomeText = "My text"
});
}
}
I managed to get it working by using unwrapped messages. This unfortunately results in both the method name and parameter name being left out. I therefore had to create wrapper classes which results in convoluted code.
Anyway, here is the code that made it work:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[XmlSerializerFormat]
void StoreSomething(StoreSomethingMessage message);
}
[MessageContract(IsWrapped=false)]
public class StoreSomethingMessage
{
[MessageBodyMember(Namespace="urn:aaa")]
public StoreSomething StoreSomething { get; set; }
}
[XmlType(Namespace="urn:bbb")]
public class StoreSomething
{
public Something Something { get; set; }
}
public class Something
{
public string SomeText { get; set; }
}
I also created a MyServiceClient which implements IMyService and inherits from ClientBase<IMyService> since IMyService now requires a StoreSomethingMessage object, but i left that part out for simplicity.
I wish there was a simpler solution.
Related
It seems like the IAppSettings implementation was not ready from IoC in the constructor.
Before I go into details, I've read similar problems:
ServiceStack doesn't auto-wire and register AppSettings
Instantiation of POCO objects with ServiceStack's IAppSettings is not working
Both were answered by #mythz that he was not able to reproduce it.
From the Doc
"ServiceStack made AppSettings a first-class property, which defaults to looking at .NET's App/Web.config's.": https://docs.servicestack.net/appsettings#first-class-appsettings
And there is default IoC registration already in Funq to give you AppSettings when you ask for IAppSettings:
What I have
All my codes are in the repo: https://github.com/davidliang2008/MvcWithServiceStack
The demo app is just an ASP.NET MVC app (.NET 4.8) that built using the template, the simplest you can get, with ServiceStack (5.12.0) installed:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
new AppHost().Init();
}
}
public class AppHost : AppHostBase
{
public AppHost() : base("MvcWithServiceStack", typeof(ServiceBase).Assembly) { }
public override void Configure(Container container)
{
SetConfig(new HostConfig
{
HandlerFactoryPath = "api";
}
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}
}
Then I have a base class for the ServiceStack Service, and a HelloService just to demo:
public abstract class ServiceBase : Service { }
public class HelloService : ServiceBase
{
public IAppSettings AppSettings { get; set; }
public object Get(HelloRequest request)
{
return new HelloResponse
{
Result = $"Hello, { request.Name }! Your custom value is { AppSettings.Get<string>("custom") }."
};
}
}
[Route("/hello/{name}")]
public class HelloRequest : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
What works
When you're not using IAppSettings in the constructor, whether in the HelloService or its base class ServiceBase, everything works out fine.
When you clone the project to your local, if you navigate to /api/hello/{your-name}, you will see its response would be able to get the custom value from web.config:
What doesn't work
When you're trying to get the IAppSettings and initialize something else with some app setting values in the constructor - whether it's in the child class or the base class, IAppSettings will fail to get the implementation from IoC, and result a NULL reference exception:
public abstract class ServiceBase : Service
{
public IAppSettings AppSettings { get; set; }
public ServiceBase()
{
// AppSettings would be NULL
var test = AppSettings.Get<string>("custom");
}
}
OR
public class HelloService : ServiceBase
{
public HelloService()
{
// AppSettings would be NULL
var test = AppSettings.Get<string>("custom");
}
}
You cannot use any property dependency in the constructor since the properties can only be injected after the class is created and the constructor is run.
You'll only be able to access it in the Constructor by using constructor injection, e.g:
public class HelloService : ServiceBase
{
public HelloService(IAppSettings appSettings)
{
var test = appSettings.Get<string>("custom");
}
}
Or accessing the dependency via the singleton:
public class HelloService : ServiceBase
{
public HelloService()
{
var test = HostContext.AppSettings.Get<string>("custom");
}
}
I have got a service class that has a constructor injection that takes IAppConfig. IAppConfig has couple of properties with just getters. And I would like to create an instance of this service class in my test project.
My Service class
IAppConfig _appConfig;
public class PeopleService(IAppConfig appConfig)
{
_appConfig = appConfig;
}
AppConfig Interface
public interface IAppConfig
{
string BaseURL {get;}
string AnotherProperty {get;}
}
How do I use NSubstitute to mock the IAppConfig to create an instance of PeopleService.
I have tried the below code, but the properties that I set are empty string.
var _appConfig = Substitute.For<IAppConfig>();
_appConfig.BaseURL.Returns("http://localhost");
new PeopleService(_appConfig);
But the property set in the _appConfig is not working. I appreciate if someone can help me.
Showing this since this is too much to put in a comment.
This simplified example shows that the framework works as expected and that you need to clarify your actual issue better.
[TestClass]
public class MyTestClass {
[TestMethod]
public void NSubstitute_Mocking_ReadOnly_Properties_Works() {
//Arrange
var expected = "http://localhost";
var _appConfig = Substitute.For<IAppConfig>();
_appConfig.BaseURL.Returns(expected);
var subject = new PeopleService(_appConfig);
//Act
var actual = subject.URL;
//Assert
actual.Should().Be(expected);
}
}
class PeopleService {
IAppConfig _appConfig;
public PeopleService(IAppConfig appConfig) {
_appConfig = appConfig;
}
public string URL => _appConfig.BaseURL;
}
public interface IAppConfig {
string BaseURL { get; }
string AnotherProperty { get; }
}
The above example passed when tested.
I have a Windows service that uses net.pipe as a WCF server and a Windows forms client to connect to it. There is a class called ConfigSettings that has values I want the client to query.
I want to have the client read the current values inside the serviceConfig instance that the service uses. Ultimately, I want the client to change values in it, but baby steps first.
The form can talk to the server via named pipes, but 'return serviceConfig is sending a new empty instance back to the client. I want the data that the service is actively using (that is, serviceConfig.Setting1 = x; serviceConfig.Setting2 = "foo"; )
The Windows service and WCF server code is (updated to working version):
using System.IO;
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
//this is the master config that the service uses
public static ConfigSettings serviceConfig = new ConfigSettings();
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1));
myServiceHost.Open();
//set active default settings
serviceConfig.Setting1 = 1;
serviceConfig.Setting2 = "initial.Setting2:" + serviceConfig.Setting1;
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public partial class WCFService1 : IService1
{
public ConfigSettings GetConfig()
{
return Service1.serviceConfig;
}
public void SetConfig(ConfigSettings sentConfig)
{
Service1.serviceConfig = sentConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
[OperationContract]
void SetConfig(ConfigSettings sentConfig);
}
public class ConfigSettings
{
public int Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings() { }
}
}
The client retrieves the config like this (updated with some changes):
using System;
using System.ServiceProcess;
using System.Windows.Forms;
using WindowsServiceTest;
namespace WindowsServiceTestForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
ConfigSettings config = new ConfigSettings();
//GetConfig()
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//get the current config and display
config = myService.GetConfig();
MessageBox.Show(config.Setting1 + "\r\n" + config.Setting2, "config");
myService.Close();
}
//SetConfig(ConfigSettings)
private void button2_Click(object sender, EventArgs e)
{ //make changes to values
config.Setting1 += 1;
config.Setting2 = "new.Setting2:" + config.Setting1;
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//send the new config
myService.SetConfig(config);
myService.Close();
}
}
}
Update:
Maybe what I'm thinking needs to be done is overkill. It seems that I'm hitting a membrane between WCF and the Windows Service.
How would YOU approach this problem?
Windows Service that needs a Form for configuration.
When service starts, it loads a config.xml file from disk. (a serialized class)
When GUI starts, I want to:
retrieve its current configuration,
make some changes to it,
push it back to the service,
trigger service to re-read and react to the new configuration.
I was trying to avoid statically/writing the config file to disk and telling service to re-read it again. It "seemed" like WCF was the way to go.
Update 2
It seems that by just changing the master config in the service to static, the WCF service can access it directly. I could have sworn I did that originally before I posted, but I guess not.
I also separated the naming of Service1 to WCFService1 above, but it turns out that doesn't matter and works either way.
New complete code has been updated above.
You are getting confused between Windows Service and WCF Service - and have tried to have them both in the same class - while this is possible - it is probably easier to understand if you split them into two classes.
In your example the Windows Service starts, creates a new instance of itself as the WCF service then sets the config elements in the Windows Service instance, meaning the config is empty in the WCF Service instance.
try this instead
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1 ));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public class WCFService1 : IService1
{
public WCFService1()
{
//change master settings from null
myConfig.Setting1 = "123";
myConfig.Setting2 = "456";
}
//this is the master config that the service uses
public ConfigSettings myConfig = new ConfigSettings();
public ConfigSettings GetConfig()
{
return myConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
}
public class ConfigSettings
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
}
There are a few ways of doing this (injecting dependencies in the service instance). I'll show two of them.
You have a special case here because the Windows Service is also your WCF service implementation. I think that soon enough you will want to separate them. So, my samples are based on a separate WCF service implementation.
The thing is that WCF has a concept of service instance mode. By default it is PerCall, meaning that a new Service1 instance is created to handle each request. This makes it a bit more difficult to inject something in these instances.
The simplest way is to set the instance mode to Single, meaning there will be only one Service1 instance handling all requests.
The second is easy to implement:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1Implementation : IService1
{
private ConfigSettings _configSettings;
public Service1(ConfigSettings settings)
{
//now you have the settings in the service
_configSettings = setting;
}
...
}
//in the Windows Service
myServiceHost = new ServiceHost(typeof(Service1Implementation), new Service1Implementation(myConfig));
myServiceHost.Open();
The first solution involves you creating and specifying a service instance factory which the infrastructure will use later to create service instances per call.
The factory gives you the possibility to instantiate Service1 yourself and pass the config.
There is quite some code to be written for this, but I'll show the essential. For the complete solution, read this.
For you, it's easier to make the Windows Service just implement IInstanceProvider:
public class Service1 : ServiceBase, IInstanceProvider
{
private ConfigSettings _myConfig; //assign this member later on
...
//IInstanceProvider implementation
public object GetInstance(InstanceContext instanceContext, Message message)
{
//this is how you inject the config
return new Service1Implementation(_myConfig);
}
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
...
}
I will admit that it's been a while since I've touched WCF, but it looks to me like your ConfigSettings class is missing some attributes required to make it serializable via WCF.
using System.Runtime.Serialization;
[DataContract]
public class ConfigSettings
{
[DataMember]
public string Setting1 { get; set; }
[DataMember]
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
I don't believe having your Windows service operate as your WCF service, like other answers have suggested, is the problem. But I do agree that it's best to have them be separate classes.
I am just starting to familiarise myself with ServiceStack and have come upon FluentValidation. I have followed the introductions and created a small Hello App.
My problem is that when I try to validate the request DTO no error messages are returned to describe how it failed validation, only a blank Json object {}.
Myself, I think the validation is autowired to the DTO so there should be no need for me to write any extra code.
The answer is probably blatant but I cannot see it. Any help would be greatly appreciated. My code is below:
namespace SampleHello2
{
[Route("/hello")]
[Route("/hello/{Name}")]
public class Hello
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
public class HelloValidator : AbstractValidator<Hello>
{
public HelloValidator()
{
//Validation rules for all requests
RuleFor(r => r.Name).NotNull().NotEmpty().Equal("Ian").WithErrorCode("ShouldNotBeEmpty");
RuleFor(r => r.Name.Length).GreaterThan(2);
}
}
public class Global : System.Web.HttpApplication
{
public class HelloAppHost : AppHostBase
{
//Tell Service Stack the name of your application and where to find your web services
public HelloAppHost() : base("Hello Web Services", typeof(HelloService).Assembly) { }
public override void Configure(Funq.Container container)
{
//Enable the validation feature
Plugins.Add(new ValidationFeature());
container.RegisterValidators(typeof(HelloValidator).Assembly);
//register any dependencies your services use, e.g:
// container.Register<ICacheClient>(new MemoryCacheClient());
}
}
//Initialize your application singleton
protected void Application_Start(object sender, EventArgs e)
{
new HelloAppHost().Init();
}
}
}
P.S. Really enjoying using ServiceStack, It really is a fantastic project so thanks.
Edit
So for example:
Calling: http://localhost:60063/hello/Ian?format=json returns {"Result":"Hello, Ian"}.
Whereas Calling: http://localhost:60063/hello/I?format=json returns {}.
The second call returns {} where I was expecting auto generated error messages.
I found the answer. It was an overlook on my behalf:
This was in the documentation and I overlooked it:
All Error handling and validation options described below are treated
in the same way - serialized into the ResponseStatus property of your
Response DTO making it possible for your clients applications to
generically treat all Web Service Errors in the same way.
So all that was missing from my code was to add the following line into the HelloResponse class.
public ResponseStatus ResponseStatus { get; set; }
I have a Silverlight project that uses a WCF service that I created. My problem is that in my WCF service, I created a ServiceHost but VS2010 doesn't seem to recognize the instance of my object (underlines the svHost). Below is the code for my service.
using System;
using System.Collection.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Activation;
namespace userIO.Web
{
[ServiceContract]
public class CoordsService
{
[OperationContract]
public double xDir();
[OperationContract]
public double yDir();
[OperationContract]
public String keyPressed();
public class Coords : CoordsService
{
public double xDir { get; set; }
public double yDir { get; set; }
public String keyPressed { get; set; }
}
ServiceHost svHost = new ServiceHost(typeof(Coords), new Uri("http://localhost:8080"));
BasicHttpBinding binding = new BasicHttpBinding();
svHost.AddServiceEndpoint(typeof(CoordsService), binding, "");
svHost.Open();
}
}
Your ServiceContract should decorate an interface (a contract). The ServiceHost should host an instance of this interface and be outside the same service that it's hosting. At least I've only seen it done in this way.
The basic structure is:
[ServiceContract]
public interface IService
{
[OperationContract]
void DoSomething(Data data);
}
[DataContract]
public class Data
{
[DataMember]
int Num {get;set;}
}
public class Service : IService
{
public void DoSomething(Data data)
{ // do something }
}
// run in any other kind of app, console, win service, winform/wpf
static void Main()
{
ServiceHost svHost = new ServiceHost(typeof(Service), new Uri("http://localhost:8080"));
BasicHttpBinding binding = new BasicHttpBinding();
svHost.AddServiceEndpoint(binding, "");
svHost.Open();
}
An even easier solution to get your service up and running in VS2010 is to just create the service in a new WCF Service template. Take out their demo code, put in your own code for the servicecontract interface and the implementation service, then choose debug -> run and VS2010 will host the service for you without having to create an external app to run the service. Will also let you send data to the service to test out the code and the return values of your wcf functions in their simple winforms app.