August 2007 Blog Posts
Creare un menu con il controllo Repeater

Da un'altra richiesta su NG :-D, dovrò creare un angolo dedicato, forse in articoli :-), un problema semplice ma non molto per chi è all'inizio: come creare un menu con il controllo Repeater.

Questo esempio parte dal presupposto di avere la struttura delle pagine in uno storage (DB), per questo sarebbe più corretto implementare un SiteMapProvider, come questo.

Per semplificare però eseguo il binding direttamente con un ObjectDataSource.

Il codice html della pagina:

   1:  <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetAll" TypeName="SectionsDataSource" />        
   2:  <asp:Repeater ID="rptMenu" runat="server" OnItemDataBound="rptMenu_ItemDataBound" DataSourceID="ObjectDataSource1">
   3:      <HeaderTemplate><ul></HeaderTemplate>
   4:      <ItemTemplate><li><asp:HyperLink ID="hypSection" runat="server" 
   5:      NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "Url") %>' 
   6:      Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' /></li>
   7:      </ItemTemplate>
   8:      <FooterTemplate></ul></FooterTemplate>
   9:  </asp:Repeater>

Il code-behind:
   1:  protected void rptMenu_ItemDataBound(object sender, RepeaterItemEventArgs e)
   2:  {
   3:      if ((!e.Item.ItemType.Equals(ListItemType.Item)) 
   4:          && (!e.Item.ItemType.Equals(ListItemType.AlternatingItem)))
   5:          return;
   6:   
   7:      Section section = e.Item.DataItem as Section;
   8:      HyperLink hypSection = e.Item.FindControl("hypSection") as HyperLink;
   9:   
  10:      string virtualPath = VirtualPathUtility.ToAppRelative(Request.Path).ToLower();
  11:      
  12:      //Check if the current item url is the current section
  13:      if (section.Url.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase)) {
  14:          hypSection.NavigateUrl = string.Empty;
  15:          hypSection.CssClass = "currentSection";
  16:      }
  17:  }

La classe Section:
   1:  public class Section
   2:  {
   3:      public Section(string name, string url) {
   4:          this.name = name;
   5:          this.url = url;
   6:      }
   7:   
   8:      string name;
   9:      public string Name
  10:      {
  11:          get { return name; }
  12:          set { name = value; }
  13:      }
  14:      
  15:      string url;
  16:      public string Url
  17:      {
  18:          get { return url; }
  19:          set { url = value; }
  20:      }
  21:  }

Ed infine il data source:
   1:  public class SectionsDataSource
   2:  {
   3:      public IList<Section> GetAll() {
   4:          List<Section> sections = new List<Section>();
   5:          sections.Add(new Section("Home", "~/default.aspx"));
   6:          sections.Add(new Section("Contacts", "~/contacts.aspx"));
   7:          sections.Add(new Section("Where", "~/where.aspx"));
   8:          return sections;
   9:      }    
  10:  }
Qui si trova il codice dell'esempio. Matteo Migliore.
Add Comment Filed Under [ ASP.NET ]
Cancellazione multipla con Repeater e ObjectDataSource
Sempre a seguito di una domanda su NG ho scritto un breve esempio che mostra come eseguire la cancellazione multipla di "record" usando un Repeaer e un'ObjectDataSource.

L'esempio è composto da un'entity Student e da una classe che si occupa della persistenza StudentDataSource.

Qui è possibile scaricare l'esempio, di seguito il codice.

Classe Student:
   1:  public class Student
   2:  {
   3:      public Student() { }
   4:   
   5:      public Student(Guid id, string name) {
   6:          this.id = id;
   7:          this.name = name;
   8:      }
   9:   
  10:      Guid id;
  11:      public Guid Id
  12:      {
  13:          get { return id; }
  14:          set { id = value; }
  15:      }
  16:      
  17:      string name;
  18:      public string Name
  19:      {
  20:          get { return name; }
  21:          set { name = value; }
  22:      }
  23:   
  24:      public override bool Equals(object obj) {
  25:          if (obj == null || GetType() != obj.GetType())
  26:              return false;
  27:   
  28:          Student other = obj as Student;
  29:          return (this.Id.Equals(other.Id));
  30:      }
  31:   
  32:      public override int GetHashCode() {
  33:          return Id.GetHashCode();
  34:      }
  35:  }

Classe StudentDataSource:
   1:  public class StudentDataSource
   2:  {
   3:      IList<Student> Students {
   4:          get {
   5:              if (HttpContext.Current.Cache["Students"] == null) {
   6:                  HttpContext.Current.Cache["Students"] = new List<Student>();
   7:                  Fill();
   8:              }
   9:              return HttpContext.Current.Cache["Students"] as IList<Student>; 
  10:          }        
  11:      }
  12:   
  13:      void Fill() {
  14:          Students.Add(new Student(Guid.NewGuid(), "Bill"));
  15:          Students.Add(new Student(Guid.NewGuid(), "Steve"));
  16:          Students.Add(new Student(Guid.NewGuid(), "John"));
  17:          Students.Add(new Student(Guid.NewGuid(), "Martin"));
  18:      }
  19:   
  20:      public IList<Student> GetAll() {
  21:          return Students;
  22:      }
  23:   
  24:      public Student Get(Guid id) {
  25:          Student student = new Student();
  26:          student.Id = id;
  27:          int position = Students.IndexOf(student);
  28:          if (position >= 0)
  29:              return Students[position];
  30:          else
  31:              return null;
  32:      }
  33:   
  34:      public void Delete(Student value) {        
  35:          Students.Remove(value);
  36:      }
  37:  }

