Welcome to MSDN Blogs Sign in | Join | Help

It's Spann, not spam

Technical blog to provide content that developers find useful.
This Week’s Silverlight Postings

Continuing the Silverlight goodness that is out there.  Here is this week’s finds.

Silverlight Postings

This week I realized that there are so many good blog entries going on around me on the subject of Silverlight.  I have decided that I would share with you the ones I have found this week.  So here they are!

And a good one to bookmark –> Silverlight Resource Kit

Technorati Tags:
Zune HD Preorder!

Zune HD will launch on September 15 and is now available for preorder. The 16-gigabyte (GB) version will cost $220, and the 32-GB version will be $290.

http://arstechnica.com/microsoft/news/2009/08/zune-hd-16gb-is-220-32gb-is-290-coming-on-september-15.ars

http://www.zune.net/en-us/mp3players/zunehd/default.htm

Windows 7

I have upgraded my machine from Windows 7 RC to Windows 7 RTM.  I have found two new tips that I thought I would share with the world.  Although I think these tips were in the previous builds but I just came across them.  So if these are a repeat, sorry!

Live Clutter-Free

Windows 7 gets rid of all the extra windows behind your active window.  Just hit Win+Home to minimize all inactive windows.  To restore the windows when you would like them, just press Win+Home again.

Help the Help Desk Help You

Solving problems unique to a machine can be an arduous task for both the end-user and the help desk. That’s why Windows 7 introduces the Problem Steps Recorder, a screen-capture tool that allows the end-user to record the problems they’re having step-by-step. It’s as simple as hitting “record” then adding in comments as needed. A HTML-based file is converted to a .ZIP folder, which is easily passed on to the help desk. The program is accessible from the Control Panel under “Record steps to reproduce a problem” or run psr.exe from Explorer.

Technorati Tags:
Exporting Binary Files Inside SharePoint WebPart

So today I ran into an issue that has sure to have plagued the SharePoint developer community in the past.  My requirement was to export data rendered in a grid to Excel.  Seems simple enough. 

So I have a DevExpress ASPxGridView control and a DevExpress ASPxGridViewExporter control inside my WebPart.  Everything was working as expected.  Here is the initial code:

   1:  protected void btnXlsExport_Click(object sender, EventArgs e)
   2:  {
   3:      DoDataBinding();
   4:      GridViewExporter.WriteXlsToResponse();
   5:  }

Deployed my WebPart and the trouble begins…

After spending half a day trying to figure out why my WebPart or for that matter, my Page would stop responding after I exported to Excel, I found a post that explained it so well.  Basically the problem is with the javascript that SharePoint embeds into every page for you.  SharePoint wants to make your life easier by protecting you from unnecessary double-posts.  Well unfortunately that is the cause of the stream output rendering your page useless.

The fix:

First you need to add the following to your button’s ClientClick event.

<asp:Button ID="ExcelExport" runat="server" Text="Export to Excel" OnClick="btnXlsExport_Click"

CausesValidation="false" OnClientClick="exportRequested=true;" />

Second, you need to add the following code to the OnLoad event of the Page/Control.

private const string startupScriptKey = "alterFormSubmitEvent";

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    
    if(!this.Page.IsStartupScriptRegistered(startupScriptKey))
    {
        StringBuilder beforeSubmitJS = new StringBuilder();
        beforeSubmitJS.Append("var beforeFormSubmitFunction = theForm.onsubmit; \n");
        beforeSubmitJS.Append("theForm.onsubmit = function(){ \n");
        beforeSubmitJS.Append("var returnVal = beforeFormSubmitFunction(); \n");
        beforeSubmitJS.Append("if(exportRequested && returnVal)");
        beforeSubmitJS.Append("{_spFormOnSubmitCalled=false; exportRequested=false;} \n");
        beforeSubmitJS.Append("return returnVal; \n");
        beforeSubmitJS.Append("}; \n");

        this.Page.ClientScript.RegisterStartupScript(
            this.GetType(), 
            startupScriptKey, 
            beforeSubmitJS.ToString(), 
            true);
     }
 }

Finally, deploy your WebPart to the SharePoint site.  You now have a responsive page and exporting the binary files that your users wanted!

Many thanks go out to Michael Nemtsev and Andy Spears for the great work.

Technorati Tags: ,,
IIS SEO Toolkit Beta

The IIS team today announced the IIS Search Engine Optimization (SEO) Toolkit beta – a free toolkit that helps Web developers, hosting providers, and server administrators improve their site’s relevance in search results by recommending how to make them more search engine friendly.  The SEO Toolkit Beta is available for installation via the Microsoft Web Platform Installer 2.0 Beta.

