Exchange, directo al grano con OwaAuth

Durante la semana pasada he estado realizando una pequeña POC para el acceso a OWA (Outloook Web Access) via OwaAuth.dll y creo que, en cierto modo, estoy en la necesidad de que no pase al olvido, (si Juan, por si acaso, jejeje…).

¿Que es eso de OwaAuth.dll?

Se trata de la ISAPI de Exchange para el Logon en Owa. Esta es quizás una gran desconocida para muchos y aunque pasa desapercibida puede ser que en alguna ocasión sea necesario tener que hacer uso de ella para un propósito “un tanto distinto” :-D.

 

Owa necesita un “sessionid” y un “cadata” para poder acceder y obtener información de cada una de sus páginas, pues bien, owaauth.dll es la encargada de obtener estos dos datos en una primera petición http de tipo POST, y, a partir de aquí, haciendo uso de estos en las peticiones sucesivas cada página web de Owa retornará el código HTML sin ningún tipo de preámbulo.

Paso a paso:

  • Invocar a owaauth.dll mediante una petición http (POST) a través de la url: “https://MACHINE_NAME/owa/auth/owaauth.dll” y con una serie de parámetros específicos; destination, flags, forcedownlevel, trusted, username, password, submitCreds, isUtf8. Para la obtención de estos parámetros he utilizado el siguiente método:
public string GetPostData(Uri uri)
{
// Setting data to create a request body.
Dictionary<string, string> props = new Dictionary<string, string>();
props.Add(
"destination",
uri.Scheme
+ Uri.SchemeDelimiter + HttpUtility.UrlEncode(uri.Host + "/exchange/" + _user + "/"));
props.Add(
"flags", "4"); /* 0 */
props.Add(
"forcedownlevel", "0");
props.Add(
"trusted", "4"); /* 0 */
props.Add(
"username", HttpUtility.UrlEncode(String.Format(@"{0}{1}", _domain, _user)));
props.Add(
"password", HttpUtility.UrlEncode(_password));
props.Add(
"submitCreds", "Log+On");
props.Add(
"isUtf8", "1");

ASCIIEncoding encoding
= new ASCIIEncoding();

string postData = String.Empty;
foreach (string key in props.Keys)
{
string pValue = null;
props.TryGetValue(key,
out pValue);
postData
+= string.Format("{0}={1}&", key, pValue);
}
if (props.Keys.Count > 0)
postData
= postData.Substring(0, postData.Length - 1);

return postData;
}
  • Realizada la petición HTTP según el siguiente método, se obtienen los datos buscados; “sessionid” y “cadata”. Estos son recuperados en el objeto “CookieCollection“.
public CookieCollection Authenticate(string url)
{
string postData = GetPostData(new Uri(url));
// Get Response - Cookie
var response = HttpManager.SendRequest(
url, postData,
null,
RequestMethod.POST, _user, _password, _domain,
"application/x-www-form-urlencoded");

if (response.Cookies.Count < 2)
return null;

return response.Cookies;
}
  • Ahora ya se pueden realizar peticiones a cualquiera de las páginas de Owa, eso sí, de tipo GET y teniendo en cuenta que deben ir acompañadas de “sessionid” y “cadata”.
  • La respuesta a cada petición contiene el código HTML de cada una de las páginas solicitadas.

 

El método “SendRequest” es el encargado de realizar:

  1. La primera petición de tipo “POST” y contentType “application/x-www-form-urlencoded” y,
  2. Cada petición de tipo “GET” y contentType “text/xml”.
public static HttpWebResponse SendRequest(string url, string postData,
CookieCollection cookieCol, RequestMethod requestMethod,
string user, string pwd, string domain, string contentType)
{
ServicePointManager.ServerCertificateValidationCallback
= delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
};

Uri uri
= new Uri(url);

var request
= (HttpWebRequest)HttpWebRequest.Create(uri);

