Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Tuesday, 18 March 2014

System.ServiceModel assembly is not marked as serializable


Most frustrating thing you ever going to come across is debugging web services, but not if you know the tricks & tools which you could use to get to the root of problem causing the error in the services at the first place. Web services related errors can be deceiving and forcing you to look at places that work perfectly. 

Today I have been working on a custom WCF web services which was trying to download a document from a SharePoint list to a workflow server, it worked perfectly until we decided to change the way our workflow server authenticates(for some reason), so we created a proxy which will be used to call custom WCF service from workflow server instead of adding a web reference.

As soon as we changed the way we were calling custom WCF web services, we started getting this error,


 Description: Type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' in Assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=""' is not marked as serializable.
Server stack trace:
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)

So looking at error initially, I thought it because we are not serializing the document who's details were wrapped up within a object, so I added this "[Serializable]" to our document class,


    [DataContract]
    [Serializable]
    public class DocumentObject
    {
        [DataMember]
        public string documentName;
        [DataMember]
        public byte[] data;
    }

   [ServiceContract]
   public interface IDocuments
   {
        [OperationContract]
        DocumentObject[] GetDocumentsFromList(string listName, string[] documentNames);
   }


On the other side when we tried to deserialize the document I still kept getting this error,

Type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' in Assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=f4tue731934e089' is not marked as serializable.
 Server stack trace:
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)


Since the error saying that, something went wrong while deserializing the object but why though ? I tried using BinaryFormatter to desearlize the method's response from webservices but it didn't helped as same code worked when there was no proxy... anyway, I added includeExceptionDetailInFaults="true" to my custom application's web.config, which was calling the WCF web services, to get the inner or real exception,

<?xml version="1.0"?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
    </startup>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="debug">
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>


Adding above helped me get to the bottom of problem, which was the size of document it was trying to desearlize. Size limit was exceeding,

The InnerException message was 'There was an error deserializing the object of  type MySolution.WebServices.DocumentObject[]. The maximum array length quota (16384) has been exceeded while reading XML data. 

To fix the above, you can either change the web.config of your WCF service and change MaxArrayLength or you can do it within code,

XmlDictionaryReaderQuotas readerQuotas = new XmlDictionaryReaderQuotas();
readerQuotas.MaxArrayLength = 1024 * 1024 * 102;  //102 Mega bytes
readerQuotas.MaxDepth = int.MaxValue;
binding.ReaderQuotas = readerQuotas;


This solved the issue for me.

Thursday, 6 February 2014

Debugging WCF service using SvcTraceView on application server

Debugging web services can be pain, specially when they are hosted on a application server within a SharePoint farm. There are several ways one can debug web services though, e.g. using a visual studio remote debugger but what if it doesn't hit your web services breakpoints or your network administrator do?

Easiest way to figure out what's wrong with web services is to use SvcTraceViewer.exe e.g. why my code not able to find the hosted service or if the problem is with service account authentication.

Not so difficult to set it up, all you need to do is to add a some tag in your hosted WCF services and then open the SvcTraceViewer to view the created file.

Step-1:
Find hosted WCF service's web.config and add following tag to it within configuration tag. It will recycle the application pool of hosted service itself when you will save changes to web.config file.


<configuration>
<system.diagnostics>
    <trace autoflush="true" />
    <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
            <listeners>
               <add name="sdt" 
                   type="System.Diagnostics.XmlWriterTraceListener" 
                   initializeData= "WcfConfigExample.e2e" />
            </listeners>
         </source>
    </sources>
</system.diagnostics>
</configuration>


Step-2:
Find SvcTraceViewer.exe within installed Visual studio files, location might change depending on version of VS, however since I am using VS 2010 ultimate so I find EXE file at,

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools

In some cases you will need to open the EXE file as administrator.

Step-3:
Do something to trigger WCF services and check folder where web.config file exists as that's where it will create "SdrConfigExample.e2e" log file, once you find it. just open the SvcTraceViewer.exe file.

Step-4:
Press Ctrl + O keys; Navigate to SdrConfigExample.e2e file to load log trace file in viewer.

