Pregunta

¿Alguien ha sido capaz de comunicarse utilizando WCF en Windows Phone 7 Series emulador?

He estado intentando durante los últimos dos días y es simplemente pasando por mí. Puedo conseguir un control Silverlight normal a trabajar tanto en Silverlight 3 y Silverlight 4, pero no la versión del teléfono. Aquí hay dos versiones que he probado:

Versión 1 - Uso asíncrono Patrón

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);

Esta versión se rompe en esta línea:

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

Lanzar System.NotSupportedException. La excepción no es muy descriptivo y la pila de llamadas no es igualmente muy útiles:


   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()
   ....

Versión 2 - bloqueo de llamadas WCF

Esta es la versión que no utiliza el patrón asincrónico.

[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");

Esta versión se estrella en el constructor System.ServiceModel.ClientBase<TChannel>. La pila de llamadas es un poco diferente:


   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.ChannelFactory1.CreateDescription()
   at System.ServiceModel.ChannelFactory.InitializeEndpoint(Binding binding, EndpointAddress address)
   at System.ServiceModel.ChannelFactory1..ctor(Binding binding, EndpointAddress remoteAddress)
   at System.ServiceModel.ClientBase1..ctor(Binding binding, EndpointAddress remoteAddress)
   at Wcf.WcfClientBase1..ctor(String name, Boolean streaming)
   at Wcf.WcfClientBase`1..ctor(String name)
   at Wcf.AuthenticationClient..ctor()
   at WindowsPhoneApplication2.MainPage.DoLogin()
   ...

¿Alguna idea?

¿Fue útil?

Solución

Como scottmarlowe señalado, la refrence servicio generado automagicly simplemente funciona. He puesto sobre la misión de averiguar por qué diablos funciona y la versión manual no lo hace.

He encontrado el culpable y es ChannelFactory. Por alguna razón new ChannelFactory<T>().CreateChannel() simplemente emite una excepción. La única solución que encontré es proporcionar su propia implementación del canal. Esto implica:

  1. Reemplaza ClientBase. (Opcional).
  2. Anular ClientBase.CreateChannel. (Opcional).
  3. ChannelBase Subclase con una aplicación específica de su interfaz WCF

Ahora, ClientBase ya prevé una instancia de la fábrica de canales a través de la propiedad ChannelFactory. Si sólo tiene que llamar CreateChannel fuera que se podrían obtener la misma excepción. Es necesario crear una instancia de un canal que se define en el paso 3 desde dentro CreateChannel.

Esta es la estructura metálica básica de lo que aparece en su conjunto.

[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;. Si desea utilizar su propio código de WCF en WP7 que necesita para crear su propia clase de canal y no depender de ChannelFactory

Otros consejos

creación de proxy dinámico utilizando ChannelFactory.CreateChannel () no es compatible con Windows Phone. Esto está documentado aquí - http://msdn.microsoft. com / es-es / library / ff426930 (VS.96) .aspx

El consumo de un servicio mediante el mecanismo de 'Agregar referencia de servicio' en un patrón asincrónico sería la forma correcta de hacerlo.

No he tenido ningún problema, pero fui la "referencia de servicio añadir ..." ruta de la que tenía que hacer a través de "VS2010 Express para Windows Phone" b / c RC VS2010 todavía no soporta esa característica para desarrollo de WP7. La versión expreso viene con el WP7 del desarrollador instalar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top