request.Method
= requestMethod.ToString();
request.CookieContainer
= new CookieContainer();
request.ContentType
= contentType;
request.AllowAutoRedirect
= false;
// request.ServicePoint.Expect100Continue = false;
request.KeepAlive = true;
request.UserAgent
= "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1;" +
".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)";
request.Accept
= "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,"
+ "application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*";

byte[] body = null;
if (requestMethod == RequestMethod.POST)
{
// BODY
body = Encoding.ASCII.GetBytes(postData);
request.ContentLength
= body.Length;
}

// Cookies
if (cookieCol != null)
{
foreach (Cookie c in cookieCol)
// request.Headers.Add(c.Name, c.Value);
request.CookieContainer.Add(c);
}

// Security
CredentialCache credentialCache = new System.Net.CredentialCache();
credentialCache.Add(uri,
"Basic", /* Basic */
new System.Net.NetworkCredential(user, pwd, domain)
);

request.Credentials
= credentialCache;

if (requestMethod == RequestMethod.POST)
{
// Response
var stream = request.GetRequestStream();
stream.Write(body,
0, body.Length);

stream.Close();
}

return (HttpWebResponse)request.GetResponse();
}

Esto ha sido todo por esta vez, espero como siempre,  haber dado un pasito más.

Saludos @SundayHome
Juanlu

WCF: Un behavior para personalizar cabeceras HTTP.

 

Hace ya unos días ante la necesidad de tener que trabajar con información adicional en las cabeceras (“Headers”) HTTP de WCF me encontré con un lugar más donde aplicar un nuevo “Behabior”.  Como siempre, el tener que crear uno nuevo implica a “vote pronto” un, ¡¡¡bufffff, una vez más a meterse en las entrañas de WCF….!!! si, esta sería quizás la reacción normal, sin embargo no fué así  la mia, jejejeje…

Comento a continuación como crear una nuevo Behavior para este caso y por supuesto con objeto de evitar esta constante idea sobre nuestras mentes informáticas bastante ocupadas, :-D.

  1. Crear un proyecto “WCF Service Application” y añadir el siguiente método a la clase “Service1” que se crea por defecto:
    public string GetHeader() { HttpApplication app = HttpContext.Current.ApplicationInstance; string header = app.Request.Headers["UserQuery"]; if ((header == null) || (header.Length == 0)) throw new Exception(String.Format("Access Denied. Header information not found. {0}", header ?? String.Empty)); return header; }
  2. Crear un proyecto cliente de tipo consola y añadir la clausula using; “using ConsoleApplication1.localhost;” y el siguiente código al método “main”:
    Service1Client client = new Service1Client(); client.Endpoint.Behaviors.Add(new HttpHeaderEndPointBehavior("UsuarioCabecera1")); string header = client.GetHeader(); client.Close();
  3. Para hostear el servicio en IIS añadir el attributo “[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)]” a la clase “Service1”, de esta manera, estará disponible el contexto HTTP (“HttpContext.Current”) para su tratamiento y de donde obtendremos la información de cabecera enviada por el cliente. Como hostear WCF en IIS.
  4. Publicar el servico Web en IIS, ej.: http://loclahost:9001/PruebaWCFCustomHeader/Service1.svc
  5. Finalmente, añadir en el proyecto consola la referencia al servicio web con la url del punto anterior.
  6. Run (F5) y a depurar.

 

Para que todo esto funcione es necesario generar un Behavior específico y para ello  será necesario escribir un par de clases que implementen las interfaces IEndPointBehavior y IClientMessageInspector. Básicamente los métodos que nos interesan son; ApplyClientBeabior y  BeforeSendRequest respectivamente:

 

HttpHeaderEndPointBehavior:

using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using elGuerre.Pocs.HttpHeaderExtension; namespace elGuerre.Pocs.HttpHeaderExtension { public class HttpHeaderEndPointBehavior : IEndpointBehavior { private string _UserName; public HttpHeaderEndPointBehavior(string userAgent) { this._UserName = userAgent; } public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { // Nothing to do } public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) { HttpHeaderMessageInspector inspector = new HttpHeaderMessageInspector(this._UserName); clientRuntime.MessageInspectors.Add(inspector); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // Nothing to do } public void Validate(ServiceEndpoint endpoint) { // Nothing to do } } }

HttpHeaderMessageInspector:

using System; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; namespace elGuerre.Pocs.HttpHeaderExtension { public class HttpHeaderMessageInspector : IClientMessageInspector { private const string USER_NAME_HTTP_HEADER = "UserQuery"; private string _UserName; private string _UserEncodedName; public HttpHeaderMessageInspector(string userName) { _UserName = userName; _UserEncodedName = null; } private string UserEncodedName { get { if (_UserEncodedName == null) { _UserEncodedName = GetBase64Encoded(_UserName); } return _UserEncodedName; } } public void AfterReceiveReply( ref System.ServiceModel.Channels.Message reply, object correlationState) { // Nothing to do. } public object BeforeSendRequest( ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { HttpRequestMessageProperty httpRequestMessage; object httpRequestMessageObject; if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) { httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty; if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_NAME_HTTP_HEADER])) { httpRequestMessage.Headers[USER_NAME_HTTP_HEADER] = this.UserEncodedName; } } else { httpRequestMessage = new HttpRequestMessageProperty(); httpRequestMessage.Headers.Add(USER_NAME_HTTP_HEADER, this.UserEncodedName); request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage); } return null; } private static string GetBase64Encoded(string userName) { byte[] inData; char[] charArr; charArr = userName.ToCharArray(); inData = new byte[charArr.Length]; for (int i = 0; i < charArr.Length; i++) { inData[i] = (byte)charArr[i]; } return Convert.ToBase64String(inData, 0, inData.Length); } } }

Nota:  La información de los “bindings” es la generada por defecto, así que para probar este behavior no será necesario nada más, luego, más fácil aún, :-D.

KIS: “Nada es difícil hasta que se demuestra lo contrario”

Saludos
JuanLu, elGuerre

Algunos BUGs de Web Service Software Factory (WSSF)

Durante el uso de Web Service Softwawre Factory en el proyecto actual nos hemos encontrado algún que otro problemilla curioso y que no quiero pasar por alto.

En esta ocasión se trata de un curioso BUG al trabajar con WSSF. Seguro que ya lo conocéis, por que seguro que os habéis pegado con WSSF más que yo, 😀

Este fenómeno se produce siguiendo los siguientes pasos:

  • Seleccionar un mensaje de tipo XSD en el diseñador

 

  • Al seleccionar la propiedad “Element” en la ventana de propiedades aparece un cuadro de dialogo como el que sigue, el cual permite seleccionar un fichero xsd adjunto en la solución.

 

  • Seguidamente añadimos a la solución un proyecto de tipo “Setup” o “Web Setup”
  • Volver al diseñador y repetir la operación inicial, en lugar de aparecer el cuadro de diálogo para la selección del fichero xsd, zasssss…, aquí aparece el “amiguete”, (“Object reference not set to an instance of an object”)

 

Curioso pero cierto, ¿solución? Sacar de nuestra el proyecto de “Setup”, 😀

También lo he probado con el Framework .NET 3.5 sp1 y  Visual Studio 2008 sp1 y nada, los mismo.  Por el momento tendremos que esperar hasta una nueva versión de WSSF.

Un saludo Halloween”iano”
Juanlu, elGuerre

Creando un "Publisher Policy File"

Ya hace unos días comentaba con un compi el cambio de versión de un Assembly y si, es de lo más fácil, pero claro cuando lo usas diariamente, porque cuando dejas de usarlo, pasa lo  que pasa, jejeje… si, que las cosas se olvidan o causan duda, :-D.  Con objeto de recordar un poco este tema y de aclararlo un poquito más, si es que aún no está claro, que mejor forma de hacerlo que con un ejemplo y con unas cuantas líneas de código.

El ejemplo que voy a mostrar es bastante sencillo y consta de un proyecto tipo DLL con un método y uno tipo consola para ejecutar la prueba:

1) Crear una solución “.net” con dos proyectos; Uno de tipo consola al que llamaré “CheckingAssemblyVersion”  y otro de tipo ClassLibrary (ClassLibrary1) de forma que las clase Class1 del proyecto ClassLibrary1 quede de la siguiente forma:

namespace ClassLibrary1
{
    public class Class1
    {
        public string DoSomething()
        {
            return "Something to do";
        }
    }
}

2) En la clase Program.cs del proyecto (CheckingAssemblyVersion) invocar al método DoSomething() de Class1, para ello será necesario añadir la referencia a dicho proyecto y una vez añadido, asignar el valor “False ” a la propiedad “Copy Local”. 

3) Registrar en el GAC ClassLibrar1.dll

4) Ejecutar la aplicación y todo debería funcionar.

 

Hasta aquí todo correcto, ahora  un cambio de versión a ver que pasa:

1) Añadir un nuevo método DoSomething_2() a  Class1

2) Cambiar la versión en las propiedades del proyecto o en el fichero AssembyInfo.cs

