Thursday, May 8, 2008

Updated VSTS VPC Available

Brian Randell has just given the official word that there is a new VSTS/TFS 2008 VPC available:

It's no April's Fool day joke.

A new all-up image has been posted. It's been updated with Office SP1, the December 2007 TFS Power Tools, and all Windows Updates.

Also available with it are some labs to help you look at the various aspects of Team System.  And, for that final bit of good news, it won't expire until December 31st, 2008.

Don't forget, you can use Free Download Manager to help you queue up and download all the different ZIP files.

DirectX MVP 2008

This would not have been possible if it wasn't for the people in the community.

Thank you to everyone, SADeveloper.NET, Gamedev.net and the XNA community over at http://creators.xna.com/. These are the communities that have made this possible! Thank you!

If you would like me to come and present a session on XNA or Game Development concepts, please don't hesitate to contact me!


Thank you once again to everyone! 

Share this post: email it! | bookmark it! | digg it! | reddit! | kick it! | live it!

Visual Studio Team Suite vNext "Rosario Express" will be FREE

Auf der MIX-Konferenz in Las Vegas hatte ich die Gelegenheit für eine exklusive Vorstellung im Akquaknox-Restaurant [1] im The Venetian Hotel. Zusammen mit anderen Microsoft Regional Directors und dem neuen Vice President Scott Guthrie durfte ich einen ersten Blick auf die letzten Bits des Visual Studio 2008 Codename "Rosario" werfen. Es handelte es sich hierbei aber nicht um die öffentlich verfügbare CTP-Version [2], sondern um die neue Visual Studio Team System "Rosario Express" Version, die vollständig kostenfrei sein wird.

Die VSTS Express Edition ist auf ein Team von 5 Personen beschränkt und kann nur mit dem TFS Workgroup Edition an einem Standort betrieben werden. Einzige Beschränkung sind maximal 10.000 Work Items pro Solution, mehr werden aber die meisten Projekte onehin nicht brauchen. Die Entscheidung finde ich sehr spannend, insbesondere da die meisten kleinen Teams die Team System Funktionalität sehr wohl benötigen und gut gebrauchen können. Diese Entscheidung wird definitiv zur Verbreitung von Team System beitragen.

Eine öffentliche CTP wird voraussichtlich Ende April verfügbar sein. Vielleicht klappt es noch rechtzeiig zur TeamConf. Aber, wie immer mit den Releases, erstmal abwarten.

[1] http://www.venetian.com/AQUAKNOX.aspx
[2] http://www.microsoft.com/downloads/details.aspx?familyid=8450eff5-24ad-44c3-ab91-1ed88ef2f4f0&displaylang=en

Setting up authentication in asp.net MVC

I wanted to setup user authentication in asp.net MVC, and I wanted it to tie in with the built-in IIdentity and IPrincipal support that is already in asp.net, but I did not want to have to setup asp.net membership services. I wanted to have my own user objects, and I wanted to save/retrieve them through my user repository just like my other domain objects. All of this was done for a demo, so it is certainly not production quality code, but I hope it helps you.

First off I setup my User table in my database, it looks like this:

This is obviously just a start, but it is enough to get the job going. I have dragged this database table onto the Linq To Sql designer and it created my User object like so:

Next in my UserRepository class I have a method that looks like this:

public User GetUserByUsername(string username)
{
    RecipeZoneDataContext context = ContextFactory.GetRecipeZoneContext();
    return (from u in context.Users where u.UserName == username select u).SingleOrDefault();
}

This way when someone tries to login, I can pass their username into this method and it tries to look that user up. You will notice that we use a "SingleOrDefault" method in order to return a single user or "null" in case there is no user by that name. Next I have created a method on my UserController named "Login" that looks like this:

public void Login()
{
    _viewData.Redirect = Request.QueryString["redirect"];
    RenderView("Login", _viewData);
}

Here we are looking for a query string parameter named "redirect" that is going to contain a url that we will redirect to upon successful login. We assign this to our UserControllerViewData class and then pass that to RenderView. The UserControllerViewData is just a class that I use to hold view data for most of the actions on my UserController class.

My Login view is then rendered:

<asp:Content ID="Content1" ContentPlaceHolderID="MainContentPlaceHolder" runat="server">  
    <%= Html.RenderUserControl("~/Views/Shared/ErrorMessages.ascx") %>
    <%using (Html.Form<UserController>(c => c.LoginValidate(RedirectUrl)))
      { %>
    <table>
        <tr>
            <td>
                Username:
            </td>
            <td>
                <%=Html.TextBox("User.UserName")%>
            </td>
        </tr>
        <tr>
            <td>
                Password:
            </td>
            <td>
                <%=Html.Password("User.Password")%>
            </td>
        </tr>
        <tr>
            <td>
            </td>
            <td>
                <%=Html.SubmitButton("login", "Login")%>
            </td>
        </tr>
    </table>
    <% } %>
</asp:Content>