The IIS SEO Toolkit can:

  • Improve the volume and quality of traffic to Web site from search engines.
  • Control how search engines access and display Web content.
  • Inform search engines about locations that are available for indexing.

The IIS SEO Toolkit includes three modules that integrate with IIS Manager.

  • Site Analysis, which suggests changes that can help improve the volume and quality of traffic to your Web site from search engines.
  • Robots Exclusion, which makes it easier to control and restrict the content that search engines index and display.
  • Sitemaps and Site Indexes, which can help inform search engines about locations that are available for indexing.
Technorati Tags:
VS2010 Beta 1 Extensibility

While reading my email, I came across a message that looked very interesting.  It was an email in regards to the extensibility of the Visual Studio 2010 Beta 1.  Back in fall at the PDC conference, the Visual Studio team showed off how easy it was to extend Visual Studio.  So you might be asking yourself, well so! 

Examples of cool things you can do:

  • Overlay rich metadata on top of the editor to bring new information to your fingertips.
  • Develop new project templates to accelerate your work.
  • Create innovative user interface elements using the WPF shell.
  • Upload your extension to the Visual Studio Gallery to find it in the Extension Manager.

Just from looking at the stuff available from the gallery, I think the RegEx Editor Sample is very promising.  I can’t tell you how many times I have gone out to the internet to find Regex engine to help parse strings.

Have a look for yourself.  Maybe you will find a tool that will help take a burden from your daily coding.  If you feel lucky, maybe you will be the next developer to write the next best extension!

Happy coding!

Visual Studio 2010 Beta 1

Monday, May 18th, Visual Studio 2010 Beta 1 (Professional, Team Suite, Team Foundation Server) will be available to MSDN Subscribers through MSDN Subscriber Downloads and to the general public on Wednesday, May 20th through Microsoft Downloads.

Today, Microsoft made available for download the Visual Studio 2010 and the .NET Framework 4 Beta 1.  Microsoft encourages customers and partners to download and evaluate the Beta, which ultimately results in changes and improvements to the final product.

Visual Studio 2010 and the .NET Framework 4 Beta 1 represents a substantial amount of the functionality that will be in the final shipping version of the products.  Later this year, Microsoft will share more details about the coming version of Visual Studio and the .NET Framework.

Top Selling Books At Tech Ed

As of Day 4, the list of the top 10 selling books at Tech Ed 2009 are:

  1. Windows Server 2008 Hyper-V
  2. Professional ASP.NET MVC 1
  3. Mastering System Center Configuration Manager
  4. Windows Server 2008 Hyper-V Unleashed
  5. Professional Silverlight 2 with ASP.NET
  6. Windows PowerShell Unleashed
  7. Silverlight 2 in Action
  8. Inside MS SQL Server 2008 T-SQL Querying
  9. Programming Entity Framework
  10. Microsoft Visual Studio Tips
Book List

I keep a running book list of technical books that I have interests as a task item in my Outlook mailbox.  I am going to share with you that list.  Now, these books are books that I have not purchased yet but plan on purchasing in the near future.

  1. Pattern Languages of Program Design
  2. More Effective C#: 50 Specific Ways to Improve Your C#
  3. Pro LINQ Object Relational Mapping in C# 2008
  4. JQuery in Action
  5. Professional ASP.NET MVC 1.0
  6. Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation
  7. Data Driven Services with Silverlight 2
  8. Professional Microsoft SharePoint 2007 Development Using Microsoft Silverlight 2
Dual Boot Windows 7 and Windows Server 2008 R2

Today I installed Windows 7 RC on my Lenovo T61p laptop.  Things went very well.  It runs very fast (teammate clocked it at 41 seconds to boot). 

I was then approached by a team member that we will be using Hyper-V images.  Now, these Hyper-V images contain Enterprise MOSS and developer tools.  I don’t like messing with my everyday laptop.  But after much consideration, I have decided to dual boot the machine.  So here are the steps that I performed.

  1. Install Windows 7 RC Enterprise x64 (currently available through MSDN subscriptions).
  2. Install Windows Server 2008 R2 Standard RC x64 (currently available through MSDN subscriptions).

Windows is smart enough to add records into the boot manager for you.  This saves a ton of time and bcdedit commands that do not have to be typed out.  Now I have x64 environment both client and server.

UPDATE:  If you would like Windows 7 to be the default boot loader, then run this command from a elevated command prompt:  bcdedit /set {bootmgr} default {current}  If you would like to cut the timeout, use this command bcdedit /set {bootmgr} timeout 15 (The 15 is the value that you would like to set).

WPF and Directory Services

