Thursday, August 27, 2009

How to get the current user from a Silverlight application

First, when I say current user, I am assuming that you are using NTLM / Windows Authentication / Active Directory. In a ASP.NET website, you would use something like User.Identity.Name. However, in Silverlight there is no such thing. So, the short answer appears to be that you can’t. At least not directly from Silverlight.

The trick is in what other tools do we have that we can use. One option ASP.NET and Javascript combination to get the value from ASP.NET, pass it to the Silverlight control as an initparam, and use it in the Silverlight app. This is is a HUGE security hole. I can’t believe people actual accept this as an option. All someone would have to do is create a .html page that has the Silverlight application object on it, and set the username to some admin user, and there is full access, no password needed. For the code, click here.

Needless to say, I don’t think the above technique is an option. The best option by far is to create a web service and get the value from it. While it is still possible to fake the response from the web server, would be a bit more difficult and would require faking a network route or something similar.

The best option if you can help it is to never need it from Silverlight. Always use this value from the web service that your Silverlight app uses already. That way there is nothing to fake, etc. It is all server-side then. If you have to have the username in the actual Silverlight application, then I recommend don’t use it for security purposes without doing server-side validation also. Think of Silverlight as a first line of defense, and the server-side (web service) being made more robust.

In case, you don’t know what the web service method would look like, below is an example.

public string GetCurrentUsername()
{
return User.Identity.Name;
}

Capturing keyboard shortcut in Silverlight

Let's assume you have some Silverlight application and you want to do something when the user types Ctrl-M for example. It is actually quite simple. This works for User Controls in Silverlight also.

I have read that the key to this is to make sure your root layout element is a Canvas, not a Grid. However, I have not verified that it does NOT work with a Grid.

You Canvas just needs to be wired up to the key down event. Here is what that would look like.

<Canvas x:Name="LayoutRoot" KeyDown="LayoutRoot_KeyDown">…</Canvas>

Here is what the event handler C# snippet would look like.

private void LayoutRoot_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.M)
{
MessageBox.Show("Got Control-" + e.Key.ToString());
}

if (e.Key == Key.Escape)
{
MessageBox.Show("Got Escape");
}
}

I have found that you only get some key events. It seems loosely tied to if the browser implements the keystroke or not. Here are the ones that I know do NOT work: B, D, E, F, G, H, I, J, L, N, O, P, Q, R, S, T, W. The ones that do work are: A, C, K, M, U, V, X, Y, Z.

As you can see many of keystrokes are not usable with the control key (Ctrl). So, I recommend using something like Control-Shift (Ctrl-Shift). I have had much better luck with this. The syntax is a bit trickier, but it is fairly common with handling these types of events. Basically you have to compare Keyboard.Modifiers with the result of the bit operation. Just add your modifier keys as desired.

Here is an example of how to handle the Ctrl-Shift-S keystroke.

private void LayoutRoot_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.S)
{
MessageBox.Show("Got Control-Shift" + e.Key.ToString());
}

if (e.Key == Key.Escape)
{
MessageBox.Show("Got Escape");
}
}

Thursday, August 20, 2009

Getting selected row in a Silverlight DataGrid when a button in a CellTemplate is clicked

Wow, what a long title. :) Here is a better description of what the scenario is. You have a Silverlight application that uses a DataGrid. The DataGrid is bound to a list of Person objects for example. You add a Button to the DataGrid by creating a DataGridTemplateColumn.CellTemplate and putting the Button in it. You want the button to select the row that contains that button, and you want access to the data in that row.

There are really two questions, but the very closely related:

  1. How do I select the row that contains the button that is clicked?
  2. How do I get data for the selected row?

The scenario is not that difficult or uncommon, but the solution was not obvious to me. This may be because I am new to Silverlight, and have most my experience in ASP.NET. The good news is that it is really easy.

Here is what I figured out.

First let’s show the DataGrid in the .xaml file to see how the Button was added in the first place. This example has an Edit button in the first column, and two other columns just to show that there would normally be other columns.