Here we are using a few of our Html helper methods. We use the Html.Form helper method in order to create our html form as well as to render our url that the form will post back to. I am using the overload that takes an Expression and then renders the url based on the controller type and method that you call on it in the expression. You can see that we are posting back to a different action called "LoginValidate" and we are passing the "RedirectUrl" as a parameter to it.

The LoginValidate controller action looks like this:

public void LoginValidate(string redirect)
{
    if (Models.User.ValidateUser(Request.Form["User.UserName"], Request.Form["User.Password"]))
    {
        if (!String.IsNullOrEmpty(redirect) || String.Compare(redirect, "none", true) == 0)
        {
            Response.Redirect(redirect, true);
        }
        else
        {
            RedirectToAction("", "");
        }
    }
 
    _viewData.ErrorMessages.Add("Invalid username or password.");
    RenderView("Login", _viewData);
}

First we pass our UserName and Password to our User.ValidateUser method. If the user is validated then we check for a redirect and if one exists then we redirect. Otherwise we redirect to a default controller and action. Here we have entered no defaults, you would want to fill them in. If we don't succeed then we add an error message to the view data and redirect back to our Login action. There is a bit of data we have to put into the web.config first though:

<authentication mode="Forms">
  <forms name=".APPNAME" protection="All" cookieless="UseCookies" />
</authentication >

This just sets up our application to use forms authentication, and then sets a few parameters on it. After we have this setup we can now call the ValidateUser method. The ValidateUser method looks like this:

public static bool ValidateUser(string username, string password)
{
    var userRepository = new UserRepository();
 
    User user = userRepository.GetUserByUsername(username);
    if (user == null)
    {
        return false;
    }
 
    if (String.Compare(user.Password, password, false) == 0)
    {
        var authTicket = new FormsAuthenticationTicket(1, username, DateTime.Now,
                                                       DateTime.Now.AddMinutes(30), true, "");
 
        string cookieContents = FormsAuthentication.Encrypt(authTicket);
        var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieContents)
                        {
                            Expires = authTicket.Expiration,
                            Path = FormsAuthentication.FormsCookiePath
                        };
        if (HttpContext.Current != null)
        {
            HttpContext.Current.Response.Cookies.Add(cookie);
        }
        return true;
    }
    return false;
}

We pull the user and if it doesn't exist then we immediately return. Then we compare passwords (yes, there is no encryption at this point, like I said, demo) and if they match then we create our authentication ticket. This is part of the forms authentication system and it lets us specify which user is currently logged in. We create a cookie with our data and write it out, then return true. Pretty simple! This saves our username that is logged in, but what about our roles? We need to be able to tell if someone is a normal user or an administrator. In order to do this we have to implement the Application_AuthenticateRequest method in the global.asax file.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{            
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {                
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        var identity = new GenericIdentity(authTicket.Name, "Forms");
        var principal = new CustomPrincipal(identity);                                
        Context.User = principal;
    }
}

Here you see how we are pulling the authentication ticket out of the cookie (if it exists) and we are creating a GenericIdentity object (this is because we don't need any additional functionality for our IIdentity class) and then we create an instance of our CustomPrincipal class which is just a class we have created which implements IPrincipal. This interface has the methods for checking roles. In this application I have just put in some simple code to test for roles since I wanted to keep it simple. This class looks like this:

public class CustomPrincipal : IPrincipal
{        
    public CustomPrincipal(IIdentity identity)
    {
        Identity = identity;
    }
 
    public bool IsInRole(string role)
    {
        if (String.Compare(role, "admin", true) == 0)
        {
            return (Identity.Name == "JustinEtheredge");
        }
        else
        {
            return false;
        }
    }
 
    public IIdentity Identity
    {
        get; private set;
    }
}

As you can see we just check for one role, "admin", and I hardcoded it against my username. I could obviously create a table to manage this, but I have not done so. So, now that we all of this setup, how are we going to use these roles? Well, we are going to create custom ActionFilterAttributes! Of course! I want to be able to protect my controller actions like this:

[UserInRole("Admin")]
public void List(int? page)
{
    _viewData.Users = _userRepository.GetUsers();
    RenderView("List", _viewData);
}

This UserInRoleAttribute will now make sure that you are authenticated and that you are in the "admin" role before it will let you access this controller action. If you aren't authenticated then it will forward you to the login page with a redirect to send you back to where you came from. This attribute looks like this:

public class UserInRoleAttribute : ActionFilterAttribute
{
    private readonly string roleName;
 
    public UserInRoleAttribute(string roleName)
    {
        this.roleName = roleName;
    }
 
    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
        if (filterContext.HttpContext.User.IsInRole(roleName)) return;
 
        //use reflection until they expose this method
        MethodInfo methodInfo = filterContext.Controller.GetType()
            .GetMethod("RedirectToAction",
                       BindingFlags.ExactBinding |
                       BindingFlags.NonPublic |
                       BindingFlags.Instance, null,
                       new[]
                           {
                               typeof (RouteValueDictionary)
                           }, null);
        methodInfo.Invoke(filterContext.Controller,
                          new object[]
                              {
                                  new RouteValueDictionary(
                                      new
                                          {
                                              controller = "User",
                                              action = "Login",
                                              redirect = filterContext.HttpContext.Request.Url.AbsolutePath
                                          })
                              });
    }
}

