Server side interface:
[OperationContract]
[WebGet(UriTemplate = "GetCardInfoByCardNumber/?cardNumber={cardNumber}&SerialNumber={SerialNumber}&token={token}", ResponseFormat = WebMessageFormat.Json)]
IList<Cards> GetCardInfoByCardNumber(string cardNumber, string SerialNumber, string token);
Server side implementation:
public IList<Cards> GetCardInfoByCardNumber(string cardNumber, string SerialNumber, string token)
{
if (BaseClass.HasPermission(token))
return cm.GetCardInfoByCardNumber(cardNumber, SerialNumber);
else
return null;
}
Client side:
class Program
{
static void Main(string[] args)
{
TestResWCF();
Console.ReadLine();
}
static List<Cards> TestResWCF()
{
List<Cards> a = null;
string ServiceUri = "http://192.168.15.18:8089/GetCardInfoByCardNumber/?cardNumber=HH-120109-017&SerialNumber=&token=123456";
WebClient proxy = new WebClient();
proxy.Encoding = Encoding.UTF8;
proxy.DownloadStringCompleted += new DownloadStringCompletedEventHandler
(
(s, e) =>
{
Stream stream = new MemoryStream(Encoding.Unicode.GetBytes(e.Result));
DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(List<Cards>));
a = obj.ReadObject(stream) as List<Cards>;
}
);
proxy.DownloadStringAsync(new Uri(ServiceUri));
return a;
}
List<Cards> a return empty string always! How to return data? Thank you very much!
Do you have any example? sorry for my bad english
Can you share the code for the "Cards" and "Card" classes?
I'm very sure that most likely, it is not decorated with [DataContract] and [DataMember] properly. You may have decorated the type with [DataContract], but forgotten to annotate the members you want with [DataMember]. Or alternatively, you may not have decorated them at all, and something else is happening behind the scenes. In 99% of the scenarios, misdecoration or improper decoration or mis-initialization of the serializer is the reason this error occurs.
If you did decorate it properly, there may be some other problems. It's hard to tell with 100% certainty from just the detail you've provided, so I'd enable tracing to generate tracing logs (that you can then view/share with SvcTraceViewer) and turn on debug exceptions (by turning on the includeExceptionDetailInFaults setting).
Related
I haven't found a clear answer on this. so if there is already a question about this, my bad.
I have a WCF service that pushes data via a callback method to connected clients. this callback method is oneway. so everytime there is new data I loop over the connected users and push the data.
The problem I have right now is when a client disconnects it throws an error and the channel becomes faulted.
I always thought that oneway didn't care if the message arrives at the destination. So if there's no client, then bad luck. but no exception.
but there is an exception and that exception faults the channel.
Now I've read somewhere that if you enable reliable sessions, that the exception won't fault the channel. Is this true?
How can I prevent that the channel goes into faulted state when an exception happens on a oneway call?
The list of registered and avaiable clients you can store in some resource such as List. Create another interface which exposes Connect/Disconnect methods. Connect is invoked when application starts off and within method client is added to the list. Disconnect in turn is invoked when application shuts down in order to get rid client of list. OnStartup/OnClosing events or their equivalents, depending on what kind of application client is, refer to moment when application is launched and closed. Such a solution ensures that resource stores only users avaiable to be reached.
[ServiceContract]
interface IConnection
{
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
}
[ServiceContract]
interface IServiceCallback
{
[OperationContract(IsOneWay = true)]
void CallbackMethod();
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
[OperationContract]
void DoSth();
}
class YourService : IConnection, IService
{
private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();
public void Connect()
{
var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.All(client => client != newClient))
Clients.Add(newClient);
}
public void Disconnect()
{
var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.Any(cl => cl == client))
Clients.Remove(client);
}
public void DoSth()
{
foreach(var client in Clients)
client.CallbackMethod();
}
}
At the end expose another endpoint with IConnection so that client can create proxy meant to be used only for connection/disconnection.
EDIT:
I know it has been a while since I posted an answear but I did not find in order to prepare an example. The workaround is to let service's interface derive IConnection and then expose only service as an endpoint. I attach simple example of WCF and WPF app as client. Client's application violates MVVM pattern but in this case it is irrelevant. Download it here.
To add on what Maximus said.
I've implemented this pattern in a class where clients can subscribe to get updates of internal states of a system, so a monitoring client can show graphs and other clients do other stuff like enabling/disabling buttons if some state is active.
It removes faulted channels from the list when they fail. Also all current states are sent when a client connects.
here's the code, hope it helps!
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
private struct SystemState
{
public string State;
public string ExtraInfo;
}
private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();
private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();
public void RegisterClient(string name, string system)
{
lock (mCallbacks)
{
IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();
Key<string> key = new Key<string>(name, system);
if (!mCallbacks.ContainsKey(key))
{
mCallbacks.Add(key, callback);
}
else
{
mCallbacks[key] = callback;
}
foreach (KeyValuePair<string, SystemState> s in mStates)
{
mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
}
}
}
public void UnregisterClient(string name)
{
lock (mCallbacks)
{
outer: foreach (var key in mCallbacks.Keys)
{
if (key.Key1 == name)
{
mCallbacks.Remove(key);
goto outer;
}
}
}
}
public void SetState(string system, string state, string extraInfo)
{
lock (mCallbacks)
{
List<Key<string>> toRemove = new List<Key<string>>();
SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
SystemState systemState;
if (!mStates.TryGetValue(system, out systemState))
mStates.Add(system, s);
else
mStates[system] = s;
foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
{
try
{
callback.Value.ServiceCallback(system, state, extraInfo);
}
catch (CommunicationException ex)
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
catch
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
}
foreach (Key<string> key in toRemove)
mCallbacks.Remove(key);
}
}
}
How can I add a custom section to WSDL that's directly under wsdl:definitions? Something like this:
I've tried stuff like using custom attributes that implement IWsdlExportExtension, but I havent gotten even close to the result I need and I'm not sure if that's the right way to do this.
Is that even possible or should I just paste that section into file and specify externalMetadataLocation in web.config?
The wsdl from your question has been genereted from asmx. If you want to do the same you should use IVIS library and decorate your class with ISService attrubute. For WCF you should do next:
[CustomAttribute]
public class Service1 : IService1
{
public void DoWork()
{
}
}
public class CustomAttribute:Attribute, System.ServiceModel.Description.IWsdlExportExtension, System.ServiceModel.Description.IWsdlImportExtension, IContractBehavior
{
public void ExportContract(System.ServiceModel.Description.WsdlExporter exporter, System.ServiceModel.Description.WsdlContractConversionContext context)
{
BeforeImport(exporter.GeneratedWsdlDocuments, exporter.GeneratedXmlSchemas, new List<XmlElement>());
}
public void BeforeImport(System.Web.Services.Description.ServiceDescriptionCollection wsdlDocuments, System.Xml.Schema.XmlSchemaSet xmlSchemas, ICollection<XmlElement> policy)
{
//throw new NotImplementedException();
var xdoc = new XmlDocument();
var element = xdoc.CreateElement("ivis","WebServiceInfo", "ivis");
var node = xdoc.CreateNode(XmlNodeType.Element, "Identifier", "ivis");
node.InnerText = "URN:IVIS:100001:ISS-IeM";
element.AppendChild(node);
/// and so on :)
wsdlDocuments[0].Extensions.Add(element);
}
}
Body of all others methods for implemented interfaces can be empty.
This is in first approach.
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);
}
Consider the following very basic WCF service implementation:
public enum TransactionStatus
{
Success = 0,
Error = 1
}
public class TransactionResponse
{
public TransactionStatus Status { get; set; }
public string Message { get; set; }
}
[ServiceContract]
[XmlSerializerFormat]
public interface ITestService
{
[OperationContract]
TransactionResponse DoSomething(string data);
}
public class TestService : ITestService
{
public TransactionResponse DoSomething(string data)
{
var result = ProcessData(data); // may throw InvalidOperationException
return new TransactionResponse()
{
Status = TransactionStatus.Success,
Message = result
};
}
private string ProcessData(string data)
{
if (data = "foobar")
throw new InvalidOperationException();
return data;
}
}
In the instance that the DoSomething method does throw an InvalidOperationException, I would like to intercept the fault and return a TransactionResponse object, rather than have WCF raise a FaultException with the client. How can I do this without surrounding each method body in a huge try catch statement? Is there some where I can hook into? Can I do this with some sort of attribute or something? An example of how I would like to handle it can be demonstrated using ASP.NET MVC:
public class ApiController : BaseController
{
protected override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
var message = HttpContext.IsDebuggingEnabled ? ex.ToString() : ex.Message;
_logger.Error("Error processing request for controller {0}, action {1}",
filterContext.RequestContext.RouteData.Values["controller"],
filterContext.RequestContext.RouteData.Values["action"]);
_logger.Error(ex.ToString());
filterContext.ExceptionHandled = true;
filterContext.Result = ToXml(new ApiResult(false)
{
Message = message
});
}
// ...
}
Using the above method in MVC, I can ensure that no matter which controller action throws an exception, I can handle it and return an appropriately formatted ActionResult containing the necessary info. Is there a way to do this kind of thing with WCF?
Check out the WCF IErrorHandler interface - it allows you to centrally define one way in your service implementation to catch all exceptions and either swallow them, or convert them to WCF-friendly SOAP exceptions. This will make sure the channel between the client and the server isn't faulted, e.g. it can still be used after this call failed.
I don't understand why you'd want to "catch" the SOAP faults and convert those to something else, though.... nor do I know of any support that WCF would give you. The basic assumption is: catch .NET exceptions and convert them into interoperable SOAP faults
So here's a super simple interface for doing rest in WCF.
[ServiceContract]
public interface IRestTest
{
[OperationContract]
[WebGet(UriTemplate="Operation/{value}")]
System.IO.Stream Operation(string value);
}
It works great, until i try to pass a string with periods in it, such as a DNS name... I get a 404 out of asp.net.
Changing the UriTemplate to stick parameters into the query string makes the problem go away. Anyone else see this or have a workaround?
That is true that a path part cannot contain a period or many other special characters for that matter. I experienced the same problem a while back and received an answer from TechNet team stating that querystring is your only option the could find. Sorry
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/d03c8331-1e98-4d5d-82a7-390942a93012/
I have a service with almost the exact signature. I can pass values that have a "." in the name. For example, this would work on mine:
[OperationContract]
[WebGet(UriTemplate = "category/{id}")]
string category(string id);
with the url http://localhost/MyService.svc/category/test.category I get the value `"test.category" passed in as the string value.
So there must be some other issue. how are you accessing the URL? just directly in the browser? Or via a javascript call? Just wondering if it is some error on the client side. The server passes the value just fine. I would recommending trying to access the url in your browser, and if it doesn't work then post exactly what URL you are using and what the error message was.
Also, are you using WCF 3.5 SP1 or just WCF 3.5? In the RESTFul .Net book I'm reading, I see there were some changes with regards to the UriTemplate.
And finally, I modified a simple Service from the RESTFul .Net book that works and I get the correct response.
class Program
{
static void Main(string[] args)
{
var binding = new WebHttpBinding();
var sh = new WebServiceHost(typeof(TestService));
sh.AddServiceEndpoint(typeof(TestService),
binding,
"http://localhost:8889/TestHttp");
sh.Open();
Console.WriteLine("Simple HTTP Service Listening");
Console.WriteLine("Press enter to stop service");
Console.ReadLine();
}
}
[ServiceContract]
public class TestService
{
[OperationContract]
[WebGet(UriTemplate = "category/{id}")]
public string category(string id)
{
return "got '" + id + "'";
}
}
Here's an example HttpModule that fixes the 'period' when they occur in REST parameters. Note that I've only seen this happen in the Development Server (aka Cassini), in IIS7 it seems to work without this "hack". The example I've included below also replaces the file extension '.svc' which I adapted from this answer. How to remove thie “.svc” extension in RESTful WCF service?
public class RestModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest +=
delegate
{
HttpContext ctx = HttpContext.Current;
string path = ctx.Request.AppRelativeCurrentExecutionFilePath;
int i = path.IndexOf('/', 2);
if (i > 0)
{
int j = path.IndexOf(".svc", 2);
if (j < 0)
{
RewritePath(ctx, path, i, ".svc");
}
else
{
RewritePath(ctx, path, j + 4, "");
}
}
};
}
private void RewritePath(HttpContext ctx, string path, int index, string suffix)
{
string svc = path.Substring(0, index) + suffix;
string rest = path.Substring(index);
if (!rest.EndsWith(ctx.Request.PathInfo))
{
rest += ctx.Request.PathInfo;
}
string qs = ctx.Request.QueryString.ToString();
ctx.RewritePath(svc, rest, qs, false);
}
}