Thursday, March 18, 2010

How to determine if Navigation Property in the Entity Framework is set without loading the related record.

Boy that is a long title. There are times (particularly with a Domain Service) that you may want to just check if an Navigation Property has a related record or not. If you just check the Navigation Property for null then you can really mess things up because if you don’t explicitly load the related record, it will be null. It is often a waste to load the related record just to save, etc.

Luckily, there is an easy solution. In fact, it also provides you with the actual foreign key value (if there is one). This can be useful for other things. The Entity Framework removes foreign key properties from the Scalar Properties section of the entity. At first glance it appears that you no longer have access to the foreign key value.

The solution is quite simple. Let’s assume we have two entities. One is Department and it has a relationship (Navigation Property) called Manager that points to the Person entity. What I propose is we do the following to determine if the Manager Navigation Property on Department points to anything.

bool isMgr = myDept.ManagerReference.EntityKey != null;

That is it. So simple, but a little hidden. You can also get the actual key values if you access the properties on the EntityKey property.

Thursday, March 11, 2010

Useful SharePoint Links

Here's a great page on how to "re-parent" a SharePoint site.  This allows us to move sites within SharePoint from one URL to another:  http://blog.solanite.com/keith/Lists/Posts/Post.aspx?List=11564265-6153-4557-aee2-72d1f62fa4c1&ID=7

Here's a great page to answer the question: "What can SharePoint do?"  It doesn't answer the question: "What does SharePoint do well?" (for example, I don't like wikis in SharePoint), but it is a start. 

http://office.microsoft.com/en-us/sharepointtechnology/fx101758691033.aspx

Wednesday, March 10, 2010

RIA Services Rocks!

I cannot believe how cool RIA Services is. I knew it was cool, but I think it does everything I have been dreaming about.

Here is a video that shows how to do a pretty nice application in an hour. Simply AMAZING!!!

http://silverlight.net/learn/videos/all/net-ria-services-intro/

You can download RIA Services for Visual Studio 2008 or VS 2010 Beta. Click here for more info on downloading.

BTW, the entire sample in the video is in the Documentation / Samples of the download.

I’m sold!

Friday, March 5, 2010

Adding a new row to a DataForm in Silverlight

Below is a snippet of code that shows the code that can be used to programmatically add a new record to a DataForm. In this snippet, the DataForm is called _dataform and has a PagedCollectionView (list) of Person objects. The DataForm UI has a + button that adds records. We want to do the same thing, but with a different button, and also set some default or maybe even values that are not on the DataForm. To do this, we call AddNewItem() which is exactly what the + button does. Next we get the CurrentAddItem. This is of type Person so we can cast it as a Person and set any properties that are on the Person object, not just the ones on the DataForm.

public PagedCollectionView Persons { get; set; }

private void btnAdd_Click(object sender, RoutedEventArgs e)
{
_dataform.AddNewItem();

Person newPerson = Persons.CurrentAddItem as Person;
newPerson.Name = "zzz";

newPerson.City = "Tempe";
}
A working version of this code is available here. See the MainPageWithOnlyCustomButtons.xaml.cs file.

Another snippet that may come in handy for adding just like the above, but more for the scenario where you have not displayed any records in the DataForm, but you want to add a new one. This was the scenario I had when I used the DataForm in a Popup Window and showed only one record at a time or in this case needed to allow the user to add a new Person. Here is the snippet for that. The big difference is we have to populate the Persons variable with an empty list so that the DataForm knows what type of objects it is supposed to add. Otherwise, _datagrid will be null and the operation will have failed.
 
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
// the DataForm needs to know what we have a list of, so give it an empty list to start with
// then wehn we tell it to add a new one, it knows what to add/create.
Persons = new PagedCollectionView(new List<Person>());
_dataform.DataContext = null;
_dataform.DataContext = Persons;

_dataform.AddNewItem();
Person newPerson = Persons.CurrentAddItem as Person;
newPerson.Name = "zzz";
newPerson.City = "Tempe";
}

A working version of this code is available here. See the MainPageWithLoadOneRecordAtATime.xaml.cs file.

Thursday, March 4, 2010

A better way of using the ComboBox on a DataForm in Silverlight 3

At first I tried to use the ComboBox on a DataForm in Silverlight 3 and was pretty successful. You read about it here. It worked fairly well for me. It doesn’t update the field that it is bound to, but I think that should be able to be done using the SelectionChanged event. I have not verified that though. The concept behind this implementation was that I had a ComboBox that was populated with Owners (people) objects and I wanted to bind to an OwnerID field (the foreign key).