This class is actually very simple. The constructor simply takes a roleName and saves it. When the action is called the OnActionExecuting method is called and we simply test the current user to see if they are in the saved role. Otherwise we use some reflection in order to call "RedirectToAction" on the controller class since it is private. Hopefully they will implement this method in the ActionFilterAttribute base class, or they will make it public on the Controller class. But for right now this is the easiest way I could find to call it.

So, what else do we need? Well, we need some way for a user to logout. For this I simply added a Logout action that looks like this:

public void Logout()
{
    FormsAuthentication.SignOut();
    RedirectToAction("List");
}

That is all. You just call SignOut and then redirect to whatever action you want. You could also add a redirect parameter here if you wanted, so you could return to whatever page you clicked "logout" on.

So, there you have it. I'm sure that there is something that I am forgetting, but this post is pretty long and I am tired. :) Please let me know if you see anything that I did wrong or could have done differently! I hope you enjoyed it!

How To: Use the XmlDataSource object load XML data to a GridView

Last week I showed you how to read an XML file, load it to a DataSet and assign those values into a GridView. Today I'll show you how you can read an XML file using the XmlDataSource object.

Let's assume that we have an XML file called Symbols.xml in our App_Data folder

that contains the following data

<?xml version="1.0" encoding="utf-8" ?> <Symbols>     <Symbol ExecutionSymbol="ATT" Name="AT&amp;T"></Symbol>     <Symbol ExecutionSymbol="MSFT" Name="Microsoft"></Symbol>     <Symbol ExecutionSymbol="GOOG" Name="Google"></Symbol>     <Symbol ExecutionSymbol="CSCO" Name="Cisco"></Symbol>     <Symbol ExecutionSymbol="IP" Name="International Paper Co."></Symbol>     <Symbol ExecutionSymbol="MF" Name="MF Global"></Symbol>     <Symbol ExecutionSymbol="Q" Name="Qwest Communications International Inc."></Symbol>     <Symbol ExecutionSymbol="BMC" Name="BMC Software Inc."></Symbol>     <Symbol ExecutionSymbol="WCI" Name="WCI Communities Inc."></Symbol>     <Symbol ExecutionSymbol="SPY" Name="SDRs"></Symbol>     <Symbol ExecutionSymbol="LEH" Name="Lehman Brothers Holdings Inc."></Symbol>     <Symbol ExecutionSymbol="XLF" Name="Financial Select Sector SPDR"></Symbol>     <Symbol ExecutionSymbol="QQQQ" Name="PowerShares QQQ TR 1"></Symbol>     <Symbol ExecutionSymbol="IWM" Name="IShare Rus 2000 INDX"></Symbol>     <Symbol ExecutionSymbol="GE" Name="General Electric Co."></Symbol>     <Symbol ExecutionSymbol="MER" Name="Merrill Lynch Co., Inc."></Symbol>     <Symbol ExecutionSymbol="BAC" Name="Bank of America Corporation"></Symbol>     <Symbol ExecutionSymbol="INTC" Name="Intel Corp"></Symbol>     <Symbol ExecutionSymbol="F" Name="Ford Motor Co."></Symbol>     <Symbol ExecutionSymbol="QID" Name="UltraShort QQQ ProShares"></Symbol> </Symbols>

and we want to load it to a GridView with no server-side code and a quick and easy way. The answer is to use the XmlDataSource object. The XmlDataSource control is an ASP.NET control that allows you to automatically read XML Data and make that data readily available to any ASP.NET control.

To start using this control, go to your Toolbox and drag the XmlDataSource control to your page.

Once the control is on the page it would popup a dialog that has configuration options for our XmlDataSource control. Click the "Configure Data Source" button to configure our XmlDataSource

A popup like below will come up that allows you to select the Xml file you want to your XmlDataSource object to read. It also gives you the option to select the XSL file. You can also specify an XPath expression to use to filter the data in our Xml.

Click the "Browse" button for the "Data File" option to select an XML file.

A new dialog will appear that will let you navigate the folder tree to select your desired XML file

Click "Ok" and you'll be taken back to the "Configure Data Source" screen. Click "Ok" again to finalize the XML data assignment.

Now that we have the file set in to our XmlDataSource control we need assign it to a control. We can do that by dragging a GridView control to our form.

Next, we need to assign the XmlDataSource control as the data source for our GridView. We can do this by selecting our XmlDataSource from the "Choose Data Source" dropdownlist.

Click "XmlDataSource1" and you will notice that our GridView was automatically updated and now shows the contents of our XML file.

Easy huh? Next up, Consuming Web Services in ASP.NET