3) Invocar al método DoSomething_2() desde el programa principal “Program.cs”.

4) Si ahora se registra la dll con la nueva versión y se ejecuta la aplicación, “zass..”

Could not load file or assembly 'ClassLibrary1, Version=2.0.0.0, Culture=neutral, PublicKeyToken=946ba4a8f00726e4' or one of its dependencies. The system cannot find the file specified.

5) Previamente hay que tener en cuenta lo siguiente:

Decir a nuestro programa que la versión a ejecutar es la 1.0.0.0 o la 2.0.0.0 y para ello:

a)  Incluir el nodo <runtime> y todo su contenido en el fichero de configuración “.config” del proyecto consola, o

b) Crear un nuevo fichero con todo este contenido, por ejemplo “PolicyVersion.config” en el mismo proyecto de la dll a la que se le pretende hacer referencia.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" appliesTo="v2.0.50727">
      <dependentAssembly>
        <assemblyIdentity name="ClassLibrary1"
                          publicKeyToken="946ba4a8f00726e4"/>
        <!--culture="en-us" />-->
        <!-- Redirecting to version 2.0.0.0 of the assembly. -->
        <bindingRedirect oldVersion="1.0.0.0"
                         newVersion="2.0.0.0"/>
      </dependentAssembly>

    </assemblyBinding>
  </runtime>
</configuration>

Nota: El valor “v2.0.50727” del atributo “appliesTo” se refiere a la versión del Framework de .Net con el que se ha creado la dll “ClassLibrary1”

6)  Si se opta por la opción (a) y a continuación se registra de nuevo la dll en el GAC con la nueva versión todo debería funcionar sin problemas.

7)  Si se opta por la opción (b) y a continuación se registra de nuevo la dll en el GAC entonces, “zaaasssss” otra vez:

Method not found: 'System.String ClassLibrary1.Class1.DoSomethingFromConfig()'.

7.a) Ejecutar la siguiente instrucción desde el “Visual Studio Command Prompt”:

al /link:PolicyVersion.config /out:policy.2.0.0.ClassLibrary1.dll /keyfile:KeyFileTest.snk /v:1.0.0.0

Nota: a la instrucción anterior se le puede añadir el parámetro “/platform:x86” pero este dependerá de la plataforma utilizada durante la compilación.

7.b) Registrar la dll “policy.2.0.0.ClassLibrary1.dll” en el GAC.

7.c) Registrar la dll  ClassLibrary1.dll con la versión 2.0.0.0 en el GAC.

8) Todo listo, ahora la aplicación funcionará sin ningún problema.

9) En ambos casos podría desinstalarse del GAC la versión 1.0.0.0 y el sistema seguiría funcionando siempre y cuando no exista ninguna otra aplicación que la utilice.

 

He aquí dos formas de utilizar una nueva versión de una DLL sin necesidad de tener que reinstalar una y otra vez.  Esto es especialmente útil para aquellas aplicaciones que tienen que ejecutarse con con versiones específicas.

