21/11/2009

DataTable e WebService: coesistenza possibile?

Di recente ho "ingenuamente" scritto un webservice con un metodo che accetti, come parametro passato in ingresso, un oggetto con diverse proprietà. Fra queste ce n'era una di tipo DataTable.
Al primo test: eccezzione legata alla serializzazione di oggetti di tipo DataTable... immediata ricerca su internet e scopro che non si devono usare le DataTable nei webservice, al posto loro si devono usare i DataSet.
Per il caso in questione passare ai DataSet era un'opzione apocalittica allora mi sono messo ad analizzare il problema e, procedendo per tentativi, sono giunto alle seguenti due conclusioni:

  • E' possibile passare un oggetto di tipo DataTable al metodo di un webservice purchè sia specificato il nome della tabella (proprietà TableName). Questo permette la corretta deserializzazione dell'oggetto
  • Se l'oggetto di tipo DataTable è vuoto (proprietà Rows.Count==0) è necessario che sia specificata almeno una colonna (proprietà Columns usare il metodo Add). Se questo non avviene il framework non riesce a gestire la deserializzazione dell'oggetto che viene passato al webservice e che contiene la DataTable; inoltre non vengono nemmeno sollvete eccezzioni...al webservice arriva solo un oggetto vuoto!

 

04/10/2009

Eseguire codice utilizanndo l'identità di un altro utente

Mi è capitato di dover accedere a una cartella di rete utilizzando un'identità (username e password) diverse da quella che eseguiva il programma. Cercando su internet ho scoperto che esiste un semplice modo per assumere programmaticamente l'identità di un altro utente. Utente che, nel mio caso, possedeva permessi adatti ad accede alla risorsa a me necessaria.
Ho riassunto i comandi necessari nella seguente classe e di seguito un esempio di utilizzo della classe.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Security.Principal;
using System.Runtime.InteropServices;
public class ImpersonateUser : IDisposable
{
 WindowsImpersonationContext impersonationContext;
 public const int LOGON32_LOGON_INTERACTIVE = 2;
 public const int LOGON32_PROVIDER_DEFAULT = 0;

 [DllImport("advapi32.dll")]
 public static extern int LogonUserA(String lpszUserName,
   String lpszDomain,
   String lpszPassword,
   int dwLogonType,
   int dwLogonProvider,
   ref IntPtr phToken);

 [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 public static extern int DuplicateToken(IntPtr hToken,
   int impersonationLevel,
   ref IntPtr hNewToken);


 [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 public static extern bool RevertToSelf();

 [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
 public static extern bool CloseHandle(IntPtr handle);

 public ImpersonateUser(string username, string password, string domain)
 {
   impersonationContext = impersonateValidUser(username, domain, password);
   if (impersonationContext == null)
     throw new System.Security.Authentication.AuthenticationException();
 }

 private WindowsImpersonationContext impersonateValidUser(String userName,
    String domain, String password)
 {
   WindowsIdentity tempWindowsIdentity;
   IntPtr token = IntPtr.Zero;
   IntPtr tokenDuplicate = IntPtr.Zero;

   if (RevertToSelf())
   {
     if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
       LOGON32_PROVIDER_DEFAULT, ref token) != 0)
     {
       if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
       {
         tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
         impersonationContext = tempWindowsIdentity.Impersonate();
         if (impersonationContext != null)
         {
           CloseHandle(token);
           CloseHandle(tokenDuplicate);
           return impersonationContext;
         }
       }
     }
   }
   if (token != IntPtr.Zero)
     CloseHandle(token);
   if (tokenDuplicate != IntPtr.Zero)
     CloseHandle(tokenDuplicate);
   return null;
 }


 #region IDisposable Members

 public void Dispose()
 {
   impersonationContext.Undo();
   impersonationContext.Dispose();
 }

 #endregion
}

Esempio di utilizzo della classe:
using (new ImpersonateUser("pippo","pluto","casaPippo"))
{
 //Codice da eseguire come utente pippo
 File.Copy("\serverPippocartellaPrivataPippofile.txt", "C:dest.txt");
}

13/09/2009

Generics in C#, le variabili di tipo

L'uso opportuno del polimorfismo solitamente riesce a sostituire l'uso dei generics e allo stesso tempo a mantenere il codice più pulito, tuttavia ci sono situazioni in cui non si può fare a meno delle variabili di tipo.
In questo post riassumerò brevemente come si utilizzano i Generics con una classe che calcola le possibili combinazioni di oggetti senza ripetizione. Per esempio dato l'insieme di oggetti A, B, C, D, E le combinazioni senza ripetizione di cardinalità 2 saranno (A, B) (A, C) (A, D) ... (C, E) ...
Per prima cosa definiamo la classe Insieme:

public class Insieme<TipoContenuto> : List<TipoContenuto>
where TipoContenuto : IComparable
{
    public Insieme()
    {
    }

    public override bool Equals(object obj)
    {
        if (obj is Insieme<TipoContenuto>)
        {
            Insieme<TipoContenuto> combinazione = (Insieme<TipoContenuto>)obj;
            //Due insiemi sono uguali se contengono elementi uguali
            bool risultato = combinazione.Count == this.Count;
            foreach (TipoContenuto temp in combinazione)
            {
                risultato = risultato & this.Contains(temp);
            }
            return risultato;
        }
        return base.Equals(obj);
    }

    //Restituisce una copia dell'insieme ordinata
    public List<TipoContenuto> getOrderedList()
    {
        List<TipoContenuto> copia = new List<TipoContenuto>();
        copia.AddRange(this);
        copia.Sort();
        return copia;
    }
}
In questa classe vediamo l'uso della variabile di tipo "TipoContenuto" e racchiusa fra parentesi angolari.
La clausola where permette di restringere il numero di tipi che possono "entrare" nella variabile TipoContenuto. In questo caso ci assicuriamo che TipoContenuto sia un oggetto confrontabile.
Insieme eredita da List, redefinisce il metodo Equals perchè due insiemi sono uuali se contengono tutti gli stessi elementi ed espone un metodo per ottenere una copia ordinata degli oggetti contenuti. Si noti come TipoContenuto venga utilizzato nella classe come se fosse un vero e proprio tipo.

Definita la classe insieme dobbiamo costruire la classe Combinatore basata su un algoritmo ricorsivo:
public class Combinatore
{
    public Combinatore()
    {
    }