Step-5:
Search for specific terms using Ctrl + F or just go through logs. You can also add custom logs to your WCF service and find them in here to figure out where exactly your code is getting exception at, You can find more about Trace Viewer at, http://msdn.microsoft.com/en-us/library/aa751795(v=vs.110).aspx

Friday, 13 September 2013

Getting SPUser using Login Name


Today I came across a situation where I had to let user input user name(s) and add them to people picker field within a list, unfortunately it has to be done using web services as user is interacting with SharePoint throw iPad.

Problem ?

User can input anything (any string) so I need to validate if user name(s) exists or not,  if user with login name provided has access to site collection page and at end if all previous are true then get SPUser object and add it to people picker field of custom list.

Solution

Assuming you already got or know how to get reference to SPSite, SPWeb, SPList and SPListItem objects

SPUser user = null;

if (!string.IsNullOrEmpty(userLogin))
{
       try
      {
           // statement below will throw exception if user doesn't exists
           if (site.RootWeb.DoesUserHavePermissions(userLogin, SPBasePermissions.Open))
          {
                try
               {
                    user = site.RootWeb.SiteUsers[userLogin];
                }
               catch (Exception ex)
              {
                   string other = string.Format("EXCEPTION! UserName '{0}' provided doesn't has permissions to root web", userLogin);
                   throw new SoapException(other, SoapException.ServerFaultCode, ex);
              }
           }
       }
       catch (Exception ex)
       {
            string other = string.Format("EXCEPTION! UserName '{0}' provided doesn't exists", userLogin);
            throw new SoapException(other, SoapException.ServerFaultCode, ex);
       }

       SPFieldUserValueCollection uvCollection = new SPFieldUserValueCollection();
       SPFieldUserValue uValue = new SPFieldUserValue(web, user.ID, user.LoginName);
       uvCollection.Add(uValue);

//Now update item just by item["People PIcker Field Name"] = uvCollection;
}

That's it, above code worked for me so it should work for you.

Lesson learned

I tried to use RootWeb.Users[userLogin] but it didn't worked for me as it does not return users who have access through a group whereas I had some user's given access to root web through groups only.

RootWeb.AllUsers[userLogin] is a dangerous function as  it returns list of all users who ever had access to web (even ones who don't have access to web anymore now).

web.EnsureUser() method creates a user if not exists already which I couldn't use in this scenario as client input is being used to validate SPUser object.

Wednesday, 19 June 2013

Convert WSDL file to a C# class and then DLL


Today I was trying to update a web reference of custom web services in my project but because of some reason visual studio 2010 wasn't getting the latest methods I added even though I could see them using browser. Hence to get around problem I converted WSDL file into C# class and then into a dll.

You can target dll to any framework you would like. In order to get DLL in .Net 4.0 framework try following steps;

For getting you library dll in.Net 4.0 framework you will require Visual Studio 2010 tools installed on your machine, however for it in .Net 3.5 framework you will need Visual Studio 2008.


  1. Open Visual Studio Command prompt as a administrator by going to Start menu > All Programs > Microsoft Visual Studio 2010 > Visual Studio Tools > Visual Studio Command Prompt (2010).
  2. Put your WSDL file in somewhere accessible e.g. in this case I will put it in my C drive and getting output CS class in C drive too but you can change it. Type this command and press okay,

    C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>wsdl /l:C# /out:C:\OutPutClassName.cs C:\myWebService.wsdl
  3. Above command will generate a CS class in your C drive, now if you want to convert this class into dll in .Net 4.0, type this command;

    C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>csc /target:library /r:System.Data.dll /r:System.Data.Services.Client.dll /out:C:\OutPutClassName.dll C:\OutPutClassName.cs

    Above command will get you class library dll in .Net 4.0 framework.
  4. In order to get ..Net 3.5 use this command,

    C:\Program Files\Microsoft Visual Studio 9.0\VC>csc /target:library /r:System.Data.dll  /out:C:\OutPutClassName.dll C:\OutPutClassName.cs
MSDN Reference: http://msdn.microsoft.com/en-us/library/bb332338.aspx#msdnwcfhc_topic6