Return a WCF EF4 Entity as JSON - wcf

My service interface is:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "HelloJSON/{name}")]
string HelloJSON(string name);
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetEmployees")]
List<Employee> GetEmployees();
}
My implementation is:
public class MyService : IMyService
{
public string HelloJSON(string name)
{
return string.Format("Hello {0} in JSON", name);
}
public List<Employee> GetEmployees()
{
using (DBEntities ctx = new DBEntities())
{
List<Employee> emp = new List<Employee>();
emp = (from e in ctx.Employee select e).ToList();
return emp;
}
}
}
When I call the first method I get something like "Hello pepe in JSON", that's ok.
When I call the second method and set a breakpoint on line "return emp;" I get the list of the employees(there are 6 records from the database), but in IE I get this:
Internet Explorer cannot display the webpage
and testing in Firefox all I get is a blank page with a blank body, no HTML, no data and no errors.
I think WCF can't serialize my default EF4 entities.
EDIT:
My final solution was something(not exactly) like this:
static string SerializeJSON<T>(T obj) {
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj); }

EF entities cannot be serialized by default you must add code generation to them.
Refer to this article on how to create Serializable entities.
called Self Tracking entities

The best way would be to enable RIA Services and expose JSON Endpoint, it does everything correctly.
http://blogs.msdn.com/b/davrous/archive/2010/12/14/how-to-open-a-wcf-ria-services-application-to-other-type-of-clients-the-json-endpoint-4-5.aspx
http://channel9.msdn.com/Shows/SilverlightTV/Silverlight-TV-26-Exposing-SOAP-OData-and-JSON-Endpoints-for-RIA-Services

Related

How to serialize a subclass using DataContracts

I just started programming a WCF project and there is a serialization issue I can't seem to fix. I'm using DataContracts and DataMembers to serialize my classes to XML.
I have a class TestResponse that includes a property TestParameter. This property contains one string parameter. Also, I have a subclass of TestParameter called TestParameterSubclass. I need to serialize this subclass. These three classes look like this:
TestResponse.cs
[DataContract(Namespace = Service.defaultNamespace, Name="TestResponse")]
public class TestResponse
{
[DataMember(Name = "TestParameter")]
public TestParameter param { get; set; }
}
TestParameter.cs
[DataContract(Namespace = Service.defaultNamespace, Name="TestParameter")]
[KnownType(typeof(TestParameterSubclass))]
public class TestParameter
{
[DataMember(Name="ParamName")]
public string ParamName {get; set;}
}
TestParameterSubclass.cs
[DataContract(Name="TestParameterSubclass")]
public class TestParameterSubclass : TestParameter
{
}
I have an OperationContract
[OperationContract]
[WebInvoke(
Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/calculateA")]
TestResponse CalculateA(TestRequest data);
and CalculateA
public TestResponse CalculateA(TestRequest request)
{
TestResponse tr = new TestResponse();
TestParameterSubclass parameterSubclass = new TestParameterSubclass();
tr.param = parameterSubclass;
tr.param.ParamName = "hai";
return tr;
}
which results in
<TestResponse>
<TestParameter i:type="a:TestParameterSubclass">
<ParamName>hai</ParamName>
</TestParameter>
</TestResponse>
However, I need the XML to use the subclass name as the element name, like this
<TestResponse>
<TestParameterSubclass>
<ParamName>hai</ParamName>
</TestParameterSubclass>
</TestResponse>
Does anyone know how to do this?
(I have a similar problem with deserializing the input XML, but I think both problems are related, so fixing this would (hopefully) also fix my other problem.)

WCF RESTful Service - HTTP POST Request

I developed a WCF Service with the following post method:
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "/InsertBearingData")]
bool InsertBearingData(String JSONString);
I am using Fiddler to formulate an HTTP POST Request for this method but, it is returning Status Code - 400 Bad Request. This is the request formulated:
Request Header:
Host: localhost:21468
Content-Length: 96
Content-Type: application/json
Request Body:
[{"start time":"29-03-2013 11:20:11.340","direction":"SW","end time":"29-03-2013 11:20:14.770"}]
Can you please tell me how to formulate a good request in order to get a succesful response?
There are a few issues in your code:
The data type of the parameter is string, but you're passing a JSON array to it; a string parameter requires a JSON string to be passed.
The body style of the operation is set to Wrapped, which means that the parameter should be wrapped in an object whose key is the parameter name, something like {"JSONString":<the actual parameter value>}
To receive a request like the one you're sending, you need to have an operation like the following:
[ServiceContract]
public interface ITest
{
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/InsertBearingData")]
bool InsertBearingData(MyType[] param);
}
[DataContract]
public class MyType
{
[DataMember(Name = "start time")]
public string StartTime { get; set; }
[DataMember(Name = "end time")]
public string EndTime { get; set; }
[DataMember(Name = "direction")]
public string Direction { get; set; }
}