    public List<Insieme<TipoContenuto>> calcolaCombinazioni<TipoContenuto>(Insieme<TipoContenuto> elementiDaCombinare, int cardinalità )
        where TipoContenuto : IComparable
    {
        //Controllo variabili input
        if (elementiDaCombinare.Count < cardinalità )
            throw new ArgumentException("Gli elementi da combinare devono essere maggiori della cardinalità .");

        if (cardinalità  <= 1)
            throw new ArgumentException("Cardinalità  non valida");

        if (elementiDaCombinare.Count <= 2)
            throw new ArgumentException("I numeri da combinare devono essere almeno 2");

        //Effettua il calcolo delle combinazioni
        List<Insieme<TipoContenuto>> risultato = new List<Insieme<TipoContenuto>>();
        risultato = calcoloRicorsivoCombinazioni(elementiDaCombinare, cardinalità );
        return risultato;
    }

    private List<Insieme<TipoContenuto>> calcoloRicorsivoCombinazioni<TipoContenuto>(List<TipoContenuto> elementiDaCombinare, int cardinalità )
        where TipoContenuto : IComparable
    {
        List<Insieme<TipoContenuto>> risultato = new List<Insieme<TipoContenuto>>();
        if (cardinalità  > 2)
        {
            // creo tanti sottoinsiemi di oggetti da combinare e
            // per ogni sottoinsieme calcolo ricorsivamente le combinazioni
            for (int i = 0; i < elementiDaCombinare.Count - cardinalità  +1; i++)
            {
                List<TipoContenuto> nuovaLista = new List<TipoContenuto>();
                nuovaLista.AddRange(elementiDaCombinare.GetRange(i+1,elementiDaCombinare.Count-i-1));
                // Per ogni sottocombinazione calcolata aggiungo l'elemento che era
                // stato tenuto in disparte
                foreach (Insieme<TipoContenuto> sottoCombinazione in calcoloRicorsivoCombinazioni(nuovaLista, cardinalità  - 1))
                {
                    sottoCombinazione.Add(elementiDaCombinare[i]);
                    risultato.Add(sottoCombinazione);
                }
            }
        }
        else
        {
            //condizione di chiusura della ricorsione --> cardinalità  2
            risultato.AddRange(calcolaCombinazioniCardinalità Due(elementiDaCombinare));
        }
        return risultato;
    }


    private List<Insieme<TipoContenuto>> calcolaCombinazioniCardinalitàDue<TipoContenuto>(List<TipoContenuto> elementiDaCombinare)
        where TipoContenuto : IComparable
    {
        List<Insieme<TipoContenuto>> risultato = new List<Insieme<TipoContenuto>>();

        //Controlla variabili in input
        if (elementiDaCombinare.Count < 2)
            return risultato;
       
        //Doppio ciclo che calcola le combinazioni a coppie SENZA ripetizioni
        for(int i = 0; i < (elementiDaCombinare.Count-1); i++)
            for (int j = i + 1; j < elementiDaCombinare.Count; j++)
            {
                Insieme<TipoContenuto> combinazione = new Insieme<TipoContenuto>();
                combinazione.Add(elementiDaCombinare[i]);
                combinazione.Add(elementiDaCombinare[j]);
                risultato.Add(combinazione);
            }

        return risultato;
    }
}
In questa classe, notiamo l'uso dei generics nei metodi. Nel metodo calcolaCombinazioniCardinalitàDue si utilizza una variabile di tipo nell'argomento passato e nel valor di output. Per poterlo fare è necessario definire che il metodo utilizzerà una variavile di tipo. Per farlo si deve far seguire al nome della funzione la variabile di tipo fra parentesi angolari. Inoltre, così come nella classe, è possibile utilizzare la clausola where per definire le caratteristiche del tipo generico.
1 2 3 4 5 6 7 8 Prossimo