El siguiente mensaje aparecerá al intentar depurar la aplicación debido a que las dlls se encuentra instaladas en el GAC y sin ninguna copia local, OK,y listo, el inconveniente es que no puede ser depurada, :-(.

 

Algunas referencias Referencias:

http://msdn.microsoft.com/en-us/library/7wd6ex19.aspx

http://msdn.microsoft.com/en-us/library/dz32563a(VS.71).aspx

 

Espero haber podido aclarar alguna dudilla, ¡bueno, algo es algo!, como se suele decir, “menos da un piedra”.

Saludos desde la “Escuela Politécnica de Caceres”, concretamente desde la biblioteca ¡Que recuerdos, :-D!
Juanlu

"El Truco del Almendruco" en la depuración con "vsjitdebugger.exe"

En más de una ocasión hemos tenido que depurar de una forma un tanto compleja, o al menos no de la forma habitual, es decir ni depurando directamente desde Visual Studio ni atachando procesos, ¡seguro que si!  Más concretamente, y para el caso que nos ocupa pondré un para de ejemplos muy prácticos.

Durante estas semanas junto con un compañero de trabajo hemos estado realizando Setups “más o menos complejos” y, bueno, así es como uno se da cuenta de los problemas y de como surgen las necesidades, jejeje…

Durante estos Setups las “Customs Acctions”, clases específicas para el tratamiento de acciones durante la instalación/desinstalación, surge la necesidad de depurarlas, y la pregunta es ¿cómo?. Alguno dirá; pues con MessageBox.Show(….), Console.WriteLine(…), etc.. si este mecanismo siempre funciona, pero es bastante más tedioso.

Pues nada de eso, a partir de ahora ¡y claro, para aquel que no conozca este truquito!, será mucho más fácil.

Pasos:

  • Abre el registro de windows: regedit.exe
  • Localiza la ruta “HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsNTCurrentVersionImage File Execution Options
  • Crear una nueva key “MsiExec.exe”
  • Para la Key creada, crear un nuevo valor alfanumérico/String value (REG_SZ) y asígnale el nombre “Debugger”
  • Ahora, asignale el valor “vsjitdebugger.exe”

Debería quedar así:

Esta configuración hará que nuestro depurador; “Visual Studio Just-In-Time Debugger” se inicie cada vez que se ejecute el comando Msiexec.exe

Si ahora sobre nuestro proyecto de Setup de Visual Studio, hacemos “click” con el botón derecho de ratón y pulsamos “Install”,  “Tachaaaaannnnnn” ¡a depurar!, claro, previamente estableceremos los puntos de ruptura (Break Points) adecuados, :-D.

De la misma forma, si queremos instalar un Servicio Windows en la consola de servicios y utilizamos “InstallUtil” repetiremos los pasos sustituyendo MsiExec.exe por “InstallUtil.exe“.

Una vez que hayamos finalizado la depuración, Modificar la key “MsiExed.exe” para que no se ejecute siempre, de manera que quede algo como; ” _***_MsiExec.exe”. De forma similar para InstallUtil.exe, por ejemplo _***_InstallUtil.exe.

Tambíen podríamos sustituir  nuestro depurador “vsjitdebugger.exe” por cualquier otro disponible en nuestra máquina.

 

Por supuesto, gracias a Javier,(si, el de las cervecitas, jejeje…) por tal descubrimiento.

 

¡Buscando la fácil senda en el desarrollo!
Juanlu

WCF & MSMQ

Hace unas semanas hablaba con un compañero acerca de como implementar una aplicación con acceso  a MSMQ y tras una larga discusión, en un intento de conseguir pensar en el mejor camino de lograrlo, pensé en hacerlo con WCF, y,  ¿Que mejor forma de probarlo que haciendo un ejemplo o proyecto? , pues, e aquí el motivo de este post.

Os dejo el conjunto de pasos seguidos:

Pasos:

  • Generar una estructura en Visual Studio similar a la siguiente:

  • Añadir el siguiente código a la interfaz, fichero IService1.cs
[ServiceContract] public interface IService1 { [OperationContract(IsOneWay = true, Action = "*")] void Send(MsmqMessage<Persona> msg); }
  • Añadir el siguiente código al servicio Service1.cs
public class Service1 : IService1 { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void Send(MsmqMessage<Persona> message) { Persona msg = (Persona)message.Body; Console.WriteLine("Persona: [{0} {1}; ({2})] ", msg.Nombre, msg.Apellidos, msg.OtraCosaMariposa); } }
  •   En el fichero Program.cs del proyecto WcfMsmqHost añadir lo siguiente:
static void Main(string[] args) { string msmqPath = ConfigurationManager.AppSettings["baseAddress"]; if (!MessageQueue.Exists(msmqPath)) MessageQueue.Create(msmqPath); Uri baseAddress = new Uri(String.Format("msmq.formatname:DIRECT=OS:{0}", msmqPath)); using (ServiceHost serviceHost = new ServiceHost(typeof(Service1), baseAddress)) { serviceHost.Authorization.PrincipalPermissionMode = System.ServiceModel.Description.PrincipalPermissionMode.None; serviceHost.Open(); Console.WriteLine("Servicio listo."); Console.WriteLine("Pulsa <INTRO> para finalizar."); Console.ReadLine(); serviceHost.Close(); } }
  • El fichero de configuración del servidor deberá contener esto:
<appSettings> <add key="baseAddress" value=".private$MyTest33"/> </appSettings> <system.serviceModel> <bindings> <msmqIntegrationBinding> <binding name="NewBinding0" exactlyOnce="false" useSourceJournal="false" useMsmqTracing="true"> <security mode="None"> <transport msmqAuthenticationMode="None" /> </security> </binding> </msmqIntegrationBinding> </bindings> <services> <service name="WcfMsmqHost.Service1"> <endpoint address="" binding="msmqIntegrationBinding" bindingConfiguration="NewBinding0" contract="WcfMsmqIntegration.IService1" /> </service> </services> </system.serviceModel>
  • Finalmente la función Main, en Program.cs del proyecto de test debe contener algo similar a esto:
string msmqPath = ConfigurationManager.AppSettings["baseAddress"]; MsmqIntegrationBinding binding = new MsmqIntegrationBinding(); EndpointAddress address = new EndpointAddress( String.Format(@"msmq.formatname:DIRECT=OS:{0}", msmqPath)); ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>(binding, address); // None, porque MQM no esta en Active Directory binding.Security.Mode = MsmqIntegrationSecurityMode.None; IService1 channel = channelFactory.CreateChannel(); Persona msg = new Persona(); msg.Nombre = "Juan Luis"; msg.Apellidos = "Guerrero"; msg.OtraCosaMariposa = "elGuerre"; MsmqMessage<Persona> message = new MsmqMessage<Persona>(msg); using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { channel.Send(message); scope.Complete(); }
  • Y el fichero de configuración del cliente deberá contener:
<configuration> <appSettings> <add key="baseAddress" value=".private$MyTest33"/> </appSettings> </configuration>

No olvidar incluir los espacios de nombre:

using System.ServiceModel; using System.Messaging; using System.ServiceModel.MsmqIntegration;

Y ni que decir tiene que la clase Persona (Persona.cs) contiene  tres propiedades públicas sin más; Nombre, Apellidos y OtraCosaMariposa, incluso, no será necesario establecer el atributo DataContract.

 

Por otro lado y durante la configuración del ejemplo nos podemos encontrar con alguno errores y que detallo a continuación:

Caso 1:

serviceHost.Open: Binding validation failed because the binding’s ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding.

Este problema es debido a la propiedad “exactlyOnce” tiene el valor “true” y la cola no es transaccional para solucionarlo:

  1. Si la cola es transaccional entonces asignarle el valor “true” a esta propiedad.
  2. si la cola NO es transaccional asignarle el valor “false”.

Caso 2:

Binding validation failed because the binding’s MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened.

En este caso la solución es evitar que el valor del modo de seguridad del binding sea “WindowsDomain”, puesto que la integración de MSMQ con Active Directory no ha sido instalada. Bastará, y si estamos en un dominio, con instalar esta integración desde los componentes de windows. Luego para nuestro caso, tanto cliente como servidor/host deberán establecer algunos valores:

Cliente en modo programático:

binding.Security.Mode = MsmqIntegrationSecurityMode.None;

Servidor mediante fichero de configuración:

<endpoint address="" binding="msmqIntegrationBinding" bindingConfiguration="NewBinding0" contract="WcfMsmqIntegration.IService1" />

El inconveniente de utilizar transacciones, si es que es un inconveniente, es el MSTC (Microsoft Transsaction Coordinator), que nos asegura la transaccionalidad de la operación de forma automática, pero que sin embargo, y dependiendo del entorno, es decir de la distribución entre servidores, podría resultar muy pesado e incluso no deseable. En caso de no hacer uso de la transaccionalidad, bastará con invocar de nuevo a nuestro “Sender” (envío de mensajes a MSMQ con WCF) desde el propio servidor/host y establecer un tratamiento adecuado y con esto estaría solucionado.

Finalmente y por ahora, he decir, que la ventaja de utilizar WCF para el tratamiento de colas MSMQ radica principalmente en la posibilidad de cambiar los bindings sin problema alguno, la trazabilidad totalmente automática, la seguridad de que todo va a funcionar a la primera, 😀

“Lo malo” puede ser que nos encontremos con que ya tenemos un sistema de colas MSMQ en un entorno de producción y que no podamos crear ningún servicio WCF, en tal caso, siempre podremos optar por crear un cliente WCF (ClientBase<T>), incluso también optar por el caso contrario, un servidor WCF y un cliente no WCF (System.Messaging).

http://www.cogin.com/mq/download.php

Un nuevo camino para el tratamiento de colas MSMQ.

saludos
Juanlu

Silverlight Beta2 Release

 

Hola a tod@s y, bueno, decios, que ya tenemos aquí la Beta 2 de Silverlight, si, este pasado viernes ha sido lanzada y la verdad, incluye numerosas y nuevas características que nos detalla nuestro compañero Scott:

UI and Control Improvements

  • More Built-in Controls
  • Control Template Editing Support
  • Visual State Manager (VSM) Support
  • TextBoxes and DataGrids scrollbars
  • Input Support
  • UI Automation and Accessibility
  • Animation and Graphic System
  • DeepZoom
  • WPF Compatibility

Media Improvements

  • Adaptive Streaming
  • Conent Protection
  • Sever Side Playlists

Networking Improvements

  • Cross Domain Sockets
  • Background Thread Networking
  • Duplex Communication (Server Push)
  • Web Services
  • Rest and ADO.NET Data Services
  • JSON

Data Improvements

  • DataGrid
  • DataBinding
  • Isolated Storage

Por último decir que “Silverlight Beta 2” es compatible con  Silverlight 1.0 pero sin embargo no lo es con aplicaciones de “Silverlight 2 Beta 1”.

¡¡¡Al fin!!!, y después de una larga y difícil temporada, por aquí estoy de nuevo,bueno, eso espero. “1000 perdones”.

Saludos
Juanlu

AutoCode. Programar más rápido, aún sigue siendo posible.

 

Bueno, pues ya se terminaron las vacaciones, al menos para algunos, para otros seguro que no, alguno aún sigue pasándolo bien, jejeje…
Vengo de vacaciones, y por fin encuentro una herramienta que me permite desarrollar aún más rápido, si, ya hacía tiempo que la estaba esperando, jejeje… me río porque un compañero (“A…..”) me comentó que la estaba desarrollando y por fin ha dado su fruto, la verdad, es maravillosa, ¡vamos, que ya la tengo instalada!, os recomiendo que le echéis un vistazo, no tiene desperdicio. Además, podéis encontrar un vídeo en su página, http://www.devprojects.net/, por el momento os adelanto algunas características:

  • Fast command invocation just pressing Ctrl+Enter
  • ASPX like syntax to generate code
  • Full access to Visual Studio automation objects
  • FileCodeModel access, SmartFormat, code surrogate, cursor positioning and more
  • Use Visual Studio editor to create custom commands
  • Integrated Catalog ToolWindow to explore commands
  • Multiple useful commands included
  • Visual Studio 2005 and 2008 versions
  • Supports Windows Vista

A continuación muestro la integración del catálogo en Visual Studio 2008.

He de decir que funciona, y además nos permite “customizarlo” a nuestro antonjo inmediatamente, accediendo a través del catálogo y guardando los cambios oportunos.

Descargarlo directamente para:

Saludos desde 3Cantos y de parte de mi compi Anonimo, 😀
Juanlu

WCF – Creación dinámica de una clase proxy a partir de un WSDL

 

No sé si a alguien le puede venir bien este ejemplo, pero seguro que sí, en cualquier caso, aquí está, 😛

Se trata de un proyecto C#  que permite crear “proxys” clientes a partir de un WSDL de forma dinámica.

Es un ejemplo de una calculadora muy sencillo pero que sin embargo y lo más importante es que puede modificarse y adaptase a nuestra necesidad con bastante facilidad.

 

He aquí la referencia para descargarlo y pegarse un poco más con el: http://wcf.netfx3.com/files/folders/development_tools/entry6148.aspx

 

Una vez Más espero haber ayudado un poquito.
Juanlu

Hosteando en IIS un WCF con Http Basic Authentication y Compatibilidad ASP.NET (HttpContext; Session, cookies, cache, etc)

Una vez más, sigo por aquí con VS2008 y con WCF, en esta ocasión trataré de comentar, como hostear en IIS un WCF basado en un binding “Http Basic Authentication” y conseguir hacer uso de nuestra conocida clase HttpContext. Son unos cuantos pasos muy fáciles y básicos, jejeje.. ¡claro, ahora que los conozco!

Los pasos a seguir para conseguir el objetivo de este post son:

  1. Crear un proyecto Web de tipo “WCF Service Application”.

  • La plantilla nos creará un proyecto con un Interfaz (Servicio) y una clase de datos (contrato), además un servicio, lo que es nuestro Web Service que ha de implementar obligatoriamente la Interfaz.
  • Seguidamente modificamos el Web.config del servicio manualmente o con nuestro maravilloso editor (Windows Communication Foundation (WCF) – BUG 1) estableciendo principalmente la configuración tal y como muestro aquí un par de pantallazos:

 

  • A continuación tenemos dos opciones para trabajar con la compatibilidad  ASP NET:
    • Anadir el attributo “AspNetCompatibilityRequirements([AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)]) al servicio, Service1 tal y como se muestra a continuación. Aprovechamos para establecer el Namespace, :-D, ¡ya sabéis “best practices”! (tempuri.org con WCF).
1 [ServiceBehavior(Namespace="http://elGuerre.loc/Service1/")] 2 [AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)] 3 public class Service1 : IService1 4 { 5 public string GetData(int value) 6 { 7 return string.Format("You entered: {0}", value); 8 } 9 10 public CompositeType GetDataUsingDataContract(CompositeType composite) 11 { 12 if (composite.BoolValue) 13 { 14 composite.StringValue += "Suffix"; 15 } 16 return composite; 17 }
    • y, añadir la siguiente línea en el web.config.
1 <system.serviceModel> 2 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 3 ...

O,

    • Añadir el atributo “AspNetCompatibilityRequirements” ([AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Required)] y,
    • no incluir la configuración en el web.config.
  • Publicar el servicio (“Publish”).
  • Modificar las propiedades del sitio Web en IIS:
    1. Quitar autenticación anónima
    2. Activar autenticación básica (dejar activa solamente esta casilla).

Ahora tenemos la compatibilidad con ASP.NET  pero con la potencia de WCF, nuestra clase HttpContext (session, cookies, cache, etc) está lista para ser usada.

Si estáis interesados en esto, aquí os dejo unas cuantas referencias:

 

Saludos @3Cantos
Juanlu