<data:DataGrid x:Name-"MyDataGrid" IsReadOnly="True">
<data:DataGrid.Columns>
<data:DataGridTemplateColumn Header="Edit" CanUserSort="False">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnEditQuote" Click="btnEditQuote_Click" Content="Edit"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTextColumn Header="Other Column1" Binding="{Binding Column1}" Width="110" />
<data:DataGridTextColumn Header="Other Column2" Binding="{Binding Column2}" Width="110" />
</data:DataGrid.Columns>
</data:DataGrid>

The answer to both questions is really quite simple once we are armed with the knowledge that the Button has a property named DataContext. The DataContext property contains the entity related to row that is clicked. 

Here is all we need in our Edit button Click event handler. In this example, I the sender is the Button that was clicked so we can cast it to a Button. The button has a DataContext so we can get the entity related to that row. If we want to access the data in the entity we will need to cast it to the appropriate type since it is of type object when returned from btn.DataContext. When we set the MyDataGrid.SelectedItem = entity this changes the MyDataGrid.SelectedIndex property and the UI is updated with the new selected row selected (highlighted). In this case, I use a MessageBox to show that we are getting the selected row index and the correct data from the other columns.

void btnEditQuote_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
var entity = btn.DataContext as MyApp.Entities.MyEntity;
MyDataGrid.SelectedItem = entity;

MessageBox.Show("Selected row: " + MyDataGrid.SelectedIndex + " - " + entity.Column1 + ", " + entity.Column2);
}

Wednesday, August 19, 2009

Performing queries using optional parameters without Dynamic SQL

Imagine you have a application that will be querying your database. The user is presented 3 different fields that will be used to filter the results that are shown to the user. They are all optional fields. The question is how can I best implement the solution.

There are several ways to approach the problem.

Option 1: Dynamic SQL

While this is an option, it is prone to SQL injection and can be difficult and error prone trying to defend against it. I know you can use parameters with it to some extent, but I think it is still not a good choice for the following reasons. It can also be difficult to read because of all the extra quotes, formatting, etc. Debugging is also difficult at best. For these reasons, try to stay away from this option.

Option 2: LINQ

You can use LINQ to SQL or LINQ to Entities to solve the problem. I highly recommend LINQ for this purpose if you have the opportunity.

Below is a snippet of code that queries the Person table by 3 optional parameters. Be sure to create and close your context object. This does not use a stored procedure, but LINQ is by design protected from SQL Injection. This is an extremely easy way to implement the desired functionality.

var people = from p in ctx.Person
select p;

if (nameParam != null)
{
people = people.Where(p => p.Name == nameParam);
}

if (phoneParam != null)
{
people = people.Where(p => p.Phone == phoneParam);
}

if (ssnParam != null)
{
people = people.Where(p => p.SSN == ssnParam);
}

return people;

Option 3: Stored Procedure

Perhaps you don’t have LINQ or know LINQ yet. I recommend you take this as an opportunity and learn it, but that is a personal choice. If you can’t use LINQ or perhaps the project you are doesn’t use it and you want to keep the way of accessing the database consistent within the project, then LINQ may not be a good choice.

Within a stored procedure you could write if else statements to control what queries are executed. However, this can get messy for more than two optional parameters. This is because you need to have 4 cases and 4 select statements. If you have three optional parameters, then you have 8 cases and 8 select statements. If you have four optional parameter, then you have 16 cases and 16 select statements. As you can see this option is not a very scalable solution, and I don’t recommend it.

I do however recommend the following technique which uses OR and NULL checks in the where clause. Below is an example of a T-SQL snippet that does the same thing as the LINQ example.

select * from Person
where
((@Name IS NULL) OR (Name = @Name))
AND
((@Phone IS NULL) OR (Phone = @Phone))
AND
((@SSN IS NULL) OR (SSN = @SSN))

Here is the same example, but with begins with search.