WCF to WCF communication 401, HttpClient

I have a WCF REST service that needs to communicate with another WCF REST service.
There are three websites:
Default Web Site
Website1
Website2
If I set up both services in Default Web Site and connect to the other (using HttpClient) using the URI http://localhost/service then everything is okay.
The desired set-up is to move these two services to separate websites and rather than using the URI http://localhost/service, accessing the service via http://website1.domain.com/service still using HttpClient.
I received the exception:
System.ArgumentOutOfRangeException: Unauthorized (401) is not one of
the following: OK (200), Created (201), Accepted (202),
NonAuthoritativeInformation (203), NoContent (204), ResetContent
(205), PartialContent (206)
I can see this is a 401, but what is going on here?
Thanks
I think this is related to your setup for webservice. It is best if you just create GET,POST,Put,DELETE heartbeat calls for new services and then check those from fiddler. If you get 401, it may mean your app pool identity could not access something.
Steps to fix that:
Give user read/write/modify/execute/..similar rights at your WCF publish folder
Create app pool for this site in .net 4 integrated
Set this user to application pool identity, enable anonymous mode
Enable PUt,Delete verbs as well
Part of a heartbeat class in your service to test calls:
[DataContract]
public class StatusInfo
{
[DataMember]
public string MachineName { get; set; }
[DataMember]
public string IpAddress{ get; set; }
[DataMember]
public string Methodname { get; set; }
public override string ToString()
{
return "Machinename:" + MachineName + " ;IP:" + IpAddress + "; Method:" + Methodname;
}
}
private void ResolveStatus(StatusInfo statusInfo,string methodname)
{
try
{
var context = System.ServiceModel.OperationContext.Current;
RemoteEndpointMessageProperty property =
(RemoteEndpointMessageProperty)
context.IncomingMessageProperties[RemoteEndpointMessageProperty.Name];
statusInfo.IpAddress = property.Address;
statusInfo.MachineName = Environment.MachineName;
statusInfo.Methodname = methodname;
}catch(Exception ex)
{
}
}
/// <summary>
/// create task
/// </summary>
/// <param name="taskwrapped"></param>
[WebInvoke(Method = "POST", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo postcall()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "postcall");
ResolveStatus(statusInfo, "POST");
return statusInfo;
}
/// <summary>
/// edit task
/// </summary>
[WebInvoke(Method = "PUT", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo Edit()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "PUT");
return statusInfo;
}
//delete request with taskid
[WebInvoke(Method = "DELETE", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo DeleteCall()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "DELETE");
return statusInfo;
}
//delete request with taskid
[WebInvoke(Method = "DELETE", UriTemplate = "/{recordid}", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo DeleteCallWithParam(string recordid)
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "DELETE/"+recordid);
return statusInfo;
}
enter code here
I received the exception:
Who is "I"? One of the web services or some other client?
If I'm understanding things correctly, it's the receiving end that seems to be expecting a range of responses, 401 not being one of them. It maybe some error checking code that expects "this range" of responses and does X (and 401 isn't one of these, or there is no "default" method to account for x response?).
That said, 401, is an authorization error so check on possible ServiceAuthorizationManager and/or similar settings in place that isn't being met by "I" causing the 401 response in the first place....
Hth...
My guess is you are missing authorizaton headers or credentials.
Check this out :
Consume RESt API from .NET
How to authenticate with Rest-client based on HttpClient and .net4

Simple WCF POST with Uri Template

I thought this would be incredibly simple, but I must be missing something. I am trying to make a simple WCF POST request in conjunction with a UriTemplate. I have read numerous examples where people use a stream paramater as the last paramater, and this is supposed to pick up the POST body. I can only get this to work if the stream is the only paramater.
I've gone back to basics with a simple Hello World service.
Here is my code on the client
static string Test()
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://localhost:1884/MyAPI/Service.svc/HelloWorld");
req.Method = "POST";
req.ContentType = "text/plain";
Stream reqStream = req.GetRequestStream();
byte[] fileToSend = System.Text.UTF8Encoding.UTF8.GetBytes("sometext");
reqStream.Write(fileToSend, 0, fileToSend.Length);
reqStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
var sr = new StreamReader(resp.GetResponseStream());
return sr.ReadToEnd();
}
And this is the code on the service
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "HelloWorld")]
Stream HelloWorld(Stream content);
}
public Stream HelloWorld(Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World! " + text));
}
This all works fine. Then I make this change:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "HelloWorld/test/{test}")]
Stream HelloWorld(string test, Stream content);
}
public Stream HelloWorld(string test, Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World! " + text + test));
}
And change the client code to hit HelloWorld/test/sometext
I get a 500 Internal Server Error. I've tried about 10 different variations including using a ?key=value type UriTemplate, returning strings instead of streams etc, and no luck.
Feels like I'm missing some tiny little thing that is going to make this work, as I have seen countless examples of exactly this all over the web. Theirs works, mine doesn't.
Any ideas?
I am not sure what went wrong, but after trying everything, I resolved this by creating a new project and copying all the code over. Never worked out what the differences were, maybe something got corrupted
Edit: in the end we discovered we had to specify WebServiceHostFactory in the Service.svc. This was there by default in the new project
When streaming, the Stream must be the only parameter: http://msdn.microsoft.com/en-us/library/ms789010.aspx
You may be able to use message headers: Add filename and length parameter to WCF stream when Transfermode = Stream
You can use new single file WCF model to configure and adjust endpoint behaviour. I combined your contract and service class into one file to show you how to do this.
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace StreamService
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MergedEndpoint
{
[WebGet(RequestFormat = WebMessageFormat.Xml, UriTemplate = "Data/{someid}",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public string GetData(string someid)
{
return string.Format("You entered: {0}", someid);
}
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, UriTemplate = "HelloWorld",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Stream HelloWorld1(Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World from single file! " + text));
}
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, UriTemplate = "HelloWorld/test/{testparam}",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Stream HelloWorld2(string testparam, Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World from single file! " + testparam+ text));
}
}
}
Input parameters need to be same name as method params. Their type is also string. You need to do the conversion if you want different input param.
You need to create WCf project and add Global.asax file with routing info for this file. You may need to add reference to System.ServiceModel.Activation to setup routing.
Example:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("MergedEndpoint", new WebServiceHostFactory(), typeof(MergedEndpoint)));
}
Your Client code has one change to content type.
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://localhost:55166/MergedEndpoint/HelloWorld/test/234");
req.Method = "POST";
//req.ContentType = "text/plain";
req.MediaType = "HTTP/1.1";
req.ContentType = "application/json; charset=utf-8";
Stream reqStream = req.GetRequestStream();
byte[] fileToSend = System.Text.UTF8Encoding.UTF8.GetBytes("sometext");
reqStream.Write(fileToSend, 0, fileToSend.Length);
reqStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
var sr = new StreamReader(resp.GetResponseStream());
string outp = sr.ReadToEnd();
Console.WriteLine("Response:"+outp);
You can still read the raw content even if it is set to Json type.

