I'm trying to make a single WCF application that will allow me to receive either SOAP or RESTful messages and essentially process them the same way. I do have code that "works" but I'm not sure it's implemented in the most efficient manner, so I'm wondering how this can be accomplished more elegantly.
I have the following code:
class Interfaces
{
[ServiceContract(Namespace = "myNameSpace")]
public interface IMessageBrokers_SOAP
{
[OperationContract]
[WebInvoke]
bool Broker_SOAP_Messages(string source, string destination,Dictionary<string, string> Incoming_Message);
}
[ServiceContract(Namespace = "myNameSpace")]
public interface ImessageBrokers_REST
{
[OperationContract, WebGet(ResponseFormat = WebMessageFormat.Xml)]
bool Broker_REST_Messages();
}
}
class MessageBrokers : Interfaces.IMessageBrokers_SOAP, Interfaces.ImessageBrokers_REST
{
public bool Broker_SOAP_Messages(string source, string destination,Dictionary<string, string> Incoming_Values)
{
//take the object passed in and pass it along to wholesale to Broker_Message
return Broker_Message(source, destination, Incoming_Values);
}
public bool Broker_REST_Messages()
{
//take the query params and put them into dictionary for Broker_Message to use
var queryParameters = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;
Dictionary<string, string> Incoming_Values = new Dictionary<string, string>();
foreach (string key in queryParameters.AllKeys)
{
string value = queryParameters[key];
Incoming_Values.Add(key, value);
}
return Broker_Message(queryParameters["source"], queryParameters["destination"], Incoming_Values);
}
bool Broker_Message(string source, string destination, Dictionary<string, string> Incoming_Values)
{
//do work
}
}
But I feel like I'm going about this the wrong way. I'd like to use inheritance for this, but I can't seem to get it to work without having two separate code sets.
Related
I have an OData controller that want to encode string fields of its output. OData output result is sent back to client as clear data but I want to encrypt them before sending it to client.
It seems that the best way is to use a customized OData serializer or a feed serializer to achieve my goal. But I'm not sure which class to customize. Is it better to override some methods or implement my own customized version of a serializing class.
Any help is appreciated.
Find a way to do it. I did it by customizing serializer by help of this guide. Final code:
using Microsoft.Data.Edm;
using Microsoft.Data.OData;
using System.Web.Http.OData.Formatter.Serialization;
namespace MyProj
{
public class CustomODataEntityTypeSerializer : ODataEntityTypeSerializer
{
public CustomODataEntityTypeSerializer(ODataSerializerProvider serializerProvider) : base(serializerProvider)
{
}
public override void WriteObjectInline(object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
{
//http://odata.github.io/WebApi/#06-03-costomize-odata-formatter
if (graph is CompanyDto)
{
var company = (CompanyDto)graph;
company.FirstName = Encrypt(company.FirstName);
company.LastName = Encrypt(company.LastName);
}
base.WriteObjectInline(graph, expectedType, writer, writeContext);
}
}
}
And this class:
using Microsoft.Data.Edm;
using System.Web.Http.OData.Formatter.Serialization;
namespace MyProj
{
public class CustomDefaultODataSerializerProvider : DefaultODataSerializerProvider
{
CustomODataEntityTypeSerializer _customODataEntityTypeSerializer;
public CustomDefaultODataSerializerProvider()
{
_customODataEntityTypeSerializer = new CustomODataEntityTypeSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.IsEntity())
{
return _customODataEntityTypeSerializer;
}
return base.GetEdmTypeSerializer(edmType);
}
}
}
And initializing it as follow:
var odataFormatters = ODataMediaTypeFormatters.Create(new CustomDefaultODataSerializerProvider(), new DefaultODataDeserializerProvider());
config.Formatters.InsertRange(0, odataFormatters);
I have a lot of businesses services already implemented, and I´m exposing them as services by WCF.
I don´t like the idea to have one endpoint to each service..... it could be a problem to maintain in the future as my repository grows.......
I´d like to know wcf´s experts opinions if the code below would be a good approach an them I can move ahead with this solution.
Business Service A:
[ServiceContract]
public interface IServiceA
{
[OperationContract]
object AddA(object a);
[OperationContract]
object Update();
}
Business Service B:
[ServiceContract]
public interface IServiceB
{
[OperationContract]
object AddB(object b);
[OperationContract]
object Update();
}
Concrete implementation for Service A
public class ConcreteServiceA : IServiceA
{
public object AddA(object a)
{
Console.WriteLine("ConcreateServiceA::AddA");
return null;
}
public object Update()
{
Console.WriteLine("ConcreateServiceA::Update");
return null;
}
}
Concrete implementation for Service B
public class ConcreteServiceB : IServiceB
{
public object AddB(object b)
{
Console.WriteLine("ConcreateServiceB::AddB");
return null;
}
public object Update()
{
Console.WriteLine("ConcreateServiceB::Update");
return null;
}
}
My single service is partial to separate concerns to each service.
Note that it´s constructors depends on both business services above, will be injection using IoC
Partial for constructors
public partial class WCFService
{
IServiceA _a;
IServiceB _b;
public WCFService()
: this(new ConcreteServiceA(), new ConcreteServiceB())
{
}
public WCFService(IServiceA serviceA, IServiceB serviceB)
{
_a = serviceA;
_b = serviceB;
}
}
Partial class implementing only IServiveA
public partial class WCFService : IServiceA
{
object IServiceB.AddB(object b)
{
return _b.AddB(b);
}
object IServiceB.Update()
{
return _b.Update();
}
}
Partial class implementing only IServiceB
public partial class WCFService : IServiceB
{
object IServiceA.AddA(object a)
{
return _a.AddA(a);
}
object IServiceA.Update()
{
return _a.Update();
}
}
And in the client side, I using like that:
var endPoint = new EndpointAddress("http://localhost/teste");
ChannelFactory<IServiceA> _factoryA = new ChannelFactory<IServiceA>(new BasicHttpBinding(), endPoint);
IServiceA serviceA = _factoryA.CreateChannel();
serviceA.Update();
var netTcpEndPoint = new EndpointAddress("net.tcp://localhost:9000/teste");
ChannelFactory<IServiceB> _factoryB = new ChannelFactory<IServiceB>(new NetTcpBinding(), netTcpEndPoint);
IServiceB serviceB = _factoryB.CreateChannel();
serviceB.Update();
I really appreciate any opinion or other suggestions.
There's nothing wrong with multiple endpoints - it's part of the process. What is wrong, however, is duplicating functionality over multiple endpoints. How many "UpdateThis's" or "AddThat's" developers need? This can get out of control and makes for a maintenance headache. Just look at your constructor, it will grow and grow as you add new services and consolidate them into one service.
Think coarse-grained not fine-grained.
As an alternative, maybe you can try passing request objects as a parameter and returning response objects. This approach may streamline your code and help you avoid the maintenance issues you mention in your post and gives you a suggestion.
So, it looks something like this:
// Your service will return a very generic Response object
public interface IService
{
Response YourRequest(Request request);
}
// Your service implementation
public partial class WCFService : IService
{
Response IService.YourRequest(Request request)
{
//inspect the Request, do your work based on the values
//and return a response object
}
}
// Your request object
public class Request()
{
object YourClass{get;set;}
DoWhat Action{get;set;} //enum, constants, string etc.
int ID {get; set;}
}
// Your response object
public class Response()
{
bool Success {get; set;}
}
// Create Request object
var request = new Request(){YourClass = YourClassName , Action DoWhat.Update(), ID=1};
// Your service call
var endPoint = new EndpointAddress("http://localhost/teste");
ChannelFactory<IService> _factory = new ChannelFactory<IService>(new BasicHttpBinding(), endPoint);
IService service = _factory.CreateChannel();
var response = service.YourRequest(request);
So, now you've removed the fine-grained approach and replaced it with course-grained one. Let me know if you'd like more detail.
I have a WCF service which has methods that depend on reading values (OData) from the http request's querystring. I'm trying to write unit tests which inject in mock values into the querystring, then when I call the method it would use these mock values rather than erroring due to the request context not being available.
I've tried using WCFMock (which is based on Moq) however I don't see a way to set or get the querystring from the WebOperationContext that it provides.
Any ideas?
I ended up using the IOC pattern to solve this, creating an IQueryStringHelper interface that is passed into the constructor of the service. If it isn't passed in then it'll default to use the "real" QueryStringHelper class. When running test cases, it'll use an overloaded service constructor to pass in the TestQueryStringHelper instance, which lets you set a mock value for the querystring.
Here is the querystring helper code.
public interface IQueryStringHelper {
string[] GetParameters();
}
public class QueryStringHelper : IQueryStringHelper {
public string[] GetParameters() {
var properties = OperationContext.Current.IncomingMessageProperties;
var property = properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
string queryString = property.QueryString;
return queryString.Split('&');
}
}
public class TestQueryStringHelper : IQueryStringHelper {
private string mockValue;
public TestQueryStringHelper(string value) {
mockValue = value;
}
public string[] GetParameters() {
return mockValue.Split('&');
}
}
And the service implementation:
public partial class RestService : IRestService {
private IAuthenticator _auth;
private IQueryStringHelper _queryStringHelper;
public RestService() : this(new Authenticator(), new QueryStringHelper()) {
}
public RestService(IAuthenticator auth, IQueryStringHelper queryStringHelper = null) {
_auth = auth;
if (queryStringHelper != null) {
_queryStringHelper = queryStringHelper;
}
}
}
And how to consume it from a test case:
string odata = String.Format("$filter=Id eq guid'{0}'", "myguid");
var service = new RestService(m_auth,new TestQueryStringHelper(odata));
var entities = service.ReadAllEntities();
Hopefully this helps someone else.
I am writing my first WCF service. I am trying to understand how Datacontracts work. I have read the MSDN Article that describes how custom types should be marked up to create a data contract but I cannot get my example to work.
I have a simple DTO object that I have placed in a shared library because I want the client and the service to know about this type (right?) it looks like this:
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace org.healthwise.gatewayinterfaces.mocks
{
[DataContract]
public class MockCheckInDTO : ICheckInDTO
{
[DataMember]
private string _testPackageFilePath = "testpackages\\973eb455-6acc-486b-a1dd-2cf527872b1e.zip";
[DataMember]
private IDictionary<string, string> _testMetaData;
public MockCheckInDTO()
{
_testMetaData = MakeTestMetaDataDictionary();
}
private IDictionary<string, string> MakeTestMetaDataDictionary()
{
IDictionary<string, string> testMetaData = new Dictionary<string, string>();
testMetaData.Add("Version", "9.0.1");
testMetaData.Add("Product Family", "Learning Modules");
return testMetaData;
}
[DataMember]
public string PackageFileLocation
{
get { return _testPackageFilePath; }
set { _testPackageFilePath = value; }
}
[DataMember]
public IDictionary<string, string> PackageMetaData
{
get { return _testMetaData; }
set { _testMetaData = value; }
}
}
}
This is the ServiceContract:
[ServiceContract]
public interface IIndexCheckIn
{
[OperationContract]
void AddToIndex(MockCheckInDTO mockCheckInDto);
}
I have created a little console application to attempt to send this MockCheckInDTO over to my service but it never gets there. It seems that I am having and issue serializing the MockCheckInDTO object. Can someone help me out?
This is the exception I am seeing:
System.Runtime.Serialization.SerializationException: Type 'org.healthwise.gatewayinterfaces.mocks.MockCheckInDTO' with data contract name 'MockCheckInDTO:http://schemas.datacontract.org/2004/07/org.healthwise.gatewayinterfaces.mocks' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known type
Try removing [DataMember] from the private fields, so it's just on the public properties. If you're still having trouble, it might be good for educating yourself on what's going on with your DataContract to, instead of having the DC in a shared library, have it automatically created from the service metadata. Then take a look at it and see if it's what you expect. If not, you'll at least have an idea of what's going wrong when you try to serialize/deserialize the object.
first of all, it is weired that you serialize the same data twice: the private fields and the public properties. As Tim S. said it's better to remove one.
I tried to reproduce your problem by using DataContractSerializer directly, but I failed.
DataContractSerializer serializer = new DataContractSerializer(typeof(MockCheckInDTO));
var data = new MockCheckInDTO();
using (var file = File.OpenWrite("dto.xml"))
using (var xmlWriter = XmlDictionaryWriter.CreateTextWriter(file))
{
serializer.WriteObject(xmlWriter, data);
}
using (var file = File.OpenRead("dto.xml"))
using (var xmlReader = XmlDictionaryReader.CreateTextReader(file, XmlDictionaryReaderQuotas.Max))
{
var result = serializer.ReadObject(xmlReader);
}
I would like to formulate a contrived scenario, which nevertheless has firm actual basis. Imagine a collection type COuter, which is a wrapper around an instance of another collection type CInner. Both implement IList (never mind the T).
Furthermore, a COuter instance is buried inside some object graph, the root of which (let us refer to it as R) is returned from a WCF service method.
My question is how can I customize the WCF serialization process, so that when R is returned, the request to serialize the COuter instance will be routed through my code, which will extract CInner and pass it to the serializer instead. Thus the receiving end still gets R, only no COuter instance is found in the object graph.
I hoped that How does WCF serialize the method call? will contain the answer, unfortunately the article mentioned there (http://msdn.microsoft.com/en-us/magazine/cc163569.aspx) only barely mentions that advanced serialization scenarios are possible using IDataContractSurrogate interface, but no details are given. I am, on the other hand, would really like to see a working example.
Thank you very much in advance.
EDIT
I have created a trivial WCF sample, which demonstrates the issue. The archive is located here - https://docs.google.com/leaf?id=0B2pbsdBJxJI3NzFiNjcxMmEtMTM5Yy00MWY2LWFiMTUtNjJiNjdkYTU1ZTk4&sort=name&layout=list&num=50
It contains three small projects:
HelloServiceAPI - contains the service interface and the argument types
Host - the HelloService host
Client - a simple console client.
The service defines one method, which returns an instance of the HelloServiceResult type, which contains a reference to COuterList type, which wraps CInnerList type. The reference is specified as IMyListInterface, where both COuterList and CInnerList implement this interface. What I need is that when the result is serialized before being transmitted to the client, the COuterList reference be replaced with the wrapped CInnerList reference. I know this can be done by utilizing the existing abilities of WCF, I just do not know how.
Here is how you implement your own Surrogate:
class YourCustomTypeSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
// Just for reference
//if (typeof(OldType).IsAssignableFrom(type))
//{
// return typeof(NewType);
//}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// This method is called on serialization.
//if (obj is OldType)
//{
// // ... use the XmlSerializer to perform the actual serialization.
// NewType newObj = new NewType();
// return newObj;
//}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
// This method is called on deserialization.
// If PersonSurrogated is being deserialized...
//if (obj is NewType)
//{
// OldType newObj = new OldType();
// return newObj;
//}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
// This method is called on schema import.
//if (typeNamespace.Equals("Your Type Namespace"))
//{
// if (typeName.Equals("NewType"))
// {
// return typeof(OldType);
// }
//}
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
// Not used in this sample.
// You could use this method to construct an entirely
// new CLR type when a certain type is imported, or modify a generated
// type in some way.
return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
// Not used in this sample
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
// Not used in this sample
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
// Not used in this sample
}
}
Then you create a custom Serializer Operation Behavior :
public class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public CustomDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
}
After that, you create an attribute to apply the above operation behavior to an operation contract :
public class CustomDataContractFormatAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void Validate(OperationDescription description)
{
}
private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
DataContractSerializerOperationBehavior dcs = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcs != null)
description.Behaviors.Remove(dcs);
description.Behaviors.Add(new CustomDataContractSerializerOperationBehavior(description));
}
}
And finally, you apply this Attribute to an operation :
[OperationContract]
[CustomDataContractFormat]
void DoWork();
If you want to apply this to whole service, then you customize Service Behavior instead of Operation Behavior.
Here are the references that were used to create this example :
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx
http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/
http://www.danrigsby.com/blog/index.php/2008/04/10/specifying-a-different-serializer-per-endpoint-in-wcf/
http://social.msdn.microsoft.com/forums/en-US/wcf/thread/e4d55f3f-86d1-441d-9187-64fbd8ab2b3d/
Have you tried the good old OnSerializingAttribute?
[Serializable]
[KnownType(typeof(COuterList))]
public class HelloServiceResult
{
public IMyListInterface List;
[OnSerialized]
void OnSerializing(StreamingContext context)
{
if (List is COuterList)
{
List = ((List as COuterList).InnerList as CInnerList);
}
}
}