A few weeks ago, a friend of mine asked a question about using WPF to display Organizational Units in a TreeView control.  My friend had a good grasp about getting OU’s from Active Directory.  He just needed to be shown the way to bind his results to the TreeView control.  So I took the challenge.  The first thing I had to do was setup an environment that had multiple OU’s defined.  Luckily I had a set of virtual machines with three Domain Controllers with many levels of OU’s.  So I fired them up and created a machine to act as a client in the domain.  So the client machine consisted of Windows XP with SP3, Visual Studio 2008, SQL Server 2005, Office 2007, etc.  Very typical developer machine configuration.  So, here is the proof of concept  WPF to bind the results of the LDAP query.

WPF

<Window

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

x:Class="ActiveDirectory.Window1"

x:Name="Window"

Title="Window1"

Width="640" Height="480">

    <Grid x:Name="LayoutRoot">

        <TreeView HorizontalAlignment="Left" Margin="5" x:Name="OrganizationUnits">

            <TreeView.ItemTemplate>

                <HierarchicalDataTemplate>

                    <TextBlock Padding="2" />

                </HierarchicalDataTemplate>

            </TreeView.ItemTemplate>

        </TreeView>

    </Grid>

</Window>

C# Code Behind

using System;

using System.DirectoryServices;

using System.Text;

using System.Windows.Controls;

 

namespace ActiveDirectory

{

    public partial class Window1

    {

        #region Fields

        private static string preferredServer = null;

        private static string appAccountId = null;

        private static string appAccountPwd = null;

        #endregion

 

        #region Constructors

        public Window1()

        {

            this.InitializeComponent();

 

            using (SearchResultCollection src = GetTopOUs())

            {

                foreach (SearchResult result in src)

                {

                    TreeViewItem item = new TreeViewItem();

 

                    if (result.Properties.Contains("Name"))

                    {

                        foreach (string name in result.Properties["Name"])

                        {

                            item.Tag = name;

                            item.Header = name;

                        }

                    }

 

                    OrganizationUnits.Items.Add(item);

 

                    this.OrganizationalHiearchy(result.GetDirectoryEntry(), item);

                }

            }

        }

        #endregion

 

        #region Methods

        /// <summary>

        /// This method retrieves the first level of Organizational Units in the Active Directory.

        /// </summary>

        /// <returns>A SearchResultCollection of the first level oranizational units in the directory.</returns>

        public static SearchResultCollection GetTopOUs()

        {

            string searchFilter = "(objectcategory=organizationalunit)";

 

            SearchResultCollection results = FindObjects(searchFilter, null, SearchScope.OneLevel);

 

            return results;

        }

 

        /// <summary>

        /// This method retrieves the first level down from the passed in Directory Entry.

        /// </summary>

        /// <param name="searchRoot">The parent DirectoryEntry object</param>

        /// <returns>A SearchResultCollection of the single level organizational units in the directory.</returns>

        public static SearchResultCollection GetSingleLevelSubOU(DirectoryEntry searchRoot)

        {

            string searchFilter = "(objectcategory=organizationalunit)";

 

            SearchResultCollection results = FindObjects(searchFilter, searchRoot, SearchScope.OneLevel);

 

            return results;

        }

 

        /// <summary>

        /// This method retrieves the all Organizational Units below the seach root in the Active Directory.

        /// </summary>

        /// <param name="searchRoot">The DirectoryEntry the corresponds to the root of the search.</param>

        /// <returns>A SearchResultCollection of oranizational units in the directory.</returns>

        public static SearchResultCollection GetSubOUs(DirectoryEntry searchRoot)

        {

            string searchFilter = "(objectcategory=organizationalunit)";

 

            SearchResultCollection results = FindObjects(searchFilter, searchRoot, SearchScope.Subtree);

 

            return results;

        }

        /// <summary>

        /// This method returns the SearchResultCollection of objects for the given LDAP query filter, initial

        /// search root and scope for the search.  It will return an empty collection if no match is found.

        /// </summary>

        /// <param name="filter">The LDAP query filter.</param>

        /// <param name="searchRoot">The DirectoryEntry that represent s the root of the search.</param>

        /// <param name="scope">The scope for the search (i.e. Base, OneLevel, Subtree)</param>

        /// <returns>The collection of SearchReults that matched the query.</returns>

        public static SearchResultCollection FindObjects(string filter, DirectoryEntry searchRoot, SearchScope scope)

