0
votes

The reason I am asking this as a separate question to the multitude of others is that I am using Castle Windsor 3.0 as my DI framework and thus configuring my endpoints through the WCF Facility of CW. I am unable to find any resolution with this setup.


:: Update ::

Thanks for the comments.

The project is a standard WCF Service Application, which feeds off a number of standard class libraries connecting to underlying funcitonality (SQl Server etc). The Web services that exist in the project are standard Wcf Services (.svc) and will be hosted in IIS (tested in the default VS debugging server) will be consumed by an ASP.NET MVC3 Web Application.

The services are hooked into the Windsor Container at both client and service sides of this.

Service side:

<%@ ServiceHost 
Language="C#" 
Debug="true" 
Service="FileDownloadService" 
CodeBehind="FileDownloadService.svc.cs" 
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, 
    Castle.Facilities.WcfIntegration" %>

Thus, the Windsor WCFacility is responsible for all dependency injection resolution in both the MVC3 app, and the WCF Service Application.

The main reason I am concerned over the configuration (which I know uses values which I have obtained form tutorials/walkthroughs/SO questions) is because I am unsure if Windsor is 100% picking this configuration up on the service side - opinions?

I have also updated the code snippets to show the current impl.

[DataContract]
    public enum FileTypeEnum
    {
        [EnumMember]
        Generic = 1,

        [EnumMember]
        TXT = 2,

        [EnumMember]
        XLS = 3,

        [EnumMember]
        PDF = 4,

        [EnumMember]
        DOC = 5
    }

The Web Service solution, which has the WCF web service in, defines the following contract:

[ServiceContract]
public interface IFileDownloadService
{
    [OperationContract]
     FileDownloadReturnMessage DownloadFile(FileDownloadMessage request);
}

The return type contract is:

[MessageContract]
public class FileDownloadReturnMessage : IDisposable
{
    public FileDownloadReturnMessage(FileMetaData metaData, Stream stream)
    {
        FileByteStream = stream;
    }

    [MessageBodyMember(Order = 1)]
    public Stream FileByteStream;

    [MessageHeader(MustUnderstand = true)]
    public FileMetaData DownloadedFileMetadata;

    public void Dispose()
    {
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }

and the request contract is:

[MessageContract]
public class FileDownloadMessage
{
    [MessageHeader(MustUnderstand = true)]
    public FileMetaData FileMetaData;
}

and:

[DataContract(Namespace = "http://schemas.acme.it/2009/04")]
public class FileMetaData
{
    public FileMetaData(string fileName, string remoteFilePath)
    {
        FileName = fileName;
        RemoteServerFilePath = remoteFilePath;
        FileType = FileTypeEnum.Generic;
    }

    public FileMetaData(string fileName, string remoteFilePath, FileTypeEnum? fileType)
    {
        FileName = fileName;
        RemoteServerFilePath = remoteFilePath;
        FileType = fileType;
    }

    [DataMember(Name = "FileType", Order = 0, IsRequired = true)]
    public FileTypeEnum? FileType;

    [DataMember(Name = "FileName", Order = 1, IsRequired = true)]
    public string FileName;