select * from Person
where
((@Name IS NULL) OR (Name = @Name))
AND
((@Phone IS NULL) OR (Phone like @Phone + '%'))
AND
((@SSN IS NULL) OR (SSN like @SSN + '%'))

You can also use Coalesce or isnull. However, It becomes complex to try to do like instead of equal to. I didn’t actually complete this in my test due to the complexity and desire for simplicity of using the above example.

For information on performance of Coalesce, ISNULL, and NULL / OR combination, click here.

Tuesday, August 18, 2009

Faster Silverlight testing

Using Control-F5, F5, and Control-Shift-B in Visual Studio 2008 works well for smaller Silverlight projects, but as you get more projects in your solution, etc building the entire solution just to see the difference in XAML or .cs can take too long for my liking.

What I figured out is that in many cases, all I have to do it build the Silverlight project, and leave open the browser (like you would do in ASP.NET) and just F5 (refresh page) works much faster.

This trick works because when the Silverlight app is built it updates the reference in the website that is hosting the Silverlight app. When you refresh the browser page, it detects that the .xap file changed and sends the new .xap file that you just built.

This trick really isn’t new or specific to Silverlight, but it is worth mentioning.

Monday, August 17, 2009

Switching Pages with Silverlight

If you have multiple pages (User Controls) in your Silverlight app and you want a specific page to show based on the page that it is hosted in then keep reading. This technique is both powerful, fairly extensible, and easy. The main idea is that we use the Application startup event to specify the page we want to show. This is determined by a class we create called PageSwitcher. All this could be done in the App.xaml.cs, but in the interest of making this a bit more reusable, I created a PageSwitcher.cs to hold this logic.

Here are the steps to do this:

Open your App.xaml.cs and change contents of the Application_Startup event handler to the following:

this.RootVisual = PageSwitcher.Switch();

Next create a class called PageSwitcher.cs in your Silverlight project. You can copy and paste the code below.

using System;
using System.Windows;
using System.Windows.Controls;

namespace MySilverlightApp
{
public class PageSwitcher
{
/// <summary>
/// Loads the proper Page (UserControl) based on the requested url.
/// </summary>
/// <returns>An instance of the page to load.</returns>
public static UserControl Switch()
{
string url = Application.Current.Host.Source.OriginalString;

if (IsPage(url, "MyPage1"))
{
return new MyPage1();
}
else if (IsPage(url, "MyPage2"))
{
return new MyPage2();
}
else
{
throw new Exception("Unknown Page parameter for url: " + url);
}
}

/// <summary>
/// Returns true if the Page=xxx is found in the Url where xxx is the Page, else false.
/// </summary>
/// <param name="Url">The url that is used to load the .xap file</param>
/// <param name="Page">The name of the Page that is to be loaded</param>
/// <returns></returns>
private static bool IsPage(string Url, string Page)
{
return Url.ToUpper().Contains(string.Format("Page={0}", Page).ToUpper());
}
}
}

The last key to this is to make sure you pass a unique url in the .aspx page that contains your Silverlight app. Here is an example of what should use to load your silverlight application. I am not changing any (from the default code that Visual Studio 2008 generates), except adding a Page=xxx to url.

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MySilverlightApp.xap?Page=MyPage1" MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

On the other .aspx page, I would reference the corresponding other page.

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MySilverlightApp.xap?Page=MyPage2" MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

Now you have two .aspx pages that reference the same .xap file, but load the desired page in the Silverlight application based on a parameter passed from each of the .aspx page.

Tuesday, August 11, 2009

Can’t connect to SQL Server 2005 via JDBC, but can via SQL Management Studio

I found out the hard way that SQL Server 2005 Developer Edition has TCP/IP connections disabled. This could also be the case for any version of SQL Server 2005 as well. The behavior that you may see is that via SQL Server Management Studio you can connect to your database server just fine, but when you use some thing like JDBC that requires TCP/IP you can’t connect and get errors. For example, you might get a Network Error on Connect.