        {

            DirectoryEntry entry = null;

            DirectorySearcher searcher = null;

 

            if (filter == null)

            {

                throw new ArgumentNullException("filter", "The search filter cannot be null.");

            }

 

            if (searchRoot == null)

            {

                entry = GetDirectoryEntry();

                searcher = new DirectorySearcher(entry);

            }

            else

            {

                searcher = new DirectorySearcher(searchRoot);

            }

 

            searcher.Filter = filter;

            searcher.SearchScope = scope;

 

            SearchResultCollection results = searcher.FindAll();

 

            return results;

        }

        /// <summary>

        /// This method retreives a new directory entry object.

        /// </summary>

        /// <returns>A DirectoryEntry object.</returns>

        public static DirectoryEntry GetDirectoryEntry()

        {

            DirectoryEntry entry;

 

            if (preferredServer != null)

            {

                entry = new DirectoryEntry("LDAP://" + preferredServer, appAccountId, appAccountPwd, AuthenticationTypes.ServerBind | AuthenticationTypes.Secure);

            }

            else

            {

                entry = new DirectoryEntry("LDAP://" + GetDomainName(), null, null, AuthenticationTypes.Secure);

            }

            return entry;

        }

        /// <summary>

        /// This method returns the simple Domain Name of the AD Domain

        /// </summary>

        /// <returns>The first value of the DC=* domain name from the default naming context.</returns>

        public static string GetDomainName()

        {

            DirectoryEntry rootDse = new DirectoryEntry("LDAP://rootDSE");

 

            //Get the defaultNamingContext and parse in into a properly formatted string

            //to use for binding with the global catalog

            string ldapDomain = rootDse.Properties["defaultNamingContext"][0].ToString();

 

            rootDse.Close();

            rootDse.Dispose();

 

            return ConvertDNToUPNSuffix(ldapDomain);

        }

        #endregion

 

        #region Helper Methods

        /// <summary>

        /// Takes a DN-formatted domain name and returns a domai name in UPN-suffix format

        /// For example: DC=ads,DC=uscg,DC=mil --> ads.uscg.mil

        /// </summary>

        /// <param name="domainDN">The raw distinguished name for a domain.</param>

        /// <returns>UPN suffix.</returns>

        private static string ConvertDNToUPNSuffix(string domainDN)

        {

            string delimStr = "=,";

            char[] delimiter = delimStr.ToCharArray();

            string[] split = null;

 

            split = domainDN.Split(delimiter);

            StringBuilder buf = new StringBuilder();

            foreach (string str in split)

            {

                if (str.Equals("DC"))

                {

                    continue;

                }

                else

                {

                    buf.Append(str);

                    buf.Append(".");

                }

            }

 

            buf.Remove(buf.Length - 1, 1);

            return buf.ToString();

        }

 

        private void OrganizationalHiearchy(DirectoryEntry de, TreeViewItem item)

        {

            using (SearchResultCollection children = GetSingleLevelSubOU(de))

            {

                foreach (SearchResult child in children)

                {

                    if (child.Properties.Contains("Name"))

                    {

                        foreach (string name in child.Properties["Name"])

                        {

                            TreeViewItem tvChild = new TreeViewItem();

 

                            tvChild.Tag = name;

                            tvChild.Header = name;

 

                            item.Items.Add(tvChild);

 

                            OrganizationalHiearchy(child.GetDirectoryEntry(), tvChild);

                        }

                    }

                }

            }

 

        }

        #endregion

    }

}

I know the code above is hard to read.  Just copy and paste it into Visual Studio.  It uses a recursive method to put the elements in the right order.  So how it works is as follows.  The constructor puts the first level OU from the RootDSE in the treeview.  Then for each of the OU's returned, it goes into a recursive method that drills down until there are no children OU's left.  Then it comes up and continues to the next.  Here is what the final product will look like.

image

Happy Coding!

Free CSS Web Templates

If you are graphically challenged like I am, check out this site.  You can browse for a look and feel that suites your need.  Then download the template and apply to your website.  The templates are pure html and css and they are free.

SSIS Performance

I am a .NET developer who likes to write code in C#.  When given the task to transform customers data from an Oracle 8i database to SQL Server 2005 Database, I immediately though of SSIS.   Well what I didn't know was the learning curve needed to write efficient packages.

Originally, I designed a package that relied heavily on scripts.  In SSIS, scripts can be used to do complex tasks.  For example: splitting out First Name, Middle Name, and Last Name from a single column.  Scripts in SSIS are not necessarily good to use when you are looping a dataset, especially one that is over 34K records.  The time for one control flow to execute on that dataset was over four hours.  Now if that was the only thing I had to worry about, I would have just moved on.  But because I am testing the package with just a subset of the data, that was not going to work.  The entire dataset from production is over 2.2 million records.  Now you can do the math as to how long that would take so, back to the drawing board.

