Really really simple. Feel free to write your own custom authentication method to fit your project context.

This is download.aspx:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="download.aspx.cs" Inherits="download" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Download Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
    </div>
    </form>
</body>
</html>
and this is the code behind that file:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class download : System.Web.UI.Page 
{

    /// <summary>
    /// Show error to user and close response object.
    /// </summary>
    /// <param name="error"></param>
    private void WriteError(string error) {
        Response.Write(error);
        Response.End();
    }

    /// <summary>
    /// Check authentication ticket
    /// </summary>
    /// <returns></returns>
    private bool Authenticated() {
        //whatever is a session ticket, membership provider base, container in a coded URL querystring parameters, etc..
        return true;    
    }

    private string GetRepositoryFolder() {
        System.Configuration.AppSettingsReader r = new AppSettingsReader();
        return r.GetValue("RepositoryFolder", typeof(string)).ToString();                
    }


    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Authenticated()){
            WriteError("You are not allowed to download this file");
            return;
        }
        else if (Request.QueryString["id"] == null)
        {
            WriteError("Missing parameter : id");
            return;
        }       
        
        string filePath = System.IO.Path.Combine(GetRepositoryFolder(),Request.QueryString["id"]);
        System.IO.FileInfo file = new System.IO.FileInfo(filePath);
        if (!file.Exists)
        {
            WriteError("File doesn't exists");
            return;
        }
        else {
            Response.Clear();
            Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
            Response.AddHeader("Content-Length", file.Length.ToString());
            Response.ContentType = "application/octet-stream";
            Response.WriteFile(file.FullName);
            Response.End();
        }        
    }
}

If successfully authenticated, you will be able to directly download the file:


If not, you will get an error message:

You are not allowed to download this file

kick it on DotNetKicks.com AddThis Social Bookmark Button

Querying Youtube API using C#

Posted in .NET 2.0 | Google API | YouTube at Thursday, December 20, 2007 10:40 PM UTC

This example shows how to use Google Data API to query and obtain videos from YouTube. There is also a downloadable zip file with the example in ASP.NET / C#.

  1. First of all, you need to download the Google Data APIs Client Libraries.
  2. Second, use the following namespaces:

    using
    Google.GData.Client;
    using
    Google.GData.Extensions;

  3. Use the service by querying by tag, or by search (you can also query related items, etc..):

        //by tag
        //feel free to change number of items, by there is a limit of 50, I believe. 
        //If you want to retreive more, you have to do a loop (retrieve 1-50, then 51 to 100, etc)
        protected void btoGo_Click(object sender, EventArgs e)
    {
    string url = "http://gdata.youtube.com/feeds/videos/-/" + this.txtTag.Text;
    AtomFeed myFeed = GetFeed(url, 1, 20);
    DisplayFeed(myFeed);
    }

    //by search //feel free to change number of items, by there is a limit of 50, I believe. //If you want to retreive more, you have to do a loop (retrieve 1-50, then 51 to 100, etc) protected void btoSearch_Click(object sender, EventArgs e)
    {
    string url = "http://gdata.youtube.com/feeds/videos?q=" + this.txtSearch.Text;
    AtomFeed myFeed = GetFeed(url, 1, 15);
    DisplayFeed(myFeed);
    }
  4. Use the following methods, or similars to get and display the Feed:

        /// <summary>
        /// Create and returns and Google.GData.Client.AtomFee from url with the specific start and number of items
        /// </summary>
        /// <param name="url"></param>
        /// <param name="start"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        private static AtomFeed GetFeed(string url, int start, int number)
    {
    System.Diagnostics.Trace.Write("Conectando youtube at " + url);
    FeedQuery query = new FeedQuery("");
    Service service = new Service("youtube", "exampleCo");
    query.Uri = new Uri(url);
    query.StartIndex = start;
    query.NumberToRetrieve = number;

    AtomFeed myFeed = service.Query(query);
    return myFeed;
    }

    /// <summary> /// Renders feed in example aspx page /// </summary> /// <param name="myFeed"></param> private void DisplayFeed(AtomFeed myFeed)
    {
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    foreach (AtomEntry entry in myFeed.Entries)
    {
    #region render each
    sb.Append("<br /><b>Title:</b> ");
    sb.Append(entry.Title.Text);
    sb.Append("<br /><b>Categories:</b> ");
    foreach (AtomCategory cat in entry.Categories)
    {
    sb.Append(cat.Term);
    sb.Append(",");
    }
    sb.Append(RenderVideoEmbedded(getIDSimple(entry.Id.AbsoluteUri)));
    sb.Append("<br /><b>Published on:</b> ");
    sb.Append(entry.Published);
    #endregion } this.lblResults.Text = sb.ToString();
    }
    private string RenderVideoEmbedded(string idSimple)
    {
    return string.Format("<div id=\"video{0}\"><object width=\"425\" height=\"355\"><param name=\"movie\" value=\"http://www.youtube.com/v/{0}&rel=1\"></param><param name=\"wmode\" value=\"transparent\"></param><embed src=\"http://www.youtube.com/v/{0}&rel=1\" type=\"application/x-shockwave-flash\" wmode=\"transparent\" width=\"425\" height=\"355\"></embed></object></div>", idSimple);
    }

Related resources:


Download example web site project in C#:
youtubeAPIExample.zip (65,91 KB)

kick it on DotNetKicks.com AddThis Social Bookmark Button

Inheriting from System.Exception or System.ApplicationException?

Posted in .NET quirks at Monday, December 17, 2007 6:02 PM UTC
Yes, there is a bit of confusion about this (also read this post).

I understand that the right way to create your application specific exceptions classes is to inherit from Exception class:
public class MyCustomEx : System.Exception{
   //class implementation
}
And if in any case you have to throw a general application exception that has not specific type, throw a System.ApplicationException:
throw new System.ApplicationException("Error retrieving data. The web service may be down");

kick it on DotNetKicks.com AddThis Social Bookmark Button

Using a RAM drive to compile Visual Studio .NET projects

Posted in .NET 2.0 | Performance | Productivity | Visual Studio 2005 | RAM disk at Saturday, December 15, 2007 10:18 AM UTC
(updated 22 dec 07)

After reading some blog posts like this I decide to test on my computers how important is hard disk speed in order to increase your productivity when compiling Visual Studio solutions with more than a few project.

I also wanted to see the gain if I placed my solution (more than 25 projects) in a RAM drive disk instead of the hard disk (be sure you backup frecuently if you do that, or get one of this things to avoid data lost).

In order to create a RAM drive, I download the trial version of Virtual Hard Drive Pro
 from FarStone.

There are the results:

1. Environment hardware information

My desktop:
  1. CPU: AMD Sempron, 1800 MHz
  2. RAM: 2 x (1 Gb Kingston)
  3. Hard disks (2):
    1. System: ATA-100 40 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 2Mb. Average read speed: 40 Mb/second (*).
    2. Data and code: SATA 200 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 8 Mb. Average read speed: 50 Mb/second (*).
  4. RAM drive speed (read): 900 Mb/sec
My laptop (Toshiba Satellite Pro M70):
  1. CPU: Intel Pentium Celeron 1.73 Ghz
  2. RAM: 2 x (512 Mb Toshiba)
  3. Hard disk (1) : Toshiba 5400 rpm. Average read speed: 30 Mb/second (*)
  4. RAM drive speed (read): 1500 Mb/sec
* (Hard disk speed tests have been done with HD_Speed from www.steelbytes.com)

2. The Microsoft Visual Studio solution

A Microsoft Visual Studio .NET 2005 solution with 26 projects (Windows Forms and Windows library projects mainly), in C#.

3. Results

Compilation:

Desktop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 72 seconds*
b) Compilation on a 500 MB RAM Drive : 71 seconds*