    [DataMember(Name = "RemoteFilePath", Order = 2, IsRequired = true)]
    public string RemoteServerFilePath;
}

The configuration on the server for the Windsor injected service is:

.Register(Component.For<IFileDownloadService>()
       .ImplementedBy<FileDownloadService>()
       .Named("FileDownloadService")
       .AsWcfService(new DefaultServiceModel()
       .AddEndpoints(WcfEndpoint
              .BoundTo(new BasicHttpBinding
                   {
                       MaxReceivedMessageSize = 2147483647,
                       MaxBufferSize = 2147483647,
                       MaxBufferPoolSize = 2147483647,
                       TransferMode = TransferMode.Streamed,
                       MessageEncoding = WSMessageEncoding.Mtom,
                       ReaderQuotas = new XmlDictionaryReaderQuotas
                            {
                              MaxDepth = 2147483647,
                              MaxArrayLength = 2147483647,
                              MaxStringContentLength = 2147483647,
                              MaxNameTableCharCount = 2147483647,
                              MaxBytesPerRead = 2147483647
                            }
                   }))
              .Hosted()
              .PublishMetadata())
      .LifeStyle.PerWcfOperation())

and the client configuration for the endpoint is:

_container.Register(Component.For<IFileDownloadService>()
                    .AsWcfClient(new DefaultClientModel
                        {
                            Endpoint = WcfEndpoint
                                .BoundTo(new BasicHttpBinding
                                    {
                                        MaxReceivedMessageSize = 2147483647,
                                        MaxBufferSize = 2147483647,
                                        MaxBufferPoolSize = 2147483647,
                                        TransferMode = TransferMode.Streamed,
                                        MessageEncoding = WSMessageEncoding.Mtom,
                                        ReaderQuotas = new XmlDictionaryReaderQuotas
                                            {
                                                MaxDepth = 2147483647,
                                                MaxArrayLength = 2147483647,
                                                MaxStringContentLength = 2147483647,
                                                MaxNameTableCharCount = 2147483647,
                                                MaxBytesPerRead = 2147483647
                                            }
                                    })
                                .At(ConfigurationManager.AppSettings["FileDownloadAddress"])
                            }));

As far as I know these endpoint configurations have to match, which they do. But for some reason hitting the method:

var commandResult = _downloadService.DownloadFile(command);

results in an exception with the following stack trace:

Ex Message:     The remote server returned an unexpected response: (400) Bad Request.
Source:     Castle.Facilities.WcfIntegration
Target Site:    Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor+<>c__DisplayClass1 -> Void <PerformInvocation>b__0(Castle.Facilities.WcfIntegration.WcfInvocation)
Stack Trace:    at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.<>c__DisplayClass1.<PerformInvocation>b__0(WcfInvocation wcfInvocation)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.ApplyChannelPipeline(Int32 policyIndex, WcfInvocation wcfInvocation, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.<>c__DisplayClass4.<ApplyChannelPipeline>b__3()
   at Castle.Facilities.WcfIntegration.WcfInvocation.Proceed()
   at Castle.Facilities.WcfIntegration.RepairChannelPolicy.Apply(WcfInvocation wcfInvocation)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.ApplyChannelPipeline(Int32 policyIndex, WcfInvocation wcfInvocation, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder, Action`1 action)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder)
   at Castle.Facilities.WcfIntegration.Async.WcfRemotingAsyncInterceptor.PerformInvocation(IInvocation invocation, IWcfChannelHolder channelHolder)
   at Castle.Facilities.WcfIntegration.Proxy.WcfRemotingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.IWcfChannelHolderProxy_2.FakeDownloadTest(FakeDownloadTestRequest request)
   at cpfe.DAL.Repositories.FileDownloadRepository.DownloadFile(IEnumerable`1 fileIds, TenantDTO tenant, String zipPackageName, UserDTO user) \..\..\..\FileDownloadRepository.cs:line 44

Does anybody have any clue as to why this is happening?

Thanks in advance!

3
It may be helpful to know a little about how you are hosting the service - I see you tagged the question asp .net mvc but you said a web service project in your description - what sort of project is it and what is going on in there with regards configuring the container, routes, etc? I just tried @Hyralex answer using a couple of console applications and it worked fine so that would narrow it down to the service host I think.kmp
@kmp - updated the question, cheers for the help.M05Pr1mty
Can we see the contract of FileTypeEnum ?Hyralex
Ok. Contract and configuration seems to be good. I don't know castle-windsor, so maybe it's the problem... Just for this service, maybe you can use default factory of wcf service. Use ChannelFactory to build your client proxy.Hyralex

3 Answers

2
votes

I think you can't transfer in the message body a Stream and another data. You need to use a MessageContract to add custom data in the header of the message, and the stream in the body.

You need to use a specific contract. You can use class with messagecontract which encapsulate your current contract. Like this :

[ServiceContract]
public interface IFileDownloadService
{
    [OperationContract]
    StreamResponse DownloadFile(DownloadFileCommandRequest command);
}

[MessageContract]
public class StreamResponse
{
    [MessageHeader()]
    public CommandResult<FileDownloadDTO> {get; set;}

    [MessageBodyMember(Order = 1)]
    public Stream FileByteStream { get; set; }
}

[MessageContract]
public class DownloadFileCommandRequest 
{
    [MessageBodyMember(Order = 1)]
    public DownloadFileCommand FileCommand {get; set;}
}

And remove the stream property in FileDownloadDTO class. Maybe if you only remove the DataMember attribute it's ok.

[DataContract]
public class FileDownloadDTO : IDisposable, IDTOBase
{
   [DataMember]
   public string FileName { get; set; }

   [DataMember]
   public long Length { get; set; }

   [DataMember]
   public int Id { get; set; }
}

Here, an exemple of the client configuration.

<binding name="FileHttpBinding"  closeTimeout="04:01:00"
                openTimeout="04:01:00" receiveTimeout="04:10:00" sendTimeout="04:01:00"
                allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
                transferMode="Streamed" messageEncoding="Mtom">
                <readerQuotas maxDepth="128" maxStringContentLength="2147483647" maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <security mode="None">
                </security>
            </binding>
2
votes

I reproduced 400 error and it is gone after a few changes made in code:

  1. Use TransferMode = TransferMode.StreamedResponse, instead of Streamed. I applied that only to client configuration. Server still has Streamed.
  2. Add default (parameterless) constructor to FileDownloadReturnMessage. It is necessary for MessageContract deserialization. Code - public FileDownloadReturnMessage() { }

Funny thing that now I cannot reproduce error back and everything works fine even with TransferMode.Streamed

Let me know if that helped.

I can publish my code if you still cannot fix the issue.

Interesting thing is that 400 error is generated by WCF in client. Server always returns 200 and correct data if you check traffic in fiddler.

0
votes

Please check your connection string at service side. If you are using windows authentication in connection string then try to use SQL authentication by creating sql user for your database