Wednesday, March 7, 2012

NancyFx returns 404 for default route

NancyFx returns 404 for default route

I ran into an issue doing some preliminary coding using NancyFx.  I kept getting an 404 to the point where it seemed like the green guy was mocking me.  My code seemed simple:


   1:  public classMainModule : NancyModule
   2:      {
   3:          public MainModule()
   4:          {
   5:              Get["/"] = x =>;
   6:                             {
   7:                                 return View["home"];
   8:                             };
   9:          }
  10:      }


I tried adding a default override to my class (: base {"/"}), but I kept seeing this.
 

I checked stack exchange and read the documentation to no avail. I don't remember what made me think to try, but i decided to change my site to use IIS Express instead of the built-in web server and that fixed it.


Thursday, June 30, 2011

Uploading Files to Network File Share from ASP.net

There are a few steps you have to take if you are storing images on a network share instead of the local drive and your site is using Windows Authentication.

Web Config

    <authentication mode="Windows" />
    <identity impersonate="true" userName="domain\user" password="password" />
    <authorization>
      <deny users="?" />
    </authorization>

Image/File Share

Add modify permissions on physical path where images will be stored for the domain user specified in the web.config.

Site Folder

Add modify permissions on folder where site is published, i.e.: C:\inetpub\wwwroot\site folder for the domain user.

Temporary ASP.Folder

Add modify permissions on {SYSTEM}\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files for the domain user.

Monday, April 18, 2011

Get those images out of your database

There is more than one way to store images that your web site will display.  The project I am working on is to change the current logic that stores the image as a blob in our database.  I looked at SQL Server 2008’s filestream datatype, but decided against using it because one of the new requirements was to be able to move the images to a new server in the near future.  And we also wanted to be able to reference the images directly to facilitate browser caching and concurrent requests to the images.  This blog shows the solution I used.

The first thing is what exactly to store in the database.  My initial thought was to store a URL relative to the virtual folder as the path, but decided to just store the image name.  My reasoning here is that the directory structure could change and we wouldn’t have to back and update the database.  I am also storing the original file name in the database and the mime type.

So then, how do you name the new image?  Use Path.GetRandomFileName()?  Or use DateTime.Now.Ticks?  Or use a Guid?  What I ended up doing is simply using the file’s MD5 hash.  This has the advantage of either being able to limit the user’s ability to upload the exact image more than once, or you can allow the upload, but you don’t save the duplicated file.  Both database records point to the same location, thus saving space on your image server.

   1: var ext = new FileInfo(uplaodedName).Extension;

   2: // use md5 hash as file name to prevent duplicate images being uploaded

   3: var md5 = MD5.Create();

   4: var hash = md5.ComputeHash(imageBytes);

   5: // user token encode to +, / and = characters 

   6: // are substituted for other tokens to make URL safe

   7: var fileName = HttpServerUtility.UrlTokenEncode(hash) + ext;

   8:  

   9: var relPath = string.Format("{0}/{1}/{2}/", DateTime.Now.Year, 

  10:     DateTime.Now.Month, userName);

  11: var fullPath = Path.Combine(basePath, relPath, fileName);

  12:  

  13: if (_directory.Exists(fullPath))

  14: {

  15:     var fi = new FileInfo(fullPath);

  16:     // make sure folder exists

  17:     if (!_directory.Exists(fi.Directory.FullName))

  18:     {

  19:         _directory.CreateDirectory(fi.Directory.FullName);

  20:     }

  21:     _file.WriteAllBytes(fullPath, imageBytes); 

  22: }

  23:  

  24: myImage.Save(fileName);

  25:  


Wednesday, October 13, 2010

MVC - Validating Required Optional Inputs

In MVC, common validation can be done with a trivial amount of code using attributes on your model views, but once you step outside of the simple required, length, and type validations things can get complicated in a hurry. 


Our client needed a view that required the user to provide additonal input if a question was answered as yes.  Providing this functionality required more than a simple [Required] attirbute on the display model.


There are multiple ways this can be done; this post will provide a quick tutorial on how we went about implementing a solution.


We had already chosen to use the JQuery validation library provided by the MVC2 template.


The first step is to create an attribute to use to decorate the model view:




1: [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
2: public sealed class YesNoControlAttribute : Attribute
3: {
4: private static string defaultTemplateName;
5: public static string DefaultTemplateName
6: {
7: get
8: {
9: if (string.IsNullOrEmpty(defaultTemplateName))
10: {
11: defaultTemplateName = "YesNoControl";
12: }
13:
14: return defaultTemplateName;
15: }
16: set
17: {
18: defaultTemplateName = value;
19: }
20: }
21: public string TemplateName { get; private set; }
22: public string[] HiddenControls { get; private set; }
23: public IDictionary<string, object> HtmlAttributes { get; private set; }
24: public string ControlName { get; set; }
25:
26: public YesNoControlAttribute(object hiddenControls, string controlName)
27: : this(DefaultTemplateName, hiddenControls, controlName, null)
28: {
29: }
30:
31: public YesNoControlAttribute(object hiddenControls, string controlName, object htmlAttributes)
32: : this(DefaultTemplateName, hiddenControls, controlName, htmlAttributes)
33: {
34: }
35:
36: public YesNoControlAttribute(string templateName, object hiddenControls, string controlName, object htmlAttributes)
37: {
38: if (string.IsNullOrEmpty(templateName))
39: {
40: throw new ArgumentException("Template name cannot be empty.");
41: }
42:
43: if (string.IsNullOrEmpty(controlName))
44: {
45: throw new ArgumentException("Control field cannot be empty.");
46: }
47:
48: if (((string[])hiddenControls).Count() < 1)
49: {
50: throw new ArgumentException("Hidden Controls must contain at least one control name.");
51: }
52:
53: TemplateName = templateName;
54: ControlName = controlName;
55: HiddenControls = (string[])hiddenControls;
56: HtmlAttributes = new RouteValueDictionary(htmlAttributes);
57: }
58:
59: }


This class provides an attribute that allows you to decorate a property and add the name of the controls that will be displayed and validated as a required field if the use answers as yes.


A sample decoration is as follows:




1: [Required()]
2: [Display(Name = "Have you ever applied to any other certification program?")]
3: [YesNoControl(new string[2] { "AppliedDate", "AppliedTo" }, "HasAppliedToOtherProgram")]
4: public bool HasAppliedToOtherProgram { get; set; }
5: [Required(ErrorMessage = "Applied Date is required.")]
6: public DateTime AppliedDate { get; set; }
7: [Required(ErrorMessage = "The program you applied to is required.")]
8: public string AppliedTo { get; set; }


The model creates three properties, the control must have at least one other control that will be validated, but can display any additonal number of controls. 




1: <div>
2: <%= Html.LabelFor(m => m.IsMVCBetterThanWebForms)%>
3: <span style="display:block;">
4: <%= Html.RadioButton("IsMVCBetterThanWebForms", "true", Model != null ? Model.IsMVCBetterThanWebForms : false, new { @onclick = "$('#IsMVCBetterThanWebForms_span').show()" })%><label for="Yes" style="width:20px;display:inline">Yes</label><br />
5: <span id="IsMVCBetterThanWebForms_span" style="display: <%= Model != null && Model.IsMVCBetterThanWebForms ? "normal" : "none" %>; width:800px; position:relative; left:25px">
6: Date:<%= Html.EditorFor(m => m.Date) %><%= Html.ValidationMessageFor(m => m.Date)%>
7: and Why: <%= Html.EditorFor(m => m.Why) %><%= Html.ValidationMessageFor(m => m.Why)%>
8: </span>
9: </span>
10: <%= Html.RadioButton("IsMVCBetterThanWebForms", "false", Model != null ? !Model.IsMVCBetterThanWebForms : false, new { @onclick = "$('#IsMVCBetterThanWebForms_span').hide()" })%><label for="No" style="width:20px;display:inline-block;">No</label><br />
11: <%= Html.ValidationMessageFor(m => m.IsMVCBetterThanWebForms)%>
12: </div>


Then in your view you add markup that displays a radio button for the yes/no answer.  This markup adds the radiobutton that will be used as the


Monday, August 17, 2009

IE Tab in Chrome (for SharePoint usage)

I really like using Google Chrome as my default browser, but need to be able to use SharePoint and some of the advanced functionality, mainly checking out documents, doesn’t work so well. I found some posts that let me achieve the same functionality as FireFox’s IE Tab plug-in.

CheckOut

First you have to download and install the MeadCo Neptune IE emulator, http://www.meadco.com/neptune/index.htm.

Then create a bookmark in Chrome:

image

Call it Switch to IE or something and in the URL paste the following JavaScript:

javascript:(function (){var lh=location.href; if( !lh || lh.match(/^(javascript|about):/i) )return; document.write('<html><head><title>'+(document.title?document.title:lh).replace(/</g,'<').replace(/>/g,'>')+' - using Internet Explorer rendering<\/title><\/head><body style=\'margin:0px;padding:0px;\'><script type=\'text/javascript\'>var script = document.createElement(\'script\'); var embed = \'<embed type=\\\'application\/x-meadco-neptune-ax\\\' width=\\\'100\x25\\\' height=\\\'100\x25\\\' param-location=\\\'' + lh + '\\\'><\/embed>\'; script.setAttribute(\'src\',\'data:text/javascript,document.write(embed);\'); document.body.appendChild(script);<\/script><\/body><\/html>'); })();



Now when you open a URL in a new tab, if you need IE functionality, just click the Switch to IE bookmark, and you are running IE in Chrome and you can now checkout SharePoint documents.

Thursday, August 6, 2009

C# and QBXML SDK

I am finishing up a project that syncs an order system
using SQL Server to the invoice system in Quickbooks 2007. Most of this information is available in various locations on the internet, but it took me way longer to finish this project than I thought it should because of all the small issues I ran
into along the way. So I am posting this in case some one else runs into this same issue.


If you have already worked with the QBXML SDK, you already know that Quickbooks documentation is severely lacking in samples, and even general documentation on the layout of the XML requests leaves the programmer without
a lot to work with. Maybe this post will shed some light on some questions for you.

The Project

The requirements were pretty simple:
  1. Retrieve invoices from SQL Server
  2. Check if company exists in Quickbooks, if not create company
  3. Check if invoice exists in Quickbooks, if not create, if it does throw duplicate exception
  4. Update invoices in SQL Server so they are not added again
  5. Create report of successfully imported invoices, duplicate invoices and invoice that could not be imported

The Issues
  1. The 'Quickbooks found an error parsing the XML text stream' error.
  2. The qbxml version of the request
This generic error message gave me fits through the development cycle. I couldn't find anything in the returned error that gave me a line number, a node that was incorrect for some reason, or any other information.

The XML Validator that is provided with the SDK did provide some help, it is definitely worth using. The problem that kept causing me issues was that the nodes have to be in a specific order (and I didn't find this piece of information until I was almost finished with the project) or you will get the parsing error, but I was unable to find any documentation that listed all of the available nodes and the specific order. I used trial and error to determine the correct order of the nodes I needed to send to Quickbooks.

The qbxml version processing instruction tripped me up a few times, but the error message it returned was specific enough that I could fix fairly easily.

The Code
I used the XML.Linq.XDocument to create my basic request and added nodes as needed when adding a customer or invoice:

  • Customer Add
   XDocument xdoc = new XDocument(
new XDeclaration("1.0", null, null),
new XProcessingInstruction("qbxml", "version=\"2.0\""),
new XElement("QBXML",
new XElement("QBXMLMsgsRq",
new XAttribute("onError", "stopOnError"),
new XElement("CustomerAddRq",
new XAttribute("requestID", "1"),
new XElement("CustomerAdd",
new XElement("Name", agency.Name),
new XElement("CompanyName", agency.Name)
)
)
)
)
);
  • Invoice Add
     XDocument xdoc = new XDocument(
new XDeclaration("1.0", null, null),
new XProcessingInstruction("qbxml", "version=\"2.0\""),
new XElement("QBXML",
new XElement("QBXMLMsgsRq",
new XAttribute("onError", "stopOnError"),
new XElement("InvoiceAddRq",
new XAttribute("requestID", "99"),
new XElement("InvoiceAdd",
new XElement("CustomerRef",
new XElement("FullName", invoice.AgencyName)
)
)
)
)
)
);
The XML to check if customers or invoices existed didn't need to be modified after
the fact so I created it inline:

var reqXML = string.Format("<?xml version="1.0"?><?qbxml version="6.0"?><?qbxml version="2.0"?><QBXML><QBXMLMsgsRq onError="stopOnError"> <customerqueryrq requestid="\"><fullname>{0}</fullname></customerqueryrq></qbxmlmsgsrq></qbxml>", customerName);

var reqXML = string.Format("<?xml version="1.0"?><?qbxml version="6.0"?><QBXML><QBXMLMsgsRq onError="stopOnError"><invoicequeryrq requestid="\">";

reqXML += string.Format("<txndaterangefilter><fromtxndate>{0}</fromtxndate><totxndate>{0}</totxndate></txndaterangefilter><entityfilter><fullname>{1}</fullname></entityfilter></invoicequeryrq></qbxmlmsgsrq></qbxml>",invoiceDate,agencyName);

My QBXML Requests
  • Customer Check

<?xml version="1.0"?>
<?
qbxml version="6.0"?>
<
QBXML>
<
QBXMLMsgsRq onError="stopOnError">
<
customerqueryrq requestid="\">
<
fullname>Company Name</fullname>
</
customerqueryrq>
</
qbxmlmsgsrq>
</
qbxml>

  • Add Customer

<?xml version="1.0"?>
<?qbxml version="2.0"?>
<
QBXML>
<QBXMLMsgsRq onError="stopOnError">
<CustomerAddRq requestID="1"><CustomerAdd>
<
Name>Company Name</Name>
<
CompanyName>Company Name</CompanyName>
<
BillAddress>
<
Addr1>1200 Street Lane</Addr1>
<
City>Dallas</City>
<
State>TX</State>
<
PostalCode>75230</PostalCode>
</
BillAddress>
<
Phone>555-555-1212</Phone>
</
CustomerAdd>
</
CustomerAddRq>
</
QBXMLMsgsRq>
</
QBXML>

  • Invoice Check

<?xml version="1.0"?>
<?
qbxml version="6.0"?>
<
QBXML>
<
QBXMLMsgsRq onError="stopOnError">
<
invoicequeryrq requestid="\">
<
txndaterangefilter>
<
fromtxndate>2009-07-01</fromtxndate>
<
totxndate>2009-07-01</totxndate>
</
txndaterangefilter>
<
entityfilter>
<
fullname>Company Name</fullname>
</
entityfilter>
</
invoicequeryrq>
</
qbxmlmsgsrq>
</
qbxml>

  • Add Invoice

<?xml version="1.0"?>
<?qbxml version="2.0"?>
<
QBXML>
<QBXMLMsgsRq onError="stopOnError">
<InvoiceAddRq requestID="99">
<InvoiceAdd>
<CustomerRef>
<FullName>Home Health Agency 139 - test6</FullName>
</CustomerRef>
<TxnDate>2008-12-31</TxnDate>
<CustomerMsgRef>
<FullName>Thank you for your business!</FullName>
</CustomerMsgRef>
<InvoiceLineAdd>
<ItemRef>
<FullName>Daily Visit PT</FullName>
</ItemRef>
<Desc>
Physical Therapist Daily Visit
Ada Feelbad (Fort Worth) 12/23/08
Betty Feelbad (Fort Worth) 12/06/08 12/15/08 12/17/08 12/21/08 12/22/08
Harold Feelbad (Fort Worth) 12/06/08 12/23/08

</Desc>
<Quantity>8</Quantity>
<Rate>102.00</Rate>
<Amount>816.00</Amount>
</InvoiceLineAdd>
<InvoiceLineAdd>
<ItemRef>
<FullName>Eval PT</FullName>
</ItemRef>
<Desc>
Physical Therapist Evaluation
Ada Feelbad (Fort Worth) 12/15/08
Harold Feelbad (Fort Worth) 12/20/08
</Desc>

<Quantity>2</Quantity>
<Rate>100.00</Rate>
<Amount>200.00</Amount>
</InvoiceLineAdd>
<InvoiceLineAdd>
<ItemRef>
<FullName>Missed PT Visit </FullName>
</ItemRef>
<Desc>
Missed Visit Physical Therapist
Ada Feelbad (Fort Worth) 12/24/08

</Desc>
<Quantity>1</Quantity>
<Rate>0.00</Rate>
<Amount>0.00</Amount>
</InvoiceLineAdd>
</InvoiceAdd>
</InvoiceAddRq>
</QBXMLMsgsRq></QBXML>

Hopefully this will help save you some time using the QBXML SDK in Visual Studio.