Laptop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 65 seconds*
b) Compilation on a 400 MB RAM Drive : 32 seconds*

*(average of three compilations)

4. Conclusions:

Seems like my desktop doesn't have much processor power, and the bottleneck is mainly on the CPU, since there is not much different results on readig from a 50 Mb/seconds hard disk or from a 900 MB/second one (the RAM Drive)

My laptop, instead has a slower disk drive but more processor power, so there is a lot of difference between compiling from hard disk and RAM drive. (it took half the time on the RAM disk - also RAM disk read speed is higher than in the desktop)

I still will do more tests with some other combinations of CPU and hard disk..

Feel free to do the above tests yourself and share the results.

Updated:
  • 22 dic 07: Added test with laptop. Desktop tests done again.

kick it on DotNetKicks.com AddThis Social Bookmark Button

Storing a collection on your app.config using Section Handlers

Posted in app.config | Section Handlers at Tuesday, October 16, 2007 6:22 PM UTC
If you followed this article, you learned how to implement the IConfigurationSectionHandler interface in a custom class to store and read data on or from the app.config (or web.config) using your own typed data structure.

It is also common that you need to store a collection of values on that app.config. Example (see ProxyConfiguracionProcesos tag):


(note that the above image has been cropped to fit the space)

If that's the case, you can create a class to store a single item (in this case a "Proceso"):
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;

namespace PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler
{
struct ProxyConfigurationItem
{
public string Name;
public string AssemblyLocation;
public string ClassName;
public int ExecutionInterval;
public byte Priority;
public byte LogLevel;
public int AlarmIfTakesMore;
public int AlertIfTakesMore;
}

and then parse every item on the custom section using your SectionHandler class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Xml;

namespace PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler
{
class ProxyConfigurationHandler : IConfigurationSectionHandler
{
public ProxyConfigurationHandler() { }



public object Create(object parent,
object configContext, System.Xml.XmlNode section)
{
List<ProxyConfigurationItem> items = new List<ProxyConfigurationItem>();
System.Xml.XmlNodeList processesNodes= section.SelectNodes("Proceso");

//process each Node "Proceso" foreach (XmlNode processNode in processesNodes){
ProxyConfigurationItem item = new ProxyConfigurationItem();
item.Name = processNode.Attributes["Name"].InnerText;
item.AssemblyLocation = processNode.Attributes["Assembly"].InnerText;
item.ClassName = processNode.Attributes["Clase"].InnerText;
item.ExecutionInterval = Convert.ToInt16(processNode.Attributes["Interval"].InnerText);
item.Priority = Convert.ToByte(processNode.Attributes["Prioridad"].InnerText);
item.LogLevel = Convert.ToByte(processNode.Attributes["LogLevel"].InnerText);
item.AlertIfTakesMore = Convert.ToInt32(processNode.Attributes["AlertIfTakesMore"].InnerText);
item.AlarmIfTakesMore = Convert.ToInt32(processNode.Attributes["AlarmIfTakesMore"].InnerText);
items.Add(item);
}
return items;
}

}
}
Also note that you will need a section handler declaration like this:
<section name="ProxyConfiguracionProcesos" type="PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationHandler, PIENSA.UI.Service.ComunicacionAutomata" />
where the first part in the type attribute is the custom class that implements the IConfigurationSectionHandler interface and the second is the assembly where that class is located.

Now, from any part of the code, you can get your collection of processes, as defined on the app.config:

  private List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem> GetProcessesConfiguration() {
List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem> processesConfig= (List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem>)System.Configuration.ConfigurationSettings.GetConfig("ProxyConfiguracionProcesos");
return
processesConfig;
}


kick it on DotNetKicks.com AddThis Social Bookmark Button