I am new to RabbitMQ and I am trying to "receiveAndConvert" to a custom type of mine: Person.java.
Here is my producer:
public class Producer {
public static void main(String[] args) {
ApplicationContext context = new GenericXmlApplicationContext("classpath:/applicationContext.xml");
AmqpTemplate template = context.getBean(AmqpTemplate.class);
Person person = new Person();
person.setAge(37);
person.setName("Julien");
template.convertAndSend("myqueue", person);
}
}
and here is my consumer:
public class Consumer {
public static void main(String[] args) {
ApplicationContext context = new GenericXmlApplicationContext("classpath:/applicationContext.xml");
AmqpTemplate template = context.getBean(AmqpTemplate.class);
Person me = (Person) template.receiveAndConvert("myqueue");
System.out.println("Me: " + me.getName() + ":" + me.getAge());
}
}
My Person.java is just a POJO with a name and an age instance variables.
I get a ClassCastException as follows:
Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to trc.suivi.amqp.Person
at trc.suivi.amqp.Consumer.main(Consumer.java:14)
Note that the Producer and Consumer classes are located in two different projects and I have copied/pasted the Person.java class over to the Consumer project.
My config is as follows:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<rabbit:connection-factory id="connectionFactory" />
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory" />
<rabbit:admin connection-factory="connectionFactory" />
<rabbit:queue name="myqueue" />
</beans>
It just occurred to me that my Person.java class needs to implement Serializable. It now works.
Related
I'm working with a .Net Core 3.1 XUnit project.
I create a SerivceCollection and call the AddLogging extension method. Then I can create an ILogger instance with the LoggerFactory / ILoggerFactory and when I debug, I can my ServiceCollection has a ServiceDescriptor for this type:
Lifetime = Singleton, ServiceType = {Name = "ILogger`1" FullName = "Microsoft.Extensions.Logging.ILogger`1"}, ImplementationType = {Name = "Logger`1" FullName = "Microsoft.Extensions.Logging.Logger`1"}
I'm curious what that tick mark means in the type name and if it's possible to resolve an ILogger instance without using the LoggerFactory.
Here are a couple failed attempts at resolving ILogger`1. The last call to CreateLogger works.
[Fact]
public void AddLogging_RegistersILogger()
{
var services = new ServiceCollection().AddLogging();
var serviceProvider = services.BuildServiceProvider();
var logger1 = serviceProvider.GetService<ILogger>(); // logger == null
try
{
var logger2 = serviceProvider.GetService(typeof(ILogger<>));
}
catch (Exception e)
{
// Implementation type 'Microsoft.Extensions.Logging.Logger`1[T]' can't be converted to service type 'Microsoft.Extensions.Logging.ILogger`1[TCategoryName]'
}
try
{
var loggerTypes = Assembly.GetAssembly(typeof(ILogger)).GetTypes().Where(t => t.Name == "ILogger`1");
var loggerType = loggerTypes.First();
var logger3 = serviceProvider.GetService(loggerType);
}
catch(Exception e)
{
// Implementation type 'Microsoft.Extensions.Logging.Logger`1[T]' can't be converted to service type 'Microsoft.Extensions.Logging.ILogger`1[TCategoryName]'
}
var logger4 = serviceProvider.GetService<ILoggerFactory>().CreateLogger<DependencyInjectionTests>();
Assert.NotNull(logger4);
}
The tick on "ILogger`1" means its a generic type, i.e. ILogger<CategoryName>
You can inject ILogger<T> for any generic type. The generic type is used to set the category name in a convenient manner, e.g. ILogger<Controller> will have "Microsoft.AspNetCore.Mvc.Controller" category name
The common practice is for each class to have a logger with the category name of the class, e.g. a class namedMyService will have a logger ILogger<MyService>
You can either inject ILogger<T> or use ILoggerFactory:
Injecting ILogger<MyService> is equivalent to calling loggerFactory.CreateLogger<MyService>() or loggerFactory.CreateLogger("<namespace_of_my_service>.MyService")
I resolved the problem by implemented NLog using the following steps in asp.net core
https://code-maze.com/net-core-web-development-part3/
Nuget:
1. NLog.Config
2. NLog.Extensions.Logging
Logging
functionality added to software so someone can get insight into the software
Logging as targets and rules.
nlog.config (change property to copy always)
1. the logfile name creates a file in the logs directory
2. debug is wrote to the logfile target
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Trace"
internalLogFile="internallog.txt">
<targets>
<target name="logfile" xsi:type="File"
fileName="${startupdir}\logs\${shortdate}_logfile.txt"
layout="${longdate} ${level:uppercase=true} ${message}"/>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
</nlog>
startup.cs
1. load the nlog.config
2. ConfigureLoggerService is part of the static class called Service Extensions. Its job is to create a singleton task for the LoggerManager
public Startup(IConfiguration configuration)
{
LogManager.LoadConfiguration(#"nlog.config");
Configuration = configuration;
//_env = env;
}
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureLoggerService();
}
serviceExtensions.cs
public static class ServiceExtensions
{
public static void ConfigureLoggerService(this IServiceCollection services)
{
services.AddSingleton<ILoggerManager, LoggerManager>();
}
}
LoggerManager.cs
1. use the dependency injected Logger for nLog to post based on type.
public class LoggerManager : ILoggerManager
{
private static ILogger logger = LogManager.GetCurrentClassLogger();
public void LogDebug(string message)
{
logger.Debug(message);
}
public void LogError(string message)
{
logger.Error(message);
}
public void LogInfo(string message)
{
logger.Info(message);
}
public void LogWarn(string message)
{
logger.Warn(message);
}
}
I'm struggling with rewriting RabbitMQ application configuration from XML to Java. Sadly once the code is executed, quite general error appears:
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException:
Failed to invoke target method 'receiveMessage' with
argument type = [class [B], value = [{[B#3bd0e47}]
...
Caused by: java.lang.NoSuchMethodException: com.mycompany.MessageListener.receiveMessage([B)
Application works if I base my configuration on XML, listed below.
I tried to rewrite it, basing on Spring Integration, AMQP, Rabbit documentation. Nevertheless, spring configuration documentation is mostly xml based, hence my question.
XML conf:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<rabbit:connection-factory id="connectionFactory" host="mycompany-host"
username="mycompany-username"
password="mycompany-password"
virtual-host="mycompany-vhost"/>
<rabbit:template id="mycompany-template" connection-factory="connectionFactory" />
<rabbit:admin id="admin" connection-factory="connectionFactory" />
<!-- ##### -->
<rabbit:queue id="queue-id" name="queue-name" declared-by="admin"/>
<rabbit:direct-exchange name="mycompany-incoming-events" declared-by="admin">
<rabbit:bindings>
<rabbit:binding queue="queue-name" key="" />
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- ##### -->
<int-amqp:inbound-channel-adapter channel="mycompany-channel"
queue-names="queue-name" connection-factory="connectionFactory" />
<int:chain input-channel="mycompany-channel">
<int:transformer>
<bean class="com.mycompany.MyCompanyParser"/>
</int:transformer>
<int:filter expression="payload.header != null"/>
<int:transformer>
<bean class="com.mycompany.MyCompanyHeaderEnricher"/>
</int:transformer>
<int:recipient-list-router>
<int:recipient channel="dataSubmittedChannel"/>
</int:recipient-list-router>
</int:chain>
<int:chain input-channel="dataSubmittedChannel">
<int:filter expression="headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')" />
<int:service-activator>
<bean class="com.mycompany.MessageListener"/>
</int:service-activator>
</int:chain>
</beans>
Java listener:
#Component
public class MessageListener {
public void receiveMessage(final MyCompanyParsedType msg){
System.out.println(msg.toString());
}
}
After some rewriting I managed to came up with this Java based configuration:
import com.nxcs3.gamefetcher.configuration.SampleConfiguration;
import com.nxcs3.gamefetcher.listener.GameMessageListener;
import nxcs.drept.nxcs2events.EventHeadersEnricher;
import nxcs.drept.nxcs2events.EventParser;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter;
import org.springframework.integration.dsl.IntegrationFlow;
#SpringBootApplication
public class MyCompanySpringBootApp {
public static final String MESSAGE_QUEUE = "queue-name";
public static final String MESSAGE_EXCHANGE = "mycompany-incoming-events";
public static void main(String[] args) {
SpringApplication.run(MyCompanySpringBootApp.class);
}
#Bean
public DirectExchange exchange(){
return new DirectExchange(MESSAGE_EXCHANGE);
}
#Bean
public Queue queue(){
return new Queue(MESSAGE_QUEUE, true);
}
#Bean
public Binding binding(Queue queue){
return BindingBuilder.bind(queue).to(exchange()).with(MESSAGE_QUEUE);
}
#Bean
MessageListenerAdapter listenerAdapter(MessageListener receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
#Bean
public IntegrationFlow flow(){
return f -> f.log()
.transform(new MyCompanyParser())
.filter("payload.header != null")
.transform(new MyCompanyHeaderEnricher())
.filter("headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(MESSAGE_QUEUE);
container.setMessageListener(listenerAdapter);
return container;
}
}
I supply connection details through yaml.
As I mentioned previously, I clearly miss something.
Any ideas where did the configuration went wrong?
Added section after comments, proposed solution:
So I removed MessageListenerAdapter and replaced it using AmqpInboundChannelAdapter and #ServiceActivator
Result would look like:
#SpringBootApplication
public class MyCompanySpringBootApp {
public static final String MESSAGE_QUEUE = "queue-name";
public static final String MESSAGE_EXCHANGE = "mycompany-incoming-events";
public static void main(String[] args) {
SpringApplication.run(MyCompanySpringBootApp.class);
}
#Bean
public DirectExchange exchange(){
return new DirectExchange(MESSAGE_EXCHANGE);
}
#Bean
public Queue queue(){
return new Queue(MESSAGE_QUEUE, true);
}
#Bean
public Binding binding(Queue queue){
return BindingBuilder.bind(queue).to(exchange()).with(MESSAGE_QUEUE);
}
#Bean
public AmqpInboundChannelAdapter
channelAdapter(SimpleMessageListenerContainer container){
AmqpInboundChannelAdapter amqpInboundChannelAdapter = new
AmqpInboundChannelAdapter(container);
amqpInboundChannelAdapter.setOutputChannelName("adapter");
return amqpInboundChannelAdapter;
}
#Bean
public MessageListener handler(){
return new MessageListener();
}
#Bean
public IntegrationFlow flow(){
return f -> f.log()
.transform(new MyCompanyParser())
.filter("payload.header != null")
.transform(new MyCompanyHeaderEnricher())
.filter("headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(MESSAGE_QUEUE);
container.setMessageListener(listenerAdapter);
return container;
}
}
and listener:
#Component
public class MessageListener {
#ServiceActivator(inputChannel = "adapter")
public void receiveMessage(final MyCompanyParsedType msg){
System.out.println(msg.toString());
}
}
Which brings us a little bit closer, because messages are being accepted and processed inside of receiveMessage method.
However somehow coming messages do not pass through IntegrationFlow filters. Messages appear to be totally random. I added imports
The MessageListenerAdapter uses a SimpleMessageConverter by default.
And its logic is based on the presence of the contentType property.
According to your error, that sounds like there is no this property in the consumed message, therefore it falls back to the message.getBody(), which is byte[] anyway.
You may consider to specify a desired MessageConverter into that MessageListenerAdapter, e.g. SerializerMessageConverter with the ignoreContentType = true.
I came across an issue, when the jpos channel header string has spaces. I configured that in the channel adaptor configuration as below, but when I start the Q2 server, it seems it trims the header value. As a result of that, I'm not getting any response from the jpos server for certain requests.
<channel-adaptor class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2" name="my-channel">
<channel class="CBCChannel" logger="Q2"
packager="org.jpos.iso.packager.GenericPackager" header="ISOHEADER ">
<property name="packager-config" value="/path/to/PACKAGER/iso87ascii.xml" />
<property name="host" value="xxx.xx.xx.xx"/>
<property name="port" value="yyyy" />
</channel>
<in>channel-send</in>
<out>channel-receive</out>
<property name="timeout" value="300000" />
<property name="keep-alive" value="true" />
<reconnect-delay>10000</reconnect-delay>
</channel-adaptor>
The CBCChannel just extends the RawChannel
public class CBCChannel extends RawChannel {
public CBCChannel() throws ISOException {
}
public CBCChannel(String host, int port, ISOPackager p, byte[] header) {
super(host, port, p, header);
}
public CBCChannel(ISOPackager p, byte[] header) throws IOException {
super(p, header);
}
public CBCChannel(ISOPackager p, byte[] header, ServerSocket serverSocket) throws IOException {
super(p, header, serverSocket);
}
}
Is there any way to configure channel header which contains spaces without neglecting the spaces?
I guess you only need to override setHeader method:
public CBCChannel extends RawChannel {
....
public void setHeader(String header){
super.setHeader(header.getBytes());
}
}
But you would only be doing what BaseChannel does in regard to the header. Are you sure you need a RawChannel based channel?
I am new to rabbitmq. I am using spring-rabbit 1.3.5 Release.
I want to register multiple message listner. How to do that?
I can register a single message listner.
Here is my code:
1)Interface which extends MessageListner interface
public interface MessageQueueManager extends MessageListener{
public String createQueue(String queueName);
public void sendMessage(String message, String destinationQueueName) throws Exception;
}
2) Here is the implementation:
#Service("messageQueueManager")
public class MessageQueueManagerImpl implements MessageQueueManager {
#Autowired
private AmqpAdmin admin;
#Autowired
private AmqpTemplate template;
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
private SimpleMessageListenerContainer container;
#Override
public void onMessage(Message message) {
// Different message can behave differently.
}
#Override
public String createQueue(String queueName) {
// survive a server restart
boolean durable = true;
// keep it even if nobody is using it
boolean autoDelete = false;
boolean exclusive = false;
// create queue
Queue newQueue = new Queue(queueName, durable, exclusive, autoDelete);
queueName = admin.declareQueue(newQueue);
// create binding with exchange
// Producer sends to an Exchange and a Consumer receives from a Queue, the bindings that connect Queues to Exchanges are critical for connecting those producers and consumers via messaging.
/*admin.declareBinding(new Binding(queueName, DestinationType.QUEUE,
"directExchange", queueName, new HashMap<String, Object>()));*/
Binding binding = BindingBuilder.bind(newQueue).to(DirectExchange.DEFAULT).with(queueName);
admin.declareBinding(binding);
// add queue to listener
container.addQueues(newQueue);
// start listener
container.start();
return queueName;
}
#Override
public void sendMessage(String message, String destinationQueueName)
throws Exception {
template.convertAndSend("directExchange", destinationQueueName,
MessageBuilder.withBody(message.getBytes()).build());
}
}
3)Listner register in applicationContext.xml file
<!-- Listener container for setting up concurrent listeners for queues -->
<bean id="simpleMessageListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<constructor-arg index="0" ref="connectionFactory" />
<property name="missingQueuesFatal" value="false" />
<property name="concurrentConsumers" value="5" />
<property name="autoStartup" value="false" />
<property name="messageListener" ref="messageQueueManager" />
</bean>
So here SimpleMessageListenerContainer class can take only one messageListner. Do I need to declare multiple SimpleMessageListenerContainer instance to register different messageListner?
I want to register this class as a message listner.
#Service("myMessageListener")
public class MessageHandler implements MessageListener {
#Override
public void onMessage(Message message) {
log.info("Received message: " + message);
log.info("Text: " + new String(message.getBody()));
}
}
1)Register your queues:
<rabbit:queue id="spring.queue" auto-delete="false" durable="true" exclusive="false" name="spring.queue"/>
<rabbit:queue id="user.login.notification" auto-delete="false" durable="true" exclusive="false" name="user.login.notification"/>
2)Declare the bindings:
<rabbit:direct-exchange name="directExchange" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="spring.queue" key="spring.queue" />
<rabbit:binding queue="user.login.notification" key="user.login.notification MAIYAM" />
</rabbit:bindings>
</rabbit:direct-exchange>
3)Tell the container to call onMessage(Message message) method when the any of the queue publishes the message.
<rabbit:listener-container
connection-factory="connectionFactory" acknowledge="auto" concurrency="10"
requeue-rejected="true">
<rabbit:listener ref="myMessageListener" queues="spring.queue" />
<rabbit:listener ref="messageQueueManager" queues="user.login.notification" />
</rabbit:listener-container>
4)Remove private SimpleMessageListenerContainer container; from MessageQueueManagerImpl class.
Now it should work.
I've added a SAP Netweaver service reference to my project using a local WSDL. The server returns a null response, but using Fiddler I can see the correct response is being sent. I assume this means the response isn't being deserialized correctly. This is how the root response element is defined in the WSDL:
<xsd:element name="adrl">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="adr" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
The child element (some fields elided):
<xsd:element name="adr">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="id"/>
</xsd:sequence>
<xsd:attribute name="op" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="update"/>
<xsd:enumeration value="delete"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
Response:
<wsdl:output message="p1:p2.adrl"/>
Namespaces:
xmlns:p1="urn:mycompany:outbound"
xmlns:p2="http://tempuri.org/mycompany"
SOAP response from service:
<SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/'>
<SOAP:Header/>
<SOAP:Body>
<adrl>
<adr op='update'>
<id>9AF1FBA0-81A4-4427-A011-0DCE3BD1F609</id>
</adr>
</adrl>
</SOAP:Body>
</SOAP:Envelope>
WCF class definitions (from Reference.cs):
// Some namespaces elided
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.17929")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/mycompany")]
public partial class adr : object, System.ComponentModel.INotifyPropertyChanged {
private string idField;
// adrOP has a suitable enumeration defined elsewhere in the file
private adrOP opField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public string id {
get {
return this.idField;
}
set {
this.idField = value;
this.RaisePropertyChanged("id");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public adrOP op {
get {
return this.opField;
}
set {
this.opField = value;
this.RaisePropertyChanged("op");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
public partial class GetAdrResponse {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://tempuri.org/mycompany", Order=0)]
[System.Xml.Serialization.XmlArrayItemAttribute("adr", IsNullable=false)]
public adr[] adrl;
public GetAdrResponse() {
}
public GetAdrResponse(adr[] adrl) {
this.adrl = adrl;
}
}
When I try manually deserializing the response I captured in Fiddler (with adrl as the root element) I get the and Xml error with the inner exception message "" was not expected. If I add a root element to the serializer the error disappears, but only the "op" attribute is properly deserialized. The rest of the fields are null.
What could be the problem? I don't really have access to the Service but if there's something that has to change there I can forward a request. Otherwise I'm contemplating adding a MessageInspector and modifying the response, but I'm not really sure in which way it should be modified.
EDIT: I created the object in C# and deserialized it and this was the result:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfAdr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<adr op="update">
<id>9AF1FBA0-81A4-4427-A011-0DCE3BD1F609</id>
</adr>
</ArrayOfAdr>
It seems like the root element is not properly defined in terms of serialization.
I had same problem when I tried to use WCF service in SAP Netweaver EJB DC. I solved this issue by adding XmlSerializerFormat tag to each operation in WCF Service and XmlRoot tag to the data contract:
[XmlRoot(Namespace = "urn:sap.com:WS:Service1", ElementName = "Entity1")]
[DataContract]
public class Entity1
{
// your properties
}
[ServiceContract(Namespace = "urn:sap.com:WS:Service1", Name = "IService1")]
public interface IService1
{
[XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]
[OperationContract]
Entity1 GetById(int id);
[XmlSerializerFormat(Style = OperationFormatStyle.Document, Use = OperationFormatUse.Literal)]
[OperationContract]
string Save(Entity1 entity);
}
[ServiceBehavior(Namespace = "urn:sap.com:WS:Service1", Name = "Service1")]
[BindingNamespaceBehavior(bindingNamespace = "urn:sap.com:WS:Service1")]
public class Service1 : IService1
{
Entity1 GetById(int id)
{
// your code
}
string Save(Entity1 entity)
{
// your code
}
}