Jeff Widmer's Blog

ASP.NET, ASP.NET MVC, C#, VB.NET, IIS7, Windows Forms, VB6, ASP 3.0

  • OneNote Feature I would love to see: Ctrl-Tab

    I use Ctrl-Tab all the time in Visual Studio and SQL Server Management Studio to switch between the current file and the previous file (and then quickly back again). 

    I would love to see OneNote enable Ctrl-Tab to switch between the last OneNote page and current OneNote page.  Currently Ctrl-Tab moves from Section to Section in your NoteBook (and hitting Ctrl-Tab and then releasing all keyboard buttons and then hitting Ctrl-Tab again just moves to the next section in your NoteBook when it really feels like it should move back to the previous section).  You can get the Visual Studio Ctrl-Tab experience by using the Alt-Left Arrow and Alt-Right Arrow keyboard shortcut but this is the same shortcut as the browser back and forward buttons and is just not natural to the design of OneNote.  (I do not think of each page in OneNote as a web page.)

    I use Ctrl-Tab so much in Visual Studio and other applications and it would be really nice to have it working in OneNote.

    Here is the Microsoft Connect feature request that I submitted: https://connect.microsoft.com/onenote/feedback/details/535310/ctrl-tab-works-just-like-visual-studio



  • Where is the QuickBooks SDK?

    This post is for all of those developers out there that are trying to find the latest version of the QuickBooks SDK on developer.intuit.com.   In the past you were able to log into the Intuit Developer Network MyIDN members site and then download the QuickBooks SDK from there.  But currently, when you log into the MyIDN members section of the Intuit Developer Network site you get this:

    image

    There is no menu or any other information to let you browse around the MyIDN Members site.  If you look at the page source everything is there but it is just not rendering in a browser. 

    So for anyone that needs to download the QuickBooks SDK here is how I did it:

    From the source of the MyIDN home page (the one pictured above) I found the url to the Technical Resources page:

    image

    https://member.developer.intuit.com/MyIDN/technical_resources/default.aspx?id=1952

    This is probably the most important page since it will let you get to all of the SDKs and the other Technical Resources.

    image

    Second down on this page is the Intuit QuickBooks Software Development Kit (QB SDK) which will lead you to the QuickBooks SDK Information page:

    image

    Clicking on the Download link at the bottom will get you to where you can download the QuickBooks SDK Version 8.0 (or the previous versions too).

    image

    And finally for those who just want the QuickBooks SDK Installer here is the direct link:

    QuickBooks SDK 8.0 Installer (self-extracting installer, approximately 95.9 MB)

     



  • How to change SQL Server login default database through SQL Script

    I am moving a SQL Server database from one drive to another by detaching and then reattaching.  I detached the database, moved the mdf and ldf files, and then went to attach it and was presented with this dialog:

    image

    TITLE: Microsoft SQL Server Management Studio
    Cannot show requested dialog.

    ADDITIONAL INFORMATION:

    Parameter name: nColIndex
    Actual value was -1. (Microsoft.SqlServer.GridControl)

    This is because my login had the default database set to the database that I just detached.  This causes all sorts of errors with SQL Server Management Studio but none of them are particularly helpful, they pretty much just keep telling you “access denied” but not why or what to do.

    This is the dialog you would get if you try to click on the properties for your login:

    image

    TITLE: Microsoft SQL Server Management Studio
    Cannot show requested dialog.

    ADDITIONAL INFORMATION:
    Cannot show requested dialog. (SqlMgmt)
    Failed to retrieve data for this request. (Microsoft.SqlServer.Management.Sdk.Sfc)
    For help, click:
    http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&LinkId=20476

    Failed to connect to server. (Microsoft.SqlServer.ConnectionInfo)

    Cannot open user default database. Login failed.
    Login failed for user 'login'. (Microsoft SQL Server, Error: 4064)
    For help, click:
    http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&EvtSrc=MSSQLServer&EvtID=4064&LinkId=20476

    And then even if you close and open SQL Server Management Studio you will get this dialog:

    image

    TITLE: Microsoft SQL Server Management Studio
    Failed to connect to server. (Microsoft.SqlServer.ConnectionInfo)

    ADDITIONAL INFORMATION:
    Cannot open user default database. Login failed.
    Login failed for user 'login'. (Microsoft SQL Server, Error: 4064)
    For help, click:
    http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&EvtSrc=MSSQLServer&EvtID=4064&LinkId=20476

    What you can do to fix this is to change your login’s default database through SQL Script:

    ALTER LOGIN  [DOMAIN\login]
    WITH DEFAULT_DATABASE = master

    This will set the default database to your master database and then you will be able to log in and continue from there.



  • Format the email subject in the Elmah Error Logging Module

    Elmah error logging modules and handlers for ASP.NET is a great module for logging asp.net errors to many different and configurable repositories.  One of the repositories that Elmah works with is email. You can easily set up Elmah to send emails by changing the elmah configuration section in your web.config.

    You can find a sample of all the different elmah web.config settings here.  The email configuration settings are the following:

    <errorMail 
      from="elmah@example.com" 
      to="admin@example.com" 
      subject="..."
      async="true|false"
      smtpPort="25"
      smtpServer="smtp.example.com" 
      userName="johndoe"
      password="secret" 
      noYsod="true|false" />

    Only the from and to settings are required. If you leave off the subject attribute you will get the default subject line which is something like this:

    Error (System.Exception): This is a test exception

    Which is made up of the type of exception followed by the exception message:

    Error (Exception.Type): Exception.Message

    But did you know you can configure the subject line with these pieces of information and also add something more specific for the application you are working on?  For example, I always like the Exception Message in my error email prefixed by the application name and the environment.  For example:

    My Web Application (STAGING): This is a test exception

    You can do that by specifying the String.Format parameters in the configuration section subject attribute like this:

    <errorMail 
      from="elmah@example.com" 
      to="admin@example.com" 
      subject="My Web Application (STAGING): {0}"
      />

    Now the Exception Message will replace the {0} in the email subject and you can more easily filter the emails that appear in your inbox (hopefully though there will not be so many).  You can also include the Exception Type by adding {1} to the subject anywhere you want.

    Here is the line of code from the Elmah project:

    mail.Subject = string.Format(subjectFormat, error.Message, error.Type).Replace('\r', ' ').Replace('\n', ' ');

    Technorati Tags:



  • WebConfigurationManager.OpenWebConfiguration() - Failed to resolve the site ID

    I am writing a small administrative tool to read configuration settings from web.config files for various websites that are running on the same web server as the tool.  Initially I thought the method WebConfigurationManager.OpenWebConfiguration(), and the various overloads, would have been able to help me out.  The method signature is exactly what I am trying to do:

    image

    The WebConfigurationManager class (System.Web.Configuration.WebConfigurationManager) has a method named OpenWebConfiguration which can open a configuration file located at a specific virtual path and for a specified IIS site. 

    image

    This sounded perfect and exactly what I was trying to do until I ran into this exception when I first tried it:

    Failed to resolve the site ID for 'otherwebsite.local'.

    After digging around a bunch and confirming I had the parameters spelled correctly I finally found this post: Thread: Unable to open web configuration on non default websites.

    It turns out that because the adminwebsite.local is in an application pool named adminwebsite.local and otherwebsite.local is in a different application pool named otherwebsite.local, they can’t talk to each other.  To prove this, I moved otherwebsite.local into the adminwebsite.local application pool and the OpenWebConfiguration method worked perfectly.

    So if the two websites could live in the same application pool then the WebConfigurationManager.OpenWebConfiguration() method would have worked great for the tool I am writing. 

    But unfortunately this is not how the web server is setup and not the recommended best practice (to have all websites in the same application pool).  So I switched to a more brute force method of reading and parsing the config file as an XML document (using a Dataset object to do the heavy lifting):

    DataSet ds = new DataSet();
    ds.ReadXml(path +
    \\web.config);
    DataTable dt = ds.Tables["appSettings"]; 
    DataRow[] appSettings= dt.Rows[0].GetChildRows("appSettings_add"); 
    foreach (DataRow appSetting in appSettings)
    {
        String str = Convert.ToString(appSetting["key"]);
        //Do other work here....
    }
    ds.Dispose();



  • Linq2Sql: How to join tables on more than one column

    You can join two tables in Linq2Sql by using an anonymous type to specify the join. 

    var r =
        from o in db.Orders
        join p in db.Products on o.ProductId equals p.ProductId
        join pu in db.ProductUsers on new { p.ProductId, o.UserId } equals new { pu.ProductId, pu.UserId }
        select new {o, p, pu};

    This is equivalent to the following SQL:

    SELECT * FROM Order o
    JOIN Product p ON o.ProductId=p.ProductId
    JOIN ProductUser pu ON p.ProductId=pu.ProductId AND o.UserId=pu.UserId

    The anonymous type { p.ProductId, o.UserId } is lined up with the second anonymous type { pu.ProductId, pu.UserId } to create the join on both columns.

    But what if you are trying to join two tables where the column names do not match?  For instance this SQL:

    SELECT * FROM Order o
    JOIN Product p ON o.ProductId=p.ProductId
    JOIN ProductUser pu ON p.ProductId=pu.ProductId AND o.UserId=pu.CreatedByUserId

    You would think you could create two anonymous types just like above and line up the properties like this:
    First anonymous type: { p.ProductId, o.UserId } 
    Second anonymous type: { pu.ProductId, pu.CreatedByUserId }

    But if you try that you will get a compilation error:

    The type of one of the expressions in the join clause is incorrect.  Type inference failed in the call to 'Join'.

    What is going on here?  Both o.UserId and pu.CreatedByUserId have the same data type so why is there a compilation error? 

    Because Linq2Sql lines up the two anonymous types by name and with the different names it cannot resolve the two column join.  Both the order and the name need to match for this to work.  To resolve this just specify an alias property name for one of the anonymous types that do not match like so:
    First anonymous type: { p.ProductId, CreatedByUserId = o.UserId }
    Second anonymous type: { pu.ProductId, pu.CreatedByUserId }

    Now Linq2Sql knows how to completely match up the two column join.  Here is the full Linq2Sql statement for a join with two columns when the column names do not match:

    var r =
        from o in db.Orders
        join p in db.Products on o.ProductId equals p.ProductId
        join pu in db.ProductUsers on new { p.ProductId, CreatedByUserId = o.UserId } equals new { pu.ProductId, pu.CreatedByUserId}
        select new {o, p, pu};

    Technorati Tags:


  • How to 301 Permanent Redirect in ASP.NET

    In the next release of the .NET Framework (.NET Framework 4.0) there is a new response method for permanently redirecting a request: Response.RedirectPermanent.  You can see the Beta MSDN documentation for Response.RedirectPermanent here.  This will automatically issue the 301 moved permanently status code and redirect to the target page.  A permanent redirect status code tells a search engine to update their cache and reassign the old url to the new url.

    But if you need to do this now (prior to .NET Framework 4.0) you will need to do it the manual way.  To do so you will need to manually add the status and location headers to the response. 

    The pre .NET Framework 4.0 versions are:

      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", "/");

      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", "/");
      Response.End();

    There is also a RedirectToRoutePermanent method which will allow you to redirect to a new url using route parameters (and sends the 301 Moved Permanently status code). 

    UPDATE: See RichardD's comment below for a different method.  I have not tried it that way but I will next time because it certainly does look much cleaner.

    Technorati Tags: ,


  • How to get an indexed item of an IEnumerable object (Linq)

    If you have an IEnumerable<T> collection of some objects T and you need to get a particular item out of that collection, you cannot use the standard indexing that you would normally use with brackets ([index]).  If you try you will get an error such as:

    Cannot apply indexing with [] to an expression of type ‘System.Collections.Generic.IEnumerable<T>

    image

    But there is the extension method ElementAt(index) (in the System.Linq namespace) for IEnumerable<T> that will allow you to get at that particular indexed item:

    MyItem = MyIEnumerableExpression.ElementAt(index);

    From the MSDN Documentation: http://msdn.microsoft.com/en-us/library/bb299233.aspx

    image