To enable TCP/IP connections on SQL Server 2005 Developer Edition (should also work for Enterprise, etc in case it is not configured with the default settings) do the following.

  1. Use remote desktop to access the server where the SQL Server service is running.
  2. Go to the Start Menu | Microsoft SQL Server 2005 | Configuration Tools | SQL Server Configuration Manager.
  3. Once the window opens, expand the SQL Server 2005 Network Configuration. You should then see something like Protocols for MSSQLSERVER where MSSQLSERVER is the name of your SQL Server Instance. You should see something like this, except TCP/IP will have a red icon if it is disabled.:

    image
  4. Right-click on TCP/IP on the right panel, and choose Enable.

Even if you enable or already had TCP/IP enabled, you may not have remote connections enabled. To enable remote connections to SQL Server, do the following:

  1. Open Microsoft SQL Server Management Studio and connect to your desired server in the Object Browser.
  2. Right-Click the server and choose Properties.
  3. Click the Connections page. You should see something like the window shown below. The Allow remote connections to this server may NOT be checked as shown below. If it is not checked then you can only connect to the SQL Server from the machine where the SQL Server is running. Be sure to check the checkbox to enable remote connections.

    image

     

Re-link database user after restoring a MS SQL Server database

If you have a database (let’s say a production server call MyDB) that has SQL Server users that have logins into a database. For example let’s pretend the login name is called MyAppUser.

When I take a backup of MyDB and then restore in to another server (let’s say my development database server) the user MyAppUser will not be able to login to my server. The reason is that the SQL Server login and the database User records have become disconnected.

To reconnect them (assuming the name is the same in both systems), you can use the following T-SQL to link them again.

use MyDB
go
sp_change_users_login 'Update_One', 'MyAppUser', 'MyAppUser'

Thursday, August 6, 2009

Passing a generic as a parameter to a method in C#

The syntax for generics can be a little strange until you get used to. I am still getting used to some parts of it. :) Maybe you want to write a method that takes a generic type as a parameter, but you don’t want to lock down what the type for the generic is. What we are talking about is Generic Type Parameters. You can even have multiple Type Parameters.

I think the best way to describe what I am talking about is to show an example.

private void MyMethod1(Array myArray)
{
// do something with the array here.
}


private void MyMethod2(List<int> myArray)
{
// do something with the generic array here.
}

private void MyMethod3<T>(List<T> myArray)
{
// do something with the generic array here.
}

private void MyMethod4<TMyTypeA, TMyTypeB>(List<TMyTypeA> myArray, List<TMyTypeB> myArray2)
{
// do something with the generic array of type a here.
// do something with the generic array of type b here.
}

private void ExampleCallingMethod()
{
// nothing fancy here
Array array1 = "a,b,c".Split(',');
MyMethod1(array1);

// nice if you know you always want to accept a list of ints
List<int> intArray = new List<int> { 1, 2, 3 };
MyMethod2(intArray);

// nice if you want to accept a list of any kind of objects
// For example ints or strings
MyMethod3<int>(intArray);
MyMethod3(intArray);


List<string> strArray = new List<string> { "a", "b", "c" };
MyMethod3<string>(strArray);
MyMethod3(strArray);

// nice if you have to accept (in this case) two lists of any kinds of objects
MyMethod4<string, int>(strArray, intArray);
MyMethod4(strArray, intArray);
}
In the above examples you can see that <T> is used as a Type Parameter. It is a placeholder for whatever type is passed to the Generic List. <T> is not a key word, but more a naming convention. The surrounding <> is important though. As described here (Generic Type Parameters) it is convention to prefix the name of a Type Parameter with a capital T. For example, TMyTypeA or TMyTypeB. It also seems to be convention to just use <T> if there is only one Type Parameter.
In the case where you don’t want to accept any type, but still want to use Generics, you can restrict the types that can be passed as described on this (Constraints on Type Parameters) page.
For other related topics on programming Generics, check out: Generics Programming Guide.