System.UnsupportedException с использованием WCF на Windows Phone 7 - PullRequest
4 голосов
/ 19 марта 2010

Кто-нибудь мог общаться с помощью WCF на эмуляторе Windows Phone Series 7?

Я пытался последние два дня, и это просто происходит для меня. Я могу заставить обычный элемент управления Silverlight работать как в Silverlight 3, так и в Silverlight 4, но не в телефонной версии. Вот две версии, которые я пробовал:

Версия 1 - Использование асинхронного шаблона

BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost/wcf/Authentication.svc");
Wcf.IAuthentication auth1 = new ChannelFactory<Wcf.IAuthentication>(basicHttpBinding, endpointAddress).CreateChannel(endpointAddress);

AsyncCallback callback = (result) =>
{

    Action<string> write = (str) =>
    {
        this.Dispatcher.BeginInvoke(delegate
        {
            //Display something
        });
    };

    try
    {
        Wcf.IAuthentication auth = result.AsyncState as Wcf.IAuthentication;
        Wcf.AuthenticationResponse response = auth.EndLogin(result);
        write(response.Success.ToString());
    }
    catch (Exception ex)
    {
        write(ex.Message);
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
};

auth1.BeginLogin("user0", "test0", callback, auth1);

Эта версия разрывается на этой строке:

Wcf.IAuthentication auth1 = new ChannelFactory<Wcf.IAuthentication>(basicHttpBinding, endpointAddress).CreateChannel(endpointAddress);

Метание System.NotSupportedException. Исключение не очень наглядно, и стек вызовов также не очень полезен:

<sub>
   at System.ServiceModel.DiagnosticUtility.ExceptionUtility.BuildMessage(Exception x)
   at System.ServiceModel.DiagnosticUtility.ExceptionUtility.LogException(Exception x)
   at System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exception e)
   at System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress address)
   at WindowsPhoneApplication2.MainPage.DoLogin()
   ....
</sub>

Версия 2 - Блокировка вызова WCF

Вот версия, которая не использует асинхронный шаблон.

[System.ServiceModel.ServiceContract]
public interface IAuthentication
{
    [System.ServiceModel.OperationContract]
    AuthenticationResponse Login(string user, string password);
}

public class WcfClientBase<TChannel> : System.ServiceModel.ClientBase<TChannel> where TChannel : class {
        public WcfClientBase(string name, bool streaming)
            : base(GetBinding(streaming), GetEndpoint(name)) {
            ClientCredentials.UserName.UserName = WcfConfig.UserName;
            ClientCredentials.UserName.Password = WcfConfig.Password;
        }
        public WcfClientBase(string name) : this(name, false) {}

        private static System.ServiceModel.Channels.Binding GetBinding(bool streaming) {
            System.ServiceModel.BasicHttpBinding binding = new System.ServiceModel.BasicHttpBinding();
            binding.MaxReceivedMessageSize = 1073741824;
            if(streaming) {
                //binding.TransferMode = System.ServiceModel.TransferMode.Streamed;
            }
            /*if(XXXURLXXX.StartsWith("https")) {
                binding.Security.Mode = BasicHttpSecurityMode.Transport;
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            }*/
            return binding;
        }

        private static System.ServiceModel.EndpointAddress GetEndpoint(string name) {
            return new System.ServiceModel.EndpointAddress(WcfConfig.Endpoint + name + ".svc");
        }

        protected override TChannel CreateChannel()
        {
            throw new System.NotImplementedException();
        }
    }


auth.Login("test0", "password0");

Эта версия вылетает в конструкторе System.ServiceModel.ClientBase<TChannel>. Стек вызовов немного отличается:

<sub>
   at System.Reflection.MethodInfo.get_ReturnParameter()
   at System.ServiceModel.Description.ServiceReflector.HasNoDisposableParameters(MethodInfo methodInfo)
   at System.ServiceModel.Description.TypeLoader.CreateOperationDescription(ContractDescription contractDescription, MethodInfo methodInfo, MessageDirection direction, ContractReflectionInfo reflectionInfo, ContractDescription declaringContract)
   at System.ServiceModel.Description.TypeLoader.CreateOperationDescriptions(ContractDescription contractDescription, ContractReflectionInfo reflectionInfo, Type contractToGetMethodsFrom, ContractDescription declaringContract, MessageDirection direction)
   at System.ServiceModel.Description.TypeLoader.CreateContractDescription(ServiceContractAttribute contractAttr, Type contractType, Type serviceType, ContractReflectionInfo& reflectionInfo, Object serviceImplementation)
   at System.ServiceModel.Description.TypeLoader.LoadContractDescriptionHelper(Type contractType, Type serviceType, Object serviceImplementation)
   at System.ServiceModel.Description.TypeLoader.LoadContractDescription(Type contractType)
   at System.ServiceModel.ChannelFactory<code>1.CreateDescription()
   at System.ServiceModel.ChannelFactory.InitializeEndpoint(Binding binding, EndpointAddress address)
   at System.ServiceModel.ChannelFactory</code>1..ctor(Binding binding, EndpointAddress remoteAddress)
   at System.ServiceModel.ClientBase<code>1..ctor(Binding binding, EndpointAddress remoteAddress)
   at Wcf.WcfClientBase</code>1..ctor(String name, Boolean streaming)
   at Wcf.WcfClientBase`1..ctor(String name)
   at Wcf.AuthenticationClient..ctor()
   at WindowsPhoneApplication2.MainPage.DoLogin()
   ...

Есть идеи?

Ответы [ 4 ]

7 голосов
/ 20 марта 2010

Как отметил Скоттмарлоу, автоматически сгенерированный сервисный референс работает. Я поставил перед собой задачу выяснить, почему, черт возьми, это работает, а ручная версия - нет.

Я нашел виновника, и это ChannelFactory. По какой-то причине new ChannelFactory<T>().CreateChannel() просто выдает исключение. Единственное решение, которое я нашел, - это предоставить собственную реализацию канала. Это включает в себя:

  1. Переопределить ClientBase. (По желанию).
  2. Переопределить ClientBase.CreateChannel. (По желанию). * * 1 010
  3. Подкласс ChannelBase с определенной реализацией вашего интерфейса WCF

Теперь ClientBase уже предоставляет экземпляр фабрики каналов через свойство ChannelFactory. Если вы просто отзовете CreateChannel, вы получите то же исключение. Вам нужно создать экземпляр канала, который вы определили на шаге 3, из CreateChannel.

Это основной каркас того, как все это выглядит вместе.

[DataContractAttribute]
public partial class AuthenticationResponse {
[DataMemberAttribute]
public bool Success {
    get; set;
}

[System.ServiceModel.ServiceContract]
public interface IAuthentication
{
    [System.ServiceModel.OperationContract(AsyncPattern = true)]
    IAsyncResult BeginLogin(string user, string password, AsyncCallback callback, object state);
    AuthenticationResponse EndLogin(IAsyncResult result);
}

public class AuthenticationClient : ClientBase<IAuthentication>, IAuthentication {

    public AuthenticationClient(System.ServiceModel.Channels.Binding b, EndpointAddress ea):base(b,ea)
    {
    }

    public IAsyncResult BeginLogin(string user, string password, AsyncCallback callback, object asyncState)
    {
        return base.Channel.BeginLogin(user, password, callback, asyncState);
    }

    public AuthenticationResponse EndLogin(IAsyncResult result)
    {
        return Channel.EndLogin(result: result);
    }

    protected override IAuthentication CreateChannel()
    {
        return new AuthenticationChannel(this);
    }

    private class AuthenticationChannel : ChannelBase<IAuthentication>, IAuthentication
    {
        public AuthenticationChannel(System.ServiceModel.ClientBase<IAuthentication> client)
        : base(client)
        {
        }

        public System.IAsyncResult BeginLogin(string user, string password, System.AsyncCallback callback, object asyncState)
        {
            object[] _args = new object[2];
            _args[0] = user;
            _args[1] = password;
            System.IAsyncResult _result = base.BeginInvoke("Login", _args, callback, asyncState);
            return _result;
        }

        public AuthenticationResponse EndLogin(System.IAsyncResult result)
        {
            object[] _args = new object[0];
            AuthenticationResponse _result = ((AuthenticationResponse)(base.EndInvoke("Login", _args, result)));
            return _result;
        }
    }
}

TLDR; Если вы хотите использовать свой собственный код WCF в WP7, вам нужно создать собственный класс канала и не полагаться на ChannelFactory.

1 голос
/ 21 сентября 2010

Я собрал сообщение в блоге на эту тему: http://blogs.msdn.com/b/andypennell/archive/2010/09/20/using-wcf-on-windows-phone-7-walk-through.aspx

1 голос
/ 23 марта 2010

Динамическое создание прокси с использованием ChannelFactory.CreateChannel () не поддерживается в Windows Phone.Это задокументировано здесь - http://msdn.microsoft.com/en-us/library/ff426930(VS.96).aspx

Использование службы, использующей механизм «Добавить ссылку на службу» в асинхронном шаблоне, будет правильным способом.

0 голосов
/ 19 марта 2010

У меня не было никаких проблем, но я пошел по пути «добавить ссылку на службу ...», который мне пришлось пройти через «VS2010 Express для Windows Phone», т.к. VS2010 RC еще не поддерживает эту функцию для WP7 разработка. Версия Express поставляется с установкой WP7 Developer.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...