I'm facing an annoying problem while providing a wcf service. I am familiar with wcf and its usage.
Service Implementation:
public class Service : IService
{
public SampleClass SampleMethod ( SampleClass sampleParameter )
{
return new SampleClass { MyProperty1 = Guid.NewGuid(), MyProperty2 = ObjectId.GenerateNewId() };
}
}
Service interface:
[ServiceContract]
public interface IService
{
[OperationContract]
SampleClass SampleMethod ( SampleClass sampleParameter );
}
And my contract class:
/// this class is in DataContracts dll - meantioned in the exception
[DataContract]
public class SampleClass
{
[DataMember]
public Guid MyProperty1 { get; set; }
[DataMember]
public ObjectId MyProperty2 { get; set; }
}
I keep the interface and the contracts in seperate library projects and use the dlls at the clients.
MVC Side calling of service:
static IService Service = new ChannelFactory<IService>(new BasicHttpBinding("regularBinding"), new EndpointAddress(BaseAddress + "Service.svc")).CreateChannel();
public ActionResult Index()
{
var xyz = Service.SampleMethod(new SampleClass());
return View();
}
I can call this service from my unit test project or from a desktop application. But when I call the service from an MVC application it throws ProtocolException:
An exception of type 'System.ServiceModel.ProtocolException' occurred in mscorlib.dll but was not handled in user code
Additional information: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:sampleParameter. The InnerException message was 'Error in line 1 position 451. 'EndElement' 'MyProperty2' from namespace 'http://schemas.datacontract.org/2004/07/DataContracts' is not expected. Expecting element '_increment'.'. Please see InnerException for more details.
I have a hunch that this is caused by some serializer related issue, but I don't really have deep understanding on those topics, so here I am.
What might be the cause of this behaviour? How can I overcome this without changing my data structures?
Update
Btw I realized that the exception occurs on return. When I throw an exception from within the service method, that exception propogates to the client. Therefore I can say my request with ObjectId can be received from the service but cannot return to the client.
We've found out the problem and the solution in the discussion with #jpgrassi, but since #jpgrassi is too humble to post the answer, here I am.
Following the answer of this question #jeff's answer was inspiring enough to make me check the MongoDB.Bson dll's versions. There it was, they were different on server and mvc client and causing this problem. Leveling them on a version solved the problem.
Related
I'm loading some endpoints defined in external assemblies, into my web app on startup with:
// Add channel endpoints.
var endpointsFolder = Configuration.GetSection("EndpointsFolder").Get<string>();
foreach (var file in Directory.GetFiles(endpointsFolder))
services.AddMvc().AddApplicationPart(Assembly.LoadFrom(file));
All of the loaded endpoints derive from a custom class called SecureEndpoint which requires an interface ISecurityContext:
[ApiEndpoint]
public class SecureEndpoint : ControllerBase {
public SecureEndpoint(ISecurityContext securityContext) { ... }
}
So, for example:
[Route("test")]
public sealed class TestEndpoint : SecureEndpoint {
public TestEndpoint(ISecurityContext securityContext) : base(securityContext) { ... }
[HttpGet]
public string Get() {
return "This is a test.";
}
}
The problem I'm having is that when I invoke the endpoint, I get an error saying:
System.InvalidOperationException: Unable to resolve service for type Namespacing.ISecurityContext while attempting to activate Namespacing.TestEndpoint.
A few searches on the matter give a pretty simple solution:
services.AddScoped<ISecurityContext, SecurityContext>();
However, I have one problem; ISecurityContext is given to me in the form of the interface only. The value that is given to me, is the value I should use everywhere it's required. As a result, I need a way to say "if ISecurityContext is needed, use this value".
Is there a way to always send a specific value for a given type?
Edit: services.AddSingleton(...) did the trick, but I don't have time to type an answer up for it currently. I'll circle back later this afternoon.
If there are any compilation issues, they are only the result of typing this question.
I am trying to return a derived class from the base class using WCF service, but I keep getting the following exception
"An error occurred while receiving the HTTP response to http://localhost:50137/Service.svc. 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..."
I have tried adding all of the following over WCF Service method.
1) [XmlInclude(typeof(DerivedClass1)), XmlInclude(typeof(DerivedClass2))]
2) [SoapRpcMethod]
3) [SoapInclude(typeof(DerivedClass1)), SoapInclude(typeof(DerivedClass2))]
Code:
public class BaseClass
{
}
public class DerivedClass1:BaseClass
{
}
public class DerivedClass2:BaseClass
{
}
Wcf Service Method:
public BaseClass Validate()
{
if(someCondition)
return new DerivedClass1();
else
return new DerivedClass2();
}
[Serializable]
[DataContract]
[
KnownType(typeof(DerivedClass1)),
KnownType(typeof(DerivedClass2))
]
public class BaseClass
{
}
public class DerivedClass1:BaseClass
{
}
public class DerivedClass2:BaseClass
{
}
see
https://msdn.microsoft.com/en-us/magazine/gg598929.aspx for more information about Known Types and the Generic Resolver.
There are a number of problems with the code you've posted:
Your contract type and operation code have no ServiceModel annotations
You haven't specified how you're hosting the service
You haven't specified how you're calling the service
You haven't specified anything about the binding you're using
Until at least some of these are clarified I think the question is unanswerable. If you could edit your question to include these points I'll edit my answer.
I have a WCF service method that sends back a MembershipCreateStatus (System.Web.Security) to the calling method. When I look at the service definition it has recreated the enum as a type of MyProject.MyWebService.MembershipCreateStatus so it is essentially a completely different object.
Is there a way in which I can tell the service definition to use the System.Web.Security class instead, even though it is this within the WCF service?
You you can. You need to use the KnownTypeAttribute class to decorate the DataContract in your WCF Service to specify that the enum is of type System.Web.Security.MembershipCreateStatus I'm not aware of the details of your environment but in general unless you control both the WCF service and the consuming clients, I would carefully research the support requirements and the possibility of a future change to the enum causing backwards compatibility issues with clients that are consuming this enum. Also to consider is a scenario where a non .NET client could consume your WCF service. In that case you need to consider if using the System.Web.Security.MembershipCreateStatus enum is a good idea versus implementing your own statuses for Member creation. Here is another question on StackOverflow with a good discussion on this topic.
For example see the following code below
[ServiceContract]
public interface IMembershipService
{
[OperationContract]
CreateMemberResponse CreateMember(ApplicationUser userToCreate);
}
[DataContract]
[KnownType(typeof(System.Web.Security.MembershipCreateStatus))]
public class CreateMemberResponse
{
[DataMember]
public MembershipCreateStatus Status { get; set; }
}
[DataContract]
public class ApplicationUser
{
public bool ReturnSuccess { get; set; }
public ApplicationUser()
{
ReturnSuccess = true;
}
}
You can write a test against this service as follows and this test will succeed.
[TestClass]
public class MembershipStatusInvocationTests
{
[TestMethod]
public void CreateMemberShouldReturnMembershipCreateStatusEnum()
{
var client = new MembershipServiceClient();
var response = client.CreateMember(new ApplicationUser {ReturnSuccess = true});
Assert.IsInstanceOfType(response.Status, typeof(System.Web.Security.MembershipCreateStatus));
}
}
For more information on the KnownTypeAttribute class see here
I have a wcf application in which i have used Entitity Framework and have implemented dbContext for querying the database.
When I view the svc file in browser it exposes the operations.
I have interface class like this:
[ServiceContract]
public interface IService1
{
[OperationContract]
List<BooksModels> GetBooksList();
[OperationContract]
BooksModels GetBook(int id);
}
I have the implementation in the svc.cs file like this
public List<BooksModels> GetBooksList()
{
MVCEntity en = new MVCEntity();
return en.book.ToList();
}
public int GetBookId(int id)
{
//return db.book.Find(id);
return 1;
}
and the BooksModels class is like this
[DataContract]
public class BooksModels
{
[Key]
[DataMember]
public int BookId { get; set; }
[DataMember]
public string BookName{get;set;}
}
and have the config file the default one as created when creating wcf service application .
but when i invoke GetBooksList the service from MVC wcf client it gives me the following error:
Failed to invoke the service. Possible causes: The service is offline
or inaccessible; the client-side configuration does not match the
proxy; the existing proxy is invalid. Refer to the stack trace for
more detail. You can try to recover by starting a new proxy, restoring
to default configuration, or refreshing the service.
but when i invoke the second method that returns 1.
i examined that when the service uses the dbContext to return data it gives error and
is fine when not.
I have gone through various blogs and also the questions in stackoverflow but didn't help.
so how can this problem be addressed.
Thanks
I think it's a problem of serialization. Make a serialisation test from you're business layer that take List from entity framework and serialize then deserialize it and compare before and after serialization.
Use DataContractSerializer :
DataContractSerializer serialiser = new DataContractSerializer(typeof(List<BooksModels>));
List<BooksModels> expected = business.GetBooksList();
Stream stream = new MemoryStream();
serialiser.WriteObject(stream, expected);
stream.Position = 0;
List<BooksModels> actual = serialiser.ReadObject(stream) as List<BooksModels>;
Assert.IsNotNull(actual);
Assert.AreEqual(expected.Prop1, actual.Prop1);
Assert.AreEqual(expected.Prop2, actual.Prop2);
// ... //
If it doesn't work you probably use proxy in entity framework. turn off proxy :
context.ContextOptions.ProxyCreationEnabled = false;
I had the same problem. Thanks to Brice2Paris, I solved it by turning off the proxy of EF:
context.Configuration.ProxyCreationEnabled = false;
I have the following WCF interface that is exposed via net.tcp:
[ServiceContract]
public interface IMyWCFService
{
[OperationContract]
Response ProcessRequest(Request request);
}
This is driven by the following classes (much simplified for the purposes of this question):
[Serializable]
public abstract class Message
{
[XmlAttribute]
public string Sender { get; set; }
[XmlAttribute]
public string Recevier { get; set; }
}
[Serializable]
public abstract class Response : Message
{
[XmlAttribute]
public int EventCode { get; set; }
}
[Serializable]
public abstract class Request : Message
{
[XmlAttribute]
public string SourceSystem { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringRequest : Request
{
[XmlElement]
public string Payload { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringResponse : Response
{
[XmlElement]
public string Payload { get; set; }
}
Note : We use XMLSerializer rather than DataContractSerializer as these classes have to be compatible with legacy systems that are .NET 2 based.
As the interface uses the abstract Request/Response classes in the ProcessRequest method we have to declare StringResponse / StringRequest as ServiceKnownType, for example:
[ServiceContract]
[ServiceKnownType(typeof(StringRequest))]
[ServiceKnownType(typeof(StringResponse))]
public interface IMyWCFService
{
[OperationContract]
ResponseMessage ProcessRequest(RequestMessage request);
}
This works perfectly and all is good in the world, however.....
The WCF listener is just one component of a much larger framework and the classes described above are used throughout. We have also designed the framework to allow us to add new types of Request/Response messages with relative ease. For example I might add:
public class CustomRequest : Request
{
public MyCustomXmlSerialisableRequestObject Payload { get; set; }
}
public class CustomResponse: Response
{
public MyCustomXmlSerialisableResponseObject Payload { get; set; }
}
Which also works fine until I get the the WCF service interface. When we add a new custom request/response pair we also need to update the ServiceKnownType on the interface to include them. Which then means I have to redeploy the service. So the question is - is there any way I can avoid having to update the interface?
As an example when we used remoting we could pass through any objects we liked as long as they were serialisable so I assume/hope that there is a similar solution in WCF.
EDIT : Update
Following the guidance found here:
http://ashgeek.blogspot.com/2011/02/wcf-serialization-dynamically-add.html
I seem to be on the right track. However when I update the client service reference it pulls in all the dynamically types into the service reference. Which is undesirable as not all clients need to, or should, know about all messages that derive from Request/Response
More importantly I seem to lose the the ServiceClient class that is used to push messages, e.g:
// Client proxy class goes AWOL after service reference update
var client = new MyServiceReference.Client();
var responseMessage = client.ProcessRequest(requestMessage)
At the beginning you are mentioning that you need compatibility with .NET 2.0 services but in the same time you are complaining that something which worked in .NET remoting doesn't work in WCF - you are limited by features possible with .NET 2.0 web services where both server and client must know about transferred types on the service layer = types must be in service description and WSDL. Moreover because you decided to use XmlSerializer you generally lost most of the ways how to achieve that:
ServiceKnowType can load known types from static method
KnownTypes defined in configuration (requires DataContractSerializer)
DataContractResolver (only WCF 4) and loading all derived types on startup (requires DataContractSerializer)
Passing .NET type information in messages (requires NetDataContractSerializer and custom behavior) = generally this is the same functionality as in remoting and it demands sharing types between service and client and both service and client must be .NET application using WCF stuff.
With XmlSerializer you have one option
Return XElement and receive XElement in your service operation and deal with XML by yourselves - doesn't work in .NET 2.0
Edit:
There is no dynamic behavior in service description. It is created only once when the host starts and after that doesn't change until you restart the host. If you need subset of WSDL per client you need separate endpoint for each client and you must define exactly which data contracts should be exposed on each endpoint.