I noticed that the data flow tasks were very efficient, compared to the control flow that contained the scripts.  So I set out to use just a data flow to do the same job.  I was able to convert all my scripts into multiple data flows.  It took some creative techniques in SQL to get a query that could be used to transform the data.  Because SQL is so powerful, I was able to break the data down into sets that could be used. 

For example:  I had a column that was positional as well as informative.  What I mean by this is you have a char(5) column that based on which position contained a number greater than 0 would be your lookup.  So you could have a value of 00100 which would tell me one review for X type was performed.

Select *

From (

         Select c.ID, Substring(review, 3, 1) as MyValue

         From Person AS p Inner Join Case as c ON p.ID = c.pID Inner Join stageTable m ON p.SSN

        ) AS RefTable

Where MyValue > 0

I have five of these queries and five Data Flows that insert my data into the correct format.

Now when I run the packages the execution time over the same dataset is much better.  I went from over four hours to ~8 seconds!  Now that is a performance gain to brag about.

I also recommend the following books in regards to help performance tune your packages.

"Microsoft SQL Server 2005: Integration Services" by Kirk Haselden (Development Manager on the Integration Services team)

"Expert SQL Server 2005 Integration Services" by Brian Knight and Erik Veerman (both SQL Server MVP's)

There is also a great article on the web that also points you in the right direction with regards to performance tuning.

"Microsoft SQL Server 2005 Integration Services: Performance Tuning Techniques" by Elizabeth Vitt

Transaction Around Non-Transaction Pieces

Modern databases deal with transactions seamlessly.  But what do you do when you want an entry added/removed to/from a database table after you perform some task on a non-transactional portion of your system, say Active Directory or even the file system?  Well the answer depends on the order in which you have to do things.  So if you can Insert a record into a database table then create/delete a user from Active Directory then you can use TransactionScope in the .NET Framework 2.0.  On the other hand if you cannot get around the order in which things have to occur, then you have to do your own management of transactions.

Background of TransactionScope Class

The

System.Transactions infrastructure provides both an explicit programming model based on the Transaction class, as well as an implicit programming model using the TransactionScope class, in which transactions are automatically managed by the infrastructure.

NoteImportant:

It is recommended that you create implicit transactions using the TransactionScope class, so that the ambient transaction context is automatically managed for you. You should also use the TransactionScope and DependentTransaction class for applications that require the use of the same transaction across multiple function calls or multiple thread calls. For more information on this model, see the Implement Implicit Transactions using Transaction Scope topic. For more information on writing a transactional application, see Writing A Transactional Application.

Upon instantiating a TransactionScope by the new statement, the transaction manager determines which transaction to participate in. Once determined, the scope always participates in that transaction. The decision is based on two factors: whether an ambient transaction is present and the value of the TransactionScopeOption parameter in the constructor. The ambient transaction is the transaction your code executes in. You can obtain a reference to the ambient transaction by calling the static Current property of the Transaction class. For more information on how this parameter is used, please see the "Transaction Flow Management" section of the Implement Implicit Transactions using Transaction Scope topic.

If no exception occurs within the transaction scope (that is, between the initialization of the TransactionScope object and the calling of its Dispose method), then the transaction in which the scope participates is allowed to proceed. If an exception does occur within the transaction scope, the transaction in which it participates will be rolled back.

When your application completes all work it wants to perform in a transaction, you should call the Complete method only once to inform that transaction manager that it is acceptable to commit the transaction. Failing to call this method aborts the transaction.

A call to the Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction.

If you modify the value of Current inside a scope, an exception is thrown when Dispose is called. However, at the end of the scope, the previous value is restored. In addition, if you call Dispose on Current inside a transaction scope that created the transaction, the transaction aborts at the end of the scope.

Code Example

using (TransactionScope ts = new TransactionScope())
{
     ApplicationRoleDao.RevokeUserAccess(applicationRoleId, userId);

     // Remove the user from the AD group
     System.DirectoryServices.DirectoryEntry deGroups = Account.GetDirectoryEntry(pathGroups);
     System.DirectoryServices.DirectoryEntry deUsers = Account.GetDirectoryEntry(pathUsers);
     string groupName = ApplicationRoleDao.GetActiveDirectoryGroup(applicationRoleId);
     if (!string.IsNullOrEmpty(groupName))
     {
          Account.RemoveUserFromGroup(deGroups, Account.GetUserFromDirectory(deUsers, userId), groupName);
     }

     ts.Complete();
}

So the code above will first remove the user from the database.  Then it will attempt to remove the user from Active Directory.  If either action throws an exception, the transaction in the database will roll back and the user will still be in the system.

More Posts Next page »
Page view tracker