WCF WebApi's Self-Hosting not accepting PUT verbs

I have put together a HTTP driven API using the WCF WebAPI that uses the PUT verb. When hosted inside of an MVC3 project that is hosted upon IIS Express, everything works as designed.
However, when I unit-test I'm occasionally wanting to test the transport aspects rather than just against my own resources. My unit-tests fail with a 405 - MethodNotAllowed. Again, exactly the same service hosted in IIS works (where I enabled the PUT and DELETE verbs in the configuration file).
How can I get the 'self-hosted' service, as used in my testing, to accept these verbs too?
The almost identical 'get' tests work, so I'm not expecting the concept of the following to be at fault.. hopefully...
[Test]
public void PutNewMachine()
{
// Create new record to add
var machine = new Machine
{
ID = 1,
Name = "One",
Description = "Machine #1",
Location = 1
};
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(
HttpMethod.Put,
HOST + "/1"))
{
request.Content = new ObjectContent<Machine>(machine);
using (var response = client.Send(request))
{
Assert.AreEqual(
HttpStatusCode.Created,
response.StatusCode,
"New record put should have been acknowledged "
+ "with a status code of 'Created'");
}
}
}
}
In the setup to the test, I'm preparing the end-points using the following Autofac code (and again this works for the 'Get'):
var builder = new ContainerBuilder();
builder
.Register(c => new FakeDatabase())
.As<IDatabase>()
.SingleInstance();
builder
.Register(c => new GenericRepository<Machine>(c.Resolve<IDatabase>()))
.As<IResourceRepository<Machine>>();
builder
.Register(c => new MachineService(c.Resolve<IResourceRepository<Machine>>()))
.As<MachineService>();
Container = builder.Build();
Scope = Container.BeginLifetimeScope();
host = new HttpServiceHost(typeof(MachineService), HOST);
host.AddDependencyInjectionBehavior<MachineService>(Container);
host.Open();
My service is defined in the following interface:
[ServiceContract]
public interface IResourceService<in TKey, TResource>
{
[WebGet(UriTemplate = "{key}")]
TResource Get(TKey key);
[WebInvoke(Method = "PUT", UriTemplate = "{key}")]
TResource Put(TKey key, TResource resource);
[WebInvoke(Method = "POST")]
TResource Post(TResource resource);
[WebInvoke(Method = "DELETE", UriTemplate = "{key}")]
void Delete(TKey key);
}
So, for example, if I have a MachineService, it implements the interface (both class MachineService : IResourceService<string, Machine> and ... : IResourceService<int, Machine> have been trialled - Get = OK, Put = Nothing.
EDIT: I seem to be bouncing between InternalServerError and MethodNotAllowed errors - only when using the self-hosting. I have ensured that I, as a user, have rights to open the port (Win7 + non-admin) but the results of that plus my choice of ports seems predicable functional for Get. "Post" seems to be having similar issues! :-(
EDIT2: Interface has now changed to which works!
[ServiceContract]
public interface IResourceService<in TKey, TResource>
{
[WebGet(UriTemplate = "{key}")]
TResource Get(TKey key);
[WebInvoke(Method = "PUT", UriTemplate = "{key}")]
TResource Put(HttpRequestMessage<TResource> resourceRequest, TKey key);
[WebInvoke(Method = "POST", UriTemplate = "{key}")]
TResource Post(HttpRequestMessage<TResource> resourceRequest, TKey key);
[WebInvoke(Method = "DELETE", UriTemplate = "{key}")]
void Delete(TKey key);
}
Doing PUT or POST works for me when I change the method signature to accept a HttpRequestMessage request instead of T itself.