There was a fair amount of code to translate between the object and id. I don’t really like having to write that code. It also seemed to me that there must be an easier way, especially in the day of RIA Services, Data Services, etc that use Object graphs and the like. The concept behind the implementation I want to cover here is a shift towards a more object based approach and one that I have to believe Microsoft had in mind when they designed the ComboBox because it works well.

Unfortunately, this example is not the same basic as the other implementation, but I think the point will still be pretty clear. This example uses Address and Country tables / objects. Think about an Address table, it could have a foreign key to a Country table so that users are not just typing countries, they are instead selecting them from a ComboBox of countries.

This example is a little more in-depth as far as a real world UI (example is still simple though). In this user interface the page starts with a DataGrid that shows a list of addresses (Address and Country information). When a row in the DataGrid is selected, the DataForm shows the detail of that row and allows the user to change the Country. Any changes are immediately reflected in the DataGrid. The user can save or cancel the changes. The cancel does NOT revert changes or save really, but you can do that by making the Person object implement the IEditableObject interface or by handling the EditEnded event on the DataForm.

This solution does properly notify the DataForm that the select has changed. You can tell because the OK button enables itself automatically when the ComboBox selection is changed. It also properly sets the Country property on the Person object automatically as you would expect. The translation of Country object to CountryID is handled by RIA Services seemlessly, but you could also (if you are not using RIA Services) do that when you save the Person object back to the database.

In order for the DataGrid to get the changes immediately, the Person object must implement the INotifyPropertyChanged interface.

The Country object MUST override the GetHashCode() and Equals() methods. This is because we want to treat instances of the Country class as the same object as long as the have the same foreign key. We can do this because the user can’t change Country, and we just really want a way of showing a user-friendly text version of the Country object to the user, but really have the entire Country object at our disposal.

In the database simulation methods I took care to always get new instances of objects and not reuse them between the ComboBox and the Country property on the Person object. The reason is that it will appear as though the GetHashCode() and Equals() methods don’t need to be overridden. That is because the objects would actually be the same. In a real life scenario, EF or web service, WCF, RIA Services, etc would return different instances of objects each time you return objects. We need to handle real life, so be sure to override those methods.

I populated the ComboBox using code, but you could do it in XML as described here. I find the code to be a little more comfortable to me, but either should work equally as well. The trick is to do that in the ContentLoaded (not to be confused with the Load event) event of the DataForm.

The Countries and Persons properties on the MainPage class are what everything will bind to. This means that we need to populate these variables, THEN set the LayoutRoot.DataContext = this. If you do it in the reverse order, the binding will bind to empty variables and your UI will not show any rows or details.

You can download a working solution from here. I have include the source code below for convenience.

Let’s start with the MainPage.xaml. Here is what it looks like:

<UserControl x:Class="DataFormTest2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical">
<TextBlock>Combobox example</TextBlock>

<StackPanel Orientation="Vertical">
<data:DataGrid x:Name="_datagrid" ItemsSource="{Binding Persons}" Margin="0,0,5,0" AutoGenerateColumns="False">
<data:DataGrid.Columns>
<data:DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
<data:DataGridTemplateColumn Header="Country">
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBlock Text="{Binding Country.CountryName}"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
<data:DataPager Grid.Row="1" Source="{Binding Persons}" PageSize="10" Margin="0" />
</StackPanel>

<dataFormToolkit:DataForm x:Name="_dataform"
ItemsSource="{Binding Persons}"
CommandButtonsVisibility="All"
AutoGenerateFields="True"
AutoCommit="False">
<dataFormToolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<dataFormToolkit:DataField>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</dataFormToolkit:DataField>
<dataFormToolkit:DataField>
<ComboBox x:Name="_comboCountries" DisplayMemberPath="CountryName"
SelectedItem="{Binding Country, Mode=TwoWay}" >
</ComboBox>
</dataFormToolkit:DataField>
</StackPanel>
</DataTemplate>
</dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>

</StackPanel>

</Grid>
</UserControl>
This is the MainPage.xaml.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.ComponentModel;


// Basis for this code was copied from here: http://forums.silverlight.net/forums/p/165152/372651.aspx
// Giant help from: http://weblogs.asp.net/manishdalal/archive/2009/07/03/silverlight-3-combobox-control.aspx