Il codice HTML:
   1:  <div>
   2:      <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
   3:          DataObjectTypeName="Student" 
   4:          SelectMethod="GetAll" 
   5:          TypeName="StudentDataSource" />
   6:      <asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
   7:          <ItemTemplate>
   8:              <input type="hidden" id="hidKey" runat="server" value='<%# DataBinder.Eval(Container.DataItem, "Id") %>' />
   9:              <asp:CheckBox ID="chkDelete" runat="server" />
  10:              &nbsp;
  11:              <asp:Label ID="lblName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
  12:              <br />
  13:          </ItemTemplate>
  14:      </asp:Repeater>
  15:      <br />
  16:      <br />
  17:      <asp:Button ID="btnDelete" runat="server" Text="Delete" OnClick="btnDelete_Click" />
  18:  </div>

Il codice che esegue la cancellazione:
   1:  protected void btnDelete_Click(object sender, EventArgs e)
   2:  {
   3:      StudentDataSource studentDataSource = new StudentDataSource();
   4:   
   5:      foreach (RepeaterItem item in Repeater1.Items) {
   6:          CheckBox chkDelete = item.FindControl("chkDelete") as CheckBox;
   7:          if (chkDelete.Checked) {
   8:              HtmlInputHidden hidKey = item.FindControl("hidKey") as HtmlInputHidden;
   9:              Guid key = new Guid(hidKey.Value);
  10:              Student student = studentDataSource.Get(key);
  11:              Response.Write(student.Name + " deleted<br/>");
  12:              studentDataSource.Delete(student);                
  13:          }
  14:      }
  15:      Repeater1.DataBind();
  16:  }

Matteo Migliore.
2 Comments Filed Under [ ASP.NET ]
Host web service in una WindowsForms, senza IIS
Non mi sono sentito di usare il termine "hostare" :-), quindi la prima parte del titolo è in inglese.

Arrivo al dunque: sempre a seguito di una richiesta sul NG di C# ho sviluppato un piccolo esempio che mostra come permettere l'host di web service senza IIS, usando l'ormai famosissimo Cassini, qui si trova una sua evoluzione. Di default Cassini proibisce la connessione fuori da localhost, limite che si può aggirare facilmente avendo a disposizione il codice sorgente. La soluzione si basa comunque su HttpListener, ma spero di riuscire a sviluppare a breve una versione basata su WCF.

Il problema, come ho segnalato nella risposta al post su NG è che WindowsForms e il web service risiedono in AppDomain differenti e quindi bisogna farli comunicare attraverso remoting, cosa abbastanza fastidiosa. In attesa di una soluzione più performante rimando all'esempio: http://www.codeplex.com/WebServiceHoster.

Matteo Migliore.
Postare codice formattato sul blog

Per chi si chiedesse come fare a postare codice C# formattato, provi questo tool CSharpFormat di cui è disponibile il codice sorgente.
Il "magic trick" funziona grazie al supporto offerto da SubText che consiste semplicemente nell'aggiungere ai blog lo stylesheet csharp.css utilizzato dal tool per definire le formattazioni da applicare al codice.

Matteo Migliore.

Copiare dati con ADO.NET e SqlDataAdapter

A seguito di una richiesta dal NG di C# (microsoft.public.it.dotnet.csharp) ho scritto un semplice metodo che consente di fare la copia dei dati provenienti da una query (che può essere complessa quanto si vuole, viene comunque restituita una tabella ;-)) in un'altra tabella sul DB.

Il metodo lavora con ADO.NET in modalità disconnessa che evidentemente può provocare problemi di memory leak e performance. L'ho implementato così per la precisa richiesta del post, ma è molto meglio un SqlDataReader o semplicemente un command "insert DEST select ...." che fa esattamente la stessa cosa.

Qui il metodo:
   1:  public static void CopyData(SqlConnection connection, string selectSource, string selectDestination) {
   2:      SqlCommand commandSource = new SqlCommand(selectSource, connection);
   3:      SqlDataAdapter source = new SqlDataAdapter(commandSource);
   4:      DataSet data = new DataSet();
   5:      source.Fill(data);
   6:      foreach (DataRow row in data.Tables[0].Rows)
   7:          row.SetAdded();
   8:      SqlCommand commandDest = new SqlCommand(selectDestination, connection);
   9:      SqlDataAdapter dest = new SqlDataAdapter(commandDest);
  10:      SqlCommandBuilder builder = new SqlCommandBuilder(dest);
  11:      dest.InsertCommand = builder.GetInsertCommand();
  12:      dest.Update(data);
  13:  }

Qui un esempio di chiamata:
   1:  SqlConnection connection = new SqlConnection(db1);   
   2:  string source = "select * from students inner join numbers on students.id = numbers.studentid";
   3:  string destination = "select * from joinned";
   4:  CopyData(connection, source, destination);

P.S.
E stavolta non è in poesia! :-)

Matteo Migliore.