namespace DataFormTest2
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
_dataform.ContentLoaded += new EventHandler<DataFormContentLoadEventArgs>(_dataform_ContentLoaded);

this.Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
}

// This could be done in the Constructor of this page also
void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// IMPORTANT: The Countries and People properties must be set
// BEFORE the DataContext is set to this
Countries = GetCountries();
Persons = new PagedCollectionView(GetPeople());
this.LayoutRoot.DataContext = this;
}

public List<Country> Countries { get; set; }
public PagedCollectionView Persons { get; set; }

#region Database Simulation

// simulate a database query
// used to populate the DataGrid and then the DataForm
private List<Person> GetPeople()
{

// get values for ComboBox
List<Country> countries = GetCountries();

// Get rows for DataGrid / DataForm
List<Person> persons = new List<Person>();
persons.Add(new Person { Name = "Charlie", Country = countries[2] });
persons.Add(new Person { Name = "Lola", Country = countries[1] });
persons.Add(new Person { Name = "Gabe", Country = countries[0] });
persons.Add(new Person { Name = "Jack", Country = countries[2] });
persons.Add(new Person { Name = "Vic", Country = countries[0] });

return persons;

}


// simulate a database query
// use this to populate the ComboBox list of values
private List<Country> GetCountries()
{
List<Country> countries = new List<Country>();
countries.Add(new Country("Andorra", 1));
countries.Add(new Country("Belgium", 2));
countries.Add(new Country("Canada", 3));

return countries;

}

#endregion

void _dataform_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
{
// find the ComboBox in the DataForm and set the ItemsSource
var comboCountries = (ComboBox)_dataform.FindNameInContent("_comboCountries");
if (comboCountries != null)
{
comboCountries.ItemsSource = Countries;
}

}
}


// NOTE: You need INotifyPropertyChanged if you want the DataGrid to automatically update
public class Person : System.ComponentModel.INotifyPropertyChanged
{
private string _Name;

public string Name
{
get { return _Name; }
set { _Name = value; NotifyPropertyChanged("Name"); }
}

private Country _Country;

public Country Country
{
get { return _Country; }
set { _Country = value; NotifyPropertyChanged("Country"); }
}

public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(info));
}
}


}

public class Country
{
public Country(string countryName, int id)
{
CountryName = countryName;
ID = id;
}

public string CountryName { get; set; }
public int ID { get; set; }

// we need to override this for comparisons (see Equals method).
public override int GetHashCode()
{
return ID.GetHashCode();
}

// We override this method so that the values will be selected properly in the ComboBox
// The reason is that two objects are not considered equal if they are not the same object.
// We get two different objects because one would be the objects we got back from the
// database to populate the ComboBox. We would have a different instance of each object
// when we reference the Country property in the Person object we are editing.
// Since ID is a primary key, that is all we really need to use for comparison
// as far as the combo is concerned. This is ok, since we are not editing Country
// in the scenario so its data members never change.
public override bool Equals(object obj)
{
if (obj == null) return false;
Country cityToCompare = obj as Country;
if (cityToCompare == null) return false;
return ID.Equals(cityToCompare.ID);
}
}
}

References

http://betaforums.silverlight.net/forums/p/146398/325962.aspx

http://weblogs.asp.net/manishdalal/archive/2009/07/03/silverlight-3-combobox-control.aspx

http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx

http://msmvps.com/blogs/deborahk/archive/2009/11/25/silverlight-and-ria-adding-a-combobox-to-a-dataform.aspx

Wednesday, February 24, 2010

Convert varchar in format of mm/dd/yyyy hh:mi:ss:mmm to DateTime using T-SQL

It seems like an easy thing to convert a sting to a datetime in SQL Server. I check the MSDN documentation of converting between strings (varchar / nvarchar) to a DateTime datatype. The problems that I ran into is that when I look at the predefined formats that it understands, none of them seem to be mm/dd/yyyy hh:mi:ss:mmm. The only one that is even close is 101 which is U.S. and makes sense. I was a bit surprised when I tried it that it actually automatically recognized the time portion as well. I was very pleased. So, in fact, I thought it should be easy, it looked like it would be difficult, and in the end it is easy if you know the trick.

Here is all I did.

select convert(datetime, '02/22/2010 16:01:51.802', 101)

That returns 2010-02-22 16:01:51.803 (a datetime datatype)

Keep in mind, I used a literal, but you could use any column that is a varchar or nvarchar and contains a string of that format.

NOTE: I have no idea why the last digit is 803 vs 802. It is not a typo on my part. It is actually accurate. So strange. Bug in SQL Server maybe?

Tip: If you happen to need to convert mm-dd-yyyy hh:mi:ss:mmm, you can just use the replace function to change it to the above format.

select convert(datetime, Replace('02-22-2010 16:01:51.802', '-', '/'), 101)

Monday, February 22, 2010

Monitoring a web server

I was recently wanted to find how much cpu, disk I/O, memory, and bandwidth was being used by my web server.

After a little research, I found that there are some basic performance counters in the Windows Performance Monitor (Start Menu | Administrative Tool | Performance). It is also known as System Monitor.

Running

To minimize overhead you probably don’t want to run the performance monitor using the current activity graph or anything like that where the UI is needed. To reduce overhead, you really want to use the following instructions to monitor your system.

  1. Open the System Monitor by going to Start Menu | Administrative Tool | Performance.
  2. Expand the Performance Logs and Alerts node on the left navigation.
  3. Right-click the Counter Logs and select the New Log Settings… menu item.
  4. Name your Log as desired
  5. On the General Tab, click the Add Counters… button. Add the counters discussed below under each of the headings.
  6. On the General Tab, you may want to change the interval to 3 seconds, but nothing more frequent than that if you can help it. Otherwise, the overhead will be too much in many cases.
  7. On the Log Files tab, change the Log file type drop down list to be Text File (Tab delimited) or other format of choice if you will be working with the data later.
  8. On the Log Files tab, click the Configure… button to change the location for the log file to be something other than c:\ in case you run out of disk space, you don’t want to crash Windows. You also may want to set a file size limit.
  9. ON the Schedule tab, you can set it to whatever makes sense for you. I prefer manual settings to start and stop the logging.
  10. Your Log settings will now show up in the detail panel to the right.
  11. Right-click your log settings in the details panel, and choose Start to start the logging.
  12. When you are ready to stop the logging, right-click your log settings in the details panel, and choose Stop to stop the logging.

CPU

When monitoring CPU activity consider monitoring the following counters:

  • Use: Processor: % Processor Time (default is for one total processors)
    • This tells us the percentage of time the CPU spends executing a thread that is not idle.
    • If you have multiple processors, use a separate instance of this counter for it.
    • Consistent 80% – 90% is considered high and should be reduced.
  • Optional Use: Processor: % Privileged Time
    • This tells us the percentage of time the cpu spends executing MS Windows kernel commands. This could be time processing I/O requests.
  • Optional Use: Processor: % User Time
    • This tells us the percentage of time the CPU spends executing user processes such as SQL Server, IIS, apps, etc.

For more information, see this MSDN page.

Disk Usage

When monitoring Disk usage disk activity typically falls into two areas:

  • Monitoring Disk I/O
    • Use: PhysicalDisk: % Disk Time
      • This tells us the percentage of time that the disk is busy with read/write activity.
      • Above 90% is considered high and should be reduced.
  • Detecting Excessive Paging
    • Use: PhysicalDisk: Avg. Disk Queue Length (default)

For more info (including how to troubleshoot bottlenecks, isolating SQL Server disk I/O, etc), see this MSDN page.

Memory (RAM)

  • Rate at which pages are read from or written to disk to resolve hard page faults.
    • Use: Pages/sec (default)
    • Should be under 1000 at all times.
  • There are so many other stats that you can look at 32 in all. They are all under memory.
    • Click here to get a better understanding of memory and what you need to monitor to answer the questions you may be trying to answer.

IIS Bandwidth

NOTE: These number may be a little lighter than that seen from a Firewall since Multicast traffic doesn’t show up and packets filtered by the firewall also don’t show up*.

  • Rate at which the WWW service is sending  and receiving data in bytes per second (both send and receive total)
    • Use: Web Service | Bytes Total/Sec
    • For more info, see here.
  • The total number of bytes of data that have been sent and received by the WWW service since the service started.
    • Use: Web Service | Total Bytes Transferred
    • To get an idea of how much data for the time you are logging you are actually transferring you will need to adjust all your values to baseline at the first value in your log. In other words, you will need to subtract your first amount from your last amount (assuming the service has not been reset).
    • For more info, see here.