tag:blogger.com,1999:blog-71777745763976084542024-03-17T16:57:06.325+04:00viaMacchinathe way of the machineEverett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.comBlogger33125tag:blogger.com,1999:blog-7177774576397608454.post-58113791553380064822017-01-20T10:29:00.000+04:002017-01-20T10:35:03.697+04:00Make your Blogger blog more responsive by auto-magically adding the SRCSET attribute to your images!viaMacchina uses <a href="https://www.blogger.com/" target="_blank">Blogger</a> for its blogging platform. Blogger has been around for a long time, and it continues to be one of the best blogging platforms available. Out of the box, you get a mobile view to your blog. But, to be clear, even though the view is geared towards mobile devices, it is not responsive. Because of this, we are developing a completely responsive template to replace the out of the box Blogger template and mobile view.<br />
<br />
<a name='more'></a><br />
"Responsive" web pages sound cool, but at the end of the day, responsive techniques are really just describing what I consider to be "good" use of HTML and CSS. For instance, when working with images, it is easy to set the images' height and width in pixels. However, doing this will force that image to remain at the specified height and width no matter how the screen or content around that image scales. An easy way to get around this limitation is to set the images' width to 100%, and contain that image in an element that has a constrained height and width.<br />
<br />
Another issue that arises when applying responsive techniques to images is the concept of downloading the correctly sized image for the resolution of the device. If your website is rendering on a mobile device with a tiny screen, what benefit is gained in downloading an image that looks good on a 30" monitor? There have been many attempts at addressing this issue in the past using JavaScript, CSS, and in some cases server side code, but all of the solutions have been clunky, and have come with their own set of shortcomings.<br />
<br />
Enter the <b><a href="http://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset" target="_blank">srcset</a></b> attribute. A part of the ever evolving HTML 5 specification, the srcset attribute has been adopted by most browsers in use today (<a href="http://caniuse.com/#search=srcset" target="_blank">see Can I Use - SRCSET</a>).<br />
<br />
Adding the srcset attribute to an image can instantly make that image act responsively and allow the browser to download the correct resolution for the viewport in use.<br />
<br />
Here's an example using an image hosted by Blogger:<br />
<br />
<div>
<img border="0" src="https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s320/IMGP3792.JPG" srcset="https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s320/IMGP3792.JPG 320w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s640/IMGP3792.JPG 640w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s1200/IMGP3792.JPG 1200w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s1600/IMGP3792.JPG 1600w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s3200/IMGP3792.JPG 3200w" width="100%" /></div>
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f8f8f8; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #204a87; font-weight: bold;"><img</span> <span style="color: #c4a000;">border=</span><span style="color: #4e9a06;">"0"</span> <span style="color: #c4a000;">src=</span><span style="color: #4e9a06;">"https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s320/IMGP3792.JPG"</span> <span style="color: #c4a000;">width=</span><span style="color: #4e9a06;">"100%"</span> <span style="color: #c4a000;">srcset=</span><span style="color: #4e9a06;">"https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s320/IMGP3792.JPG 320w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s640/IMGP3792.JPG 640w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s1200/IMGP3792.JPG 1200w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s1600/IMGP3792.JPG 1600w, https://3.bp.blogspot.com/-6UwIvGZx3YE/WBX_yG8fgQI/AAAAAAAAMZQ/CHD-jCXZOYYzftmYcgtE6DNWf23kVstTACPcB/s3200/IMGP3792.JPG 3200w"</span><span style="color: #204a87; font-weight: bold;">></span>
</pre>
</div>
<br />
The src for the image is set to the smallest resolution available using the Blogger image service. Normally, when using an image hosting service such as Google Photos or Blogger, etc... the host will encourage you to upload a high resolution or original resolution photo. The service can then serve that image at many different smaller resolutions.<br />
<br />
In the above example, we are configuring the images srcset attribute to use the following resolutions:<br />
<br />
<b>320</b><br />
<b>640</b><br />
<b>1200</b><br />
<b>1600</b><br />
<b>3200</b><br />
<br />
This range covers most of the popular resolutions in use today.<br />
<br />
The result of adding the srcset is that the browser will determine the appropriate image to download for the current viewport resolution. This greatly improves performance because the device is determining the correct size of the image that it needs.<br />
<br />
The following video shows the srcset in action. The browser starts constrained to a very small resolution. In the Chrome developers tools, you can see that only the smallest resolution, 320, has been downloaded. As the browser windows size increases, larger images defined in the srcset are downloaded.<br />
<br />
<div class="embed-container">
<iframe allowfullscreen="" frameborder="0" src="https://www.youtube.com/embed//XmmR2nO8UFw"></iframe><br /></div>
<br />
If you are using Blogger, and you're comfortable modifying your blog template HTML, you can quickly add this behavior to all of the images in your blog posts using the following jQuery:<br />
<br />
<script async="" src="//jsfiddle.net/everettcomstock/uh6ye9bL/embed/js,html,css,result/dark/"></script><br />
<br />
and here is a pure JavaScript variant:<br />
<br />
<script async="" src="//jsfiddle.net/everettcomstock/q4ykasjf/embed/js,html,css,result/dark/"></script><br />
<br />
These functions need to be added to a script tag at the end of the HTML body so that they will execute after the image tags have loaded.<br />
<br />
One important note: If your images have explicit height and width set, or if you have modified the underlying html so that the image src is pointing to a higher resolution file, you may need to manually update the image attributes in order to gain all of the performance benefits that come with the addition of the srcset attribute.<br />
<br />
I hope this helps!<br />
<br />
<br />Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com54tag:blogger.com,1999:blog-7177774576397608454.post-65630528932709990892016-03-13T11:51:00.003+04:002016-03-14T08:25:32.267+04:00Breaking All The Rules with WCF Full Circle<div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/-KtCrOG4MyGw/VuUbdggp6cI/AAAAAAAAASg/Qh2F2eiCFHAbgTY_5oisFbpT25B6lG1dQ/s1600/WcfWsSecurity.jpg" imageanchor="1"><img border="0" src="https://2.bp.blogspot.com/-KtCrOG4MyGw/VuUbdggp6cI/AAAAAAAAASg/Qh2F2eiCFHAbgTY_5oisFbpT25B6lG1dQ/s1280/WcfWsSecurity.jpg" width="100%" /></a></div><br />
Recently while working on a project for a large government enterprise, I encountered a problem similar, nay, exactly the same as Scott Hanselman in his post "<a href="http://www.hanselman.com/blog/BreakingAllTheRulesWithWCF.aspx" target="_blank">Breaking All The Rules with WCF</a>." From the malformed WSDL to a custom WS-Security usernameToken without a password, I was dealing with the same issue.<br />
<br />
Scott, as always, has done an incredible job highlighting the problem, and providing a solution, so I highly encourage you to read his post. I'm writing this post to cover the one thing he missed, creating a service that can be used to test this binding configuration against.<br />
<a name='more'></a><br />
<b>CREATING THE BINDING</b><br />
<br />
Scott was able to create a Custom WCF binding that emits a username without password using the following code:<br />
<br />
<pre class="brush: csharp">WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
//Just the username
oldBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
//And basically nothing else
oldBinding.Security.Message.NegotiateServiceCredential = false;
oldBinding.Security.Message.EstablishSecurityContext = false;
//oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");
//oldBinding.UseDefaultWebProxy = false;
//remove the timestamp
BindingElementCollection elements = oldBinding.CreateBindingElements();
elements.Find<securitybindingelement>().IncludeTimestamp = false;
//sets the content type to application/soap+xml
elements.Find<textmessageencodingbindingelement>().MessageVersion = MessageVersion.Soap12;
CustomBinding newBinding = new CustomBinding(elements);
FooPortTypeClient svc = new FooPortTypeClient(newBinding, new EndpointAddress("https://example.com/foo/v1"));
FooRequest req = new FooRequest();
//...etc...now it's just request and response.
</textmessageencodingbindingelement></securitybindingelement></pre><br />
The same binding can be created in configuration like this:<br />
<br />
<pre class="brush: xml"><bindings>
<customBinding>
<binding name="WSSecurityBinding">
<security defaultAlgorithmSuite="Default"
authenticationMode="UserNameOverTransport"
requireDerivedKeys="true"
includeTimestamp="false"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap12" />
<httpsTransport />
</binding>
</customBinding>
</bindings></pre><br />
This will create the binding, but what about testing it?<br />
<br />
<b>TESTING</b><br />
<br />
If you configure a test client and service with this binding you'll find that as soon as the client calls the service, you'll receive the following exception:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-W6bSrMPIbD8/VuUNSSilaDI/AAAAAAAAASI/Rc3XtBpH-s4L9YaEhVM3bdovXlHAgMS6A/s1600/SecurityTokenException.jpg" imageanchor="1"><img border="0" src="https://1.bp.blogspot.com/-W6bSrMPIbD8/VuUNSSilaDI/AAAAAAAAASI/Rc3XtBpH-s4L9YaEhVM3bdovXlHAgMS6A/s1280/SecurityTokenException.jpg" width="100%" /></a></div><br />
This exception is thrown because the default <b>UserNamePasswordValidator</b> expects both a username and a password... and we just removed the password. However, fixing this issue is easy enough... we can create a custom UserNamePasswordValidator that allows us to validate the request according to our custom business logic.<br />
<br />
<pre class="brush: csharp">public class NullPasswordValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (string.IsNullOrWhiteSpace(userName))
throw new FaultException("The userName cannot be null or whitespace.");
// Allow any password, even null values to pass.
}
}</pre><br />
Configuring the service to use this new validator is as simple as creating a new service behavior configuration and pointing to the new custom validator:<br />
<br />
<pre class="brush: xml"> <serviceBehaviors>
<behavior name="NullPasswordValidator">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="Security.WCF.Validators.NullPasswordValidator, Security" />
</serviceCredentials>
</behavior>
</serviceBehaviors></pre><br />
<b>FULL CIRCLE</b><br />
<br />
After creating and configuring the new UserNamePasswordValidator, our client will be able to communicate with the service. We can open up Fiddler and prove that everything is working correctly by inspecting the SOAP message:<br />
<br />
<pre class="brush: xml"><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="uuid-38dc2ff9-a055-4a66-90e3-3aa0c267c36e-1">
<o:Username>viaMacchina\\dec</o:Username>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
...
</s:Body>
</s:Envelope>
</pre><br />
This brings the custom binding full circle. We're able to create a message request without a password to fulfill the custom WS-Security requirements. We're also able to create a test service that will accept the requests without throwing an exception. Finally, we can use tools like Fiddler to inspect the SOAP messages between the client and service to ensure that the message is formed exactly as it should be.Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com2tag:blogger.com,1999:blog-7177774576397608454.post-38440174733627684742015-12-17T06:39:00.000+04:002015-12-17T16:20:25.224+04:00Open Live Writer is finally here!<div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-uLudOoDIhG8/VnIdPfD9d4I/AAAAAAAAARg/tpJ-OHvu6hE/s1600-h/image%25255B2%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="79" src="https://lh3.googleusercontent.com/-bJTDs5Z8IZI/VnIdQ_MnRZI/AAAAAAAAARk/A_GdMtRwRTY/image_thumb.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="244" /></a></div><br />
Just over a week ago Scott Hanselman <a href="http://www.hanselman.com/blog/AnnouncingOpenLiveWriterAnOpenSourceForkOfWindowsLiveWriter.aspx" target="_blank">dropped the news</a> that an open source version of the popular Windows Live Writer blogging application has finally been released. The Windows Live Writer application has been a popular publishing platform for years, but has not been updated since 2012. Earlier this year Hanselman ignited the communities interest by tweeting that talks were under way to open source the app.<br />
<center><div><blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/pzerger">@pzerger</a> <a href="https://twitter.com/rodtrent">@rodtrent</a> we are going to open-source live writer, so don't worry</p>— Scott Hanselman (@shanselman) <a href="https://twitter.com/shanselman/status/608671203192995843">June 10, 2015</a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script><br />
</div></center>The wait is finally over. You can download it here:<br />
<br />
<a href="http://openlivewriter.org/" title="http://openlivewriter.org/">http://openlivewriter.org/</a><br />
<br />
The application is still “old” compared to 2015 standards… but now that it is open source, I’m confident the community is going to run with it. Just in the past week the authentication for the popular Blogger service has been updated to work with the OAuth 2.0 spec.<br />
<br />
Happy Blogging!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com1tag:blogger.com,1999:blog-7177774576397608454.post-74348342211473028012015-12-07T13:35:00.001+04:002015-12-07T18:16:43.269+04:00Let’s Encrypt!<a href="http://1.bp.blogspot.com/-e2b8tQgiqWk/VmVQNKz5LaI/AAAAAAAAARQ/MXR4GTGUX9w/s1600/LetsEncrypt.jpg" style="clear: left; float: left; margin-bottom: .5em; margin-right: 2em; overflow: hidden;"><img border="0" src="http://1.bp.blogspot.com/-e2b8tQgiqWk/VmVQNKz5LaI/AAAAAAAAARQ/MXR4GTGUX9w/s1600/LetsEncrypt.jpg" width="100%" /></a> Just last week <a href="https://letsencrypt.org/" target="_blank">LetsEncrypt.org</a> entered public beta. This is newsworthy because SSL and TLS security are becoming more and more important for web applications, and the developers that create them. I won’t go into the details of the importance of using SSL and other stronger forms of transport layer security (TLS). There's a plethora of information on the web, as well as my favorite training site, <a href="https://www.pluralsight.com/" target="_blank">Pluralsight.com</a>. But, it suffices to say, if you are creating a web app these days, you need to be using SSL.<br />
<a name='more'></a><br />
LetsEncrypt.org is important because they are a free certificate authority backed by some of the biggest companies in tech such as Facebook, Cisco, and Mozilla. This corporate support is part of a larger movement in the industry to make SSL the default transport on the internet. Amidst enormous data breaches and high-profile website hacks, companies are beginning to realize the fundamental importance of securing the internet.<br />
<br />
If you're running Linux you can get started by downloading the LetsEncrypt client from GitHub here:<br />
<br />
<a href="https://github.com/letsencrypt/letsencrypt" title="https://github.com/letsencrypt/letsencrypt">https://github.com/letsencrypt/letsencrypt</a><br />
<br />
This will give you a command line client that allows you issue, renew, and revoke a certificate. My goal with this post is not to cover how to issue certs, but to raise awareness of the LetsEncrypt.org service, so I’m not going to go into the details of certificate management. But, their website and GitHub pages offer detailed instructions on how to use the client.<br />
<br />
From now on, we have no excuse to not secure our sites! Let’s Encrypt!<br />
<br />
P.S. - A quick note about viaMacchina.com... We recognize the hypocrisy of highlighting a service that enables easy and free SSL certificates when this blog does not, in fact, use SSL. To be clear, this blog is hosted by Blogger.. which does support SSL..., but only for blogs that use the internal, Blogger domain names. Since we are using a custom domain name, currently Blogger does not allow us to add any type of TLS. We could always move to a new blogging platform, but we are very happy with Blogger, and we hope that the option to secure all blogs, including those that use custom domains, will be a feature incorporated into the Blogger service in the near future.Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com2tag:blogger.com,1999:blog-7177774576397608454.post-88129896780778491622015-07-06T11:12:00.001+04:002015-07-08T17:30:49.001+04:00Asynchronous Task Containers<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-omULGBs4LZc/VZopd7Z51iI/AAAAAAAAAP8/2Eo9q6GcgDo/s1600/AsyncFunctionDelegateFinal.JPG"><img border="0" src="http://1.bp.blogspot.com/-omULGBs4LZc/VZopd7Z51iI/AAAAAAAAAP8/2Eo9q6GcgDo/s640/AsyncFunctionDelegateFinal.JPG" width="100%" /></a></div>
<br />
A few years ago I posted about <a href="http://www.viamacchina.com/2013/10/lights-camera-action-delegates.html">using Action delegates</a> to create logical containers to encapsulate functionality. This technique is a good example of <a href="https://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a> programming, and it is very useful in scenarios where you are performing similar operations with different parameters such as calling a database or service.<br />
<br />
<a name='more'></a>Traditionally, .Net code has been very synchronous, and only since the introduction of the <b>async</b> and <b>await</b> keywords has there been a noticeable shift to asynchronous programming. I've had to update my pattern using Action delegates to create logical containers to accommodate asynchronous operations.<br />
<br />
In a synchronous scenario, to create a container to encapsulate functionality, create a method that accepts an Action delegate of the Type that you want to perform an operation on:<br />
<br />
<pre class="brush:csharp">public void ExecuteServiceCall(Action<IMobileServiceClient> serviceCall)
{
using (MobileServiceClient service = new MobileServiceClient(ServiceUrl))
{
try
{
serviceCall.Invoke(service);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}</pre>
<br />
You would think that updating this method to be asynchronous would be as simple as adding the <b>async</b> and <b>await</b> keywords. But as you can see in the following image, the compiler does not like the fact that Invoking the delegate returns void.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-8EVtINa6YEk/VZojOoeY3JI/AAAAAAAAAPg/xSamhlI3k8w/s1600/AsyncActionDelegate.jpg"><img border="0" src="http://2.bp.blogspot.com/-8EVtINa6YEk/VZojOoeY3JI/AAAAAAAAAPg/xSamhlI3k8w/s640/AsyncActionDelegate.jpg" width="100%" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
So, how do you get around this issue?<br />
<br />
Instead of using an Action delegate, use a Function delegate. The Function delegate will allow you to pass in an operation on a Type along with a Task parameter that can be used to notify the framework of the delegates completion.<br />
<br />
<pre class="brush:csharp">public async void ExecuteServiceCall(Func<IMobileServiceClient, System.Threading.Tasks.Task> serviceCall)
{
using (MobileServiceClient service = new MobileServiceClient(ServiceUrl))
{
try
{
await serviceCall(service);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}</pre>
<br />
This seems simple enough, but if you try to call this method, you will still encounter a problem. You can see in the following image that the compiler still needs a Task to be returned in order to properly utilize the <b>async</b> and <b>await</b> infrastructure.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-t5S4eaztVGM/VZoq2eYe2KI/AAAAAAAAAQI/V1LBYkJKBdE/s1600/AsyncFunctionDelegateError.JPG"><img border="0" src="http://3.bp.blogspot.com/-t5S4eaztVGM/VZoq2eYe2KI/AAAAAAAAAQI/V1LBYkJKBdE/s640/AsyncFunctionDelegateError.JPG" width="100%" /></a></div>
<br />
Once again, this is an easy fix. If you update the method to return a Type of Task, and then return the instantiated Type from inside of the delegate, you'll find that the compiler will be happy, and that your asynchronous calls will work properly.<br />
<br />
<pre class="brush:csharp">public async System.Threading.Tasks.Task<IMobileServiceClient> ExecuteServiceCall(Func<IMobileServiceClient, System.Threading.Tasks.Task> serviceCall)
{
using (MobileServiceClient service = new MobileServiceClient(Ir360ServiceUrl))
{
try
{
await serviceCall(service);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return service;
}
}</pre>
<br />
Using this updated technique will allow you to stick to <b>DRY</b> programming principles and help you create cleaner, more efficient code!<br />
<br />
I hope this helps!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com2tag:blogger.com,1999:blog-7177774576397608454.post-63833619355231397492015-02-17T13:43:00.004+04:002015-04-19T08:38:58.723+04:00iTextSharp hates your HTML5<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-uox6QCFa62E/VOL_fVhjibI/AAAAAAAAAOw/-S5oA2pocxk/s1600/Hate.PNG"><img border="0" src="http://4.bp.blogspot.com/-uox6QCFa62E/VOL_fVhjibI/AAAAAAAAAOw/-S5oA2pocxk/s1600/Hate.PNG" width="100%" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
When it comes to PDF generation in .Net there aren't many open source projects to choose from, so by default many people choose the most popular community driven project, <a href="http://sourceforge.net/projects/itextsharp/" target="_blank">iTextSharp</a>. With a relatively easy learning curve and decent API, iTextSharp can address most simple scenarios for generating PDF's, and with a little more effort can render even more complex PDF's as well. iTextSharp has been essentially free using the LGPL license, but with the introduction of version 5, the AGPL license has clouded the waters a bit.<br />
<br />
This post addresses a potential gotcha when using the iTextSharp XmlWorker to render HTML5 to PDF's.<br />
<a name='more'></a><br />
I was working on a solution that required HTML to be clipped from the client using jQuery's .html() method, and then that HTML would be posted to a .Net Web Api controller that would take the posted HTML and create a PDF using iTextSharp.<br />
<br />
The issue that I encountered is that iTextSharp's XmlWorker would not process the HTML from the client. At first I did not understand why the XmlWorker was failing... I mean I could already tell from the client that the HTML was rendering just fine. But... when I took a minute to examine the HTML in the Chrome Developer Tools, the problem hit me. I had open tags in my markup... therefore in the eyes of the iTextSharp XmlWorker... it was not valid XML.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-v9B0eLJXZBw/VOL_h5Fm56I/AAAAAAAAAO4/GHAak5zJcY0/s1600/ValidMarkup.jpg"><img border="0" src="http://2.bp.blogspot.com/-v9B0eLJXZBw/VOL_h5Fm56I/AAAAAAAAAO4/GHAak5zJcY0/s1600/ValidMarkup.jpg" width="100%" /></a></div>
<br />
I reviewed my code and noticed that I was properly closing all of my tags... so why was the browser rendering open tags? The page was using an HTML5 DOCTYPE... that's why. I was properly closing my tags... which is actually improper if you are accurately following the HTML5 spec (<a href="http://tiffanybbrown.com/2011/03/23/html5-does-not-allow-self-closing-tags/" target="_blank">Here is a great post on HTML5's treatment of self-closing tags</a>). The browser was taking my invalid markup, and correcting it... which in turn was causing the iTextSharp XmlWorker to fail.<br />
<br />
So, how to fix the situation? Well, I came up with two solutions. First, I added the <a href="https://code.google.com/p/jquery-clean/" target="_blank">jQuery-clean plugin</a> to my View so that after I grabbed the HTML from the page, I could then "clean" it so that it would be XHTML compatible. This was actually enough to solve the problem. After converting the HTML5 to XHTML, iTextSharp began processing the HTML input and creating PDF's from it. However, I took an additional step to ensure that any HTML that entered the controller would arrive at the XmlWorker as valid XHTML by installing the <a href="http://htmlagilitypack.codeplex.com/" target="_blank">HTML Agility Pack</a> and creating a method that would transform any HTML input to valid XHTML output.<br />
<br />
My PDF's looked horrible, but this had more to do with iTextSharp's abysmal <a href="http://demo.itextsupport.com/xmlworker/itextdoc/CSS-conformance-list.htm" target="_blank">CSS support</a>. But hey... at least PDF's were being generated! (I later completely restructured the View's HTML to use old school Table layouts, which helped the look and layout of the PDF's ).<br />
<br />
Although I've often used iTextSharp in the past, I generally look for alternatives if they are available. For instance, if your organization is running SharePoint 2010 or higher, I recommend checking out SharePoint's <a href="https://msdn.microsoft.com/en-us/library/office/ee558278(v=office.14).aspx" target="_blank">Word Automation Services</a>. If SharePoint is not an option there's <a href="http://www.pdfsharp.net/" target="_blank">PDFSharp</a>, and my new favorite, <a href="https://github.com/gmanny/Pechkin" target="_blank">Pechkin</a>, which is a .Net wrapper for the <a href="https://github.com/wkhtmltopdf/wkhtmltopdf" target="_blank">wkhtmltopdf </a> rendering engine. As a matter of fact, I initially used Pechkin in this project before going with iTextSharp. I was able to create my PDF using HTML clipped from the client in 1 line of code. The problem I encountered is that the site I was working on was hosted in Azure, and the wkhtmltopdf engine requires GDI+, which Azure currently does not support.<br />
<br />
I hope this helps someone who may be struggling to get iTextSharp to play nicely with their markup. If you have any suggestions or alternative approaches for rendering PDF's using iTextSharp or any other projects out there, I'd love to hear them!<br />
<br />Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com2tag:blogger.com,1999:blog-7177774576397608454.post-34458227771035577272014-10-27T16:01:00.000+04:002014-11-01T13:26:18.684+04:00An argument for using WebGrease instead of the ASP.Net Web OptimizationFramework<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-JjJFf5PnMCQ/VEYIT0EWmEI/AAAAAAAAAME/-OaOBToCD-4/s1600/Nuget.PNG" imageanchor="1"><img border="0" src="http://3.bp.blogspot.com/-JjJFf5PnMCQ/VEYIT0EWmEI/AAAAAAAAAME/-OaOBToCD-4/s1600/Nuget.PNG" width="100%" /></a></div>
<br />
ASP.Net Web Optimization was introduced to ease the minification and bundling of JavaScript and CSS files within your project. It's available out of the box with any new project that uses the MVC 4 project templates. In your App_Start folder, there's a file called BundleConfig where you configure what files should be minified and bundled into one file. Minification and bundling are a good thing of course, because they reduce the number of requests the browser has to make in order to retrieve the resources it needs to render a page, and they reduce the size of those resources.<br />
<a href="https://www.blogger.com/null" name="more"></a><br />
But, what if you have an MVC 3 or WebForms project? What do you do? There are plenty of tutorials out there that will walk you through adding the ASP.Net Web Optimization NuGet package to your project, and the steps that it will take to get your project configured. This post however takes a different approach. What if we don't use the ASP.Net Web Optimization package at all... what if we just use WebGrease instead?<br />
<a name='more'></a><br />
What's WebGrease you ask? Well, if you notice in the NuGet Package Manager window, WebGrease is a dependancy of the ASP.Net Web Optimization pacakage. WebGrease is what the System.Web.Optimization.dll utilizes to minify and bundle resources behind the scenes when you define bundles in your BundleConfig. You can actually do more with WebGrease than you can utilizing the System.Web.Optimization assembly. According to the <a href="https://webgrease.codeplex.com/" target="_blank">WebGrease CodePlex site</a>, WebGrease:<br />
<blockquote class="tr_bq" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; font-stretch: normal; line-height: 19.0176963806152px; margin-bottom: 20px; outline: 0px; padding: 0px; vertical-align: baseline;">
"optimizes a web application’s static files, including CSS, JavaScript, and image files. It includes the following capabilities:<br />
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.0176963806152px; list-style: none url(https://download-codeplex.sec.s-msft.com/Images/v20941/doublearrow.gif); margin: 15px 0px 20px; outline: 0px; padding: 0px 0px 0px 35px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0.3em 0px 0.3em 40px; outline: 0px; padding: 0px; vertical-align: middle;">Minimize CSS and JavaScript files</li>
</ul>
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.0176963806152px; list-style: none url(https://download-codeplex.sec.s-msft.com/Images/v20941/doublearrow.gif); margin: 15px 0px 20px; outline: 0px; padding: 0px 0px 0px 35px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0.3em 0px 0.3em 40px; outline: 0px; padding: 0px; vertical-align: middle;">Bundle all static files into a single output file</li>
</ul>
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.0176963806152px; list-style: none url(https://download-codeplex.sec.s-msft.com/Images/v20941/doublearrow.gif); margin: 15px 0px 20px; outline: 0px; padding: 0px 0px 0px 35px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0.3em 0px 0.3em 40px; outline: 0px; padding: 0px; vertical-align: middle;">Combine all images referenced in a CSS file into a single sprite</li>
</ul>
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.0176963806152px; list-style: none url(https://download-codeplex.sec.s-msft.com/Images/v20941/doublearrow.gif); margin: 15px 0px 20px; outline: 0px; padding: 0px 0px 0px 35px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0.3em 0px 0.3em 40px; outline: 0px; padding: 0px; vertical-align: middle;">Verify JavaScript files</li>
</ul>
<ul style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #253340; font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.0176963806152px; list-style: none url(https://download-codeplex.sec.s-msft.com/Images/v20941/doublearrow.gif); margin: 15px 0px 20px; outline: 0px; padding: 0px 0px 0px 35px; vertical-align: baseline;">
<li style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; margin: 0.3em 0px 0.3em 40px; outline: 0px; padding: 0px; vertical-align: middle;">Rename static files based on a hash of their content</li>
</ul>
WebGrease can be run completely from the command line, or you can specify a <a href="https://webgrease.codeplex.com/wikipage?title=WebGrease%20configuration%20file" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #2e8bcc; margin: 0px; outline: 0px; padding: 0px; text-decoration: none; vertical-align: baseline;">WebGrease configuration file</a> that contains the settings. WebGrease also includes a <a href="https://webgrease.codeplex.com/wikipage?title=WebGrease%20build%20task" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #2e8bcc; margin: 0px; outline: 0px; padding: 0px; text-decoration: none; vertical-align: baseline;">WebGrease build task</a> that can be integrated with an MSBuild project."</blockquote>
To get started using WebGrease, add it to your project using NuGet. Search for "WebGrease".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-zcOU1B3ZP1g/VEYQ9QM9sMI/AAAAAAAAAMQ/ifRkq1wmBFo/s1600/WebGrease.PNG" imageanchor="1"><img border="0" src="http://3.bp.blogspot.com/-zcOU1B3ZP1g/VEYQ9QM9sMI/AAAAAAAAAMQ/ifRkq1wmBFo/s1600/WebGrease.PNG" width="100%" /></a></div>
<br />
<br />
After the package has been added to your project you will be able to utilize the WebGrease Executable in Pre and Post build operations.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-A8Jo6cyKbDY/VEYRwCB-5OI/AAAAAAAAAMY/ML8qONWyIHM/s1600/PreBuild.PNG" imageanchor="1"><img border="0" src="http://2.bp.blogspot.com/-A8Jo6cyKbDY/VEYRwCB-5OI/AAAAAAAAAMY/ML8qONWyIHM/s1600/PreBuild.PNG" width="100%" /></a></div>
<br />
Here is an example of calling the executable to minimize a CSS file:<br />
<pre class="brush:bash">$(SolutionDir)packages\WebGrease.1.3.0\tools\wg -m -in:$(ProjectDir)Content\themes\base\source\normalize.css -out:$(ProjectDir)Content\themes\base\source\normalize.min.css</pre>
The WebGrease executable accepts arguments but can also be executed in conjunction with a configuration file.<br />
<pre class="brush:bash">$(SolutionDir)packages\WebGrease.1.3.0\tools\wg -b -in:$(ProjectDir)Scripts\ -out:$(ProjectDir)Scripts\ -c:$(ProjectDir)Scripts\WebGrease.config -type:$(ConfigurationName)</pre>
<br />
So, why use WebGrease without the ASP.Net Web Optimization Package? Well, for one, you can create CSS Sprites using WebGrease, which is currently something that can't be done using the ASP.Net Web Optimization Package (There is currently a Sprite and Image Optimization Preview package available <a href="https://aspnet.codeplex.com/releases/view/65787" target="_blank">here</a>.).<br />
<br />
In my opinion, the biggest reason to go down this road, comes down to a shortcoming with ASP.Net WebForms.<br />
<br />
I was recently working on a client project that was created using ASP.Net WebForms. Like many WebForms projects, a ScriptManager control had been added to the MasterPage to handle any necessary script management in the content pages. I initially added the ASP.Net Web Optimization package to the project in order to get some minification and bundling setup. However, I quickly ran into an issue....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-XbYnTiMfmSU/VEYWoiDDnnI/AAAAAAAAAMk/1fy2JLJulAA/s1600/CodeBlocksAreNot%2BSupported.png" imageanchor="1"><img border="0" src="http://3.bp.blogspot.com/-XbYnTiMfmSU/VEYWoiDDnnI/AAAAAAAAAMk/1fy2JLJulAA/s1600/CodeBlocksAreNot%2BSupported.png" width="100%" /></a></div>
<br />
You can't add code blocks to the declarative markup inside the Scripts collection of the ScriptManager control. In turn, this meant that I could not add the declarative call to output the script bundle created by the ASP.Net Web Optimization framework. And while I technically could have made all of this work by programmatically adding the output of the bundle to the ScriptManager, I really did not want to have to introduce dependencies on the bundling process in the application.<br />
<br />
Secondly, if you notice, I'm using the CompositeScript collection in the ScriptManager. This performs a similar function to WebGrease in that it bundles all of the scripts defined in its collection and outputs them as one script. It gets better though, because you can reference scripts that have been embedded as resource in assemblies, and combine them into one script. So, for example, in the following image we reference a bunch of the default scripts used by WebForms, the GridViewControl, MenuControl, Validation, and Microsoft Ajax (UpdatePanel), and all of these scripts will be loaded from their respective assembly, combined into one script, and then transferred to the browser as one file.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/--bNK5BtYb3I/VEYYxAFcbnI/AAAAAAAAAMw/X9MOmKVDGVY/s1600/ScriptManager.PNG" imageanchor="1"><img border="0" src="http://1.bp.blogspot.com/--bNK5BtYb3I/VEYYxAFcbnI/AAAAAAAAAMw/X9MOmKVDGVY/s1600/ScriptManager.PNG" width="100%" /></a></div>
<br />
So how do you add your minimized and bundled project JavaScript files to the ScriptManager?<br />
<br />
<a href="http://1.bp.blogspot.com/-0vF7UTkWXwc/VEYcxfcoMiI/AAAAAAAAAM8/55Ao_gFxJ6M/s1600/Scripts.PNG" imageanchor="1" style="float: right; margin-left: 10px; width: 40%;"><img border="0" src="http://1.bp.blogspot.com/-0vF7UTkWXwc/VEYcxfcoMiI/AAAAAAAAAM8/55Ao_gFxJ6M/s1600/Scripts.PNG" width="100%" /></a><br />
First, identify all of the scripts that you want to be included in your bundle. In this case, I'm using a bunch of Microsoft scripts that are embedded in assemblies. Instead of trying to extract them from the assemblies, just grab them from the <a href="http://www.asp.net/ajax/cdn" target="_blank">CDN</a>. Add them to your project along with your other JavaScript Files.<br />
<br />
Second, create the necessary Pre Build commands to get WebGrease to minimize your JavaScript files.<br />
<br />
Third, create the necessary Pre Build command to get WebGrease to bundle the various JavaScript files into a single file. In this example, I've used a configuration file with the WebGrease executable to bundle the minimized files from the various script folder and they are all bundled into the "FacilityOptimization.js" file.<br />
<br />
Finally, reference the minimized and bundled JavaScript file in your ScriptManager's CompositeScript Path property. There seems to be some confusion on the web that this property can be used to set the name of the script that will be sent to the browser. This is incorrect. The correct usage of the Composite Script Path property is to set the path to a script file on the file system that contains all of the scripts defined in the CompositeScript Scripts collection, whether they exist on disk or as a resource in an assembly. This way, the ScriptManager knows that the scripts that would normally be loaded from assemblies will now be loaded from the script identified by the CompositeScript Path property.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-9ChvD5PiZBw/VEYhgAI5bRI/AAAAAAAAANU/pY5FWTFchto/s1600/Path.PNG" imageanchor="1"><img border="0" src="http://4.bp.blogspot.com/-9ChvD5PiZBw/VEYhgAI5bRI/AAAAAAAAANU/pY5FWTFchto/s1600/Path.PNG" width="100%" /></a></div>
<br />
So what does this get you? Here's what the requested resources looked like before optimization:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-0nbHx4ZR234/VEYlHvRNVeI/AAAAAAAAAN0/3o_xheIEvdY/s1600/Before.PNG" imageanchor="1"><img border="0" src="http://4.bp.blogspot.com/-0nbHx4ZR234/VEYlHvRNVeI/AAAAAAAAAN0/3o_xheIEvdY/s1600/Before.PNG" width="100%" /></a></div>
<br />
Here's what the requested resources looked like after optimization:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-6x6V6VgTgCk/VEYlJNjlaKI/AAAAAAAAAN8/b5PZ9T8TNMw/s1600/After.PNG" imageanchor="1"><img border="0" src="http://2.bp.blogspot.com/-6x6V6VgTgCk/VEYlJNjlaKI/AAAAAAAAAN8/b5PZ9T8TNMw/s1600/After.PNG" width="100%" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
The pages have considerably fewer resources that need to be loaded, and those resources are comparatively smaller, which leads to faster page loads.<br />
<br />
To sum up, I believe there are situations that warrant straying from the path and utilizing different methods that best address the situation. In this case, moving away from the ASP.Net Web Optimization framework and utilizing WebGrease was the right solution. It does however take considerably more effort, and is ultimately more brittle than using the web optimization framework.<br />
<br />
So, if you find yourself in a similar situation, consider dropping the ASP.Net Web Optimization framework and just stick with WebGrease!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com5tag:blogger.com,1999:blog-7177774576397608454.post-74945948409724152662014-08-31T19:43:00.000+04:002014-08-31T19:50:11.152+04:00Installing Ghost on Ubuntu 14I've been wanting to check out <a href="https://ghost.org/" target="_blank">Ghost</a> for a while now, and over the weekend I decided to give it a whirl. I fired up a new VM and installed the latest version of Ubuntu, downloaded the source from the Ghost.org site, and followed the simple installation procedure. And that's about as far as I got.<br />
<br />
Unfortunately, something within the latest release of 'buntu is not configured correctly to just install and go. After some Googlin' I found <a href="http://log4think.com/setup-ghost-blog-system-on-ubuntu/" target="_blank">Jinyu Liu's post</a> about setting up Ghost, and he found the magic.<br />
<br />
Run the following:<br />
<pre class="brush:bash">sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10</pre>It will fix the Node symbolic link, which is apparently not correct.<br />
<br />
After running that, everything installed just as described on the Ghost.org site, and I had a new blog up and running in minutes.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-8g3Y3BSSt2A/VAM_HIG1OVI/AAAAAAAAALw/Gp-hPOpikGE/s1600/Ghost.png" imageanchor="1"><img border="0" src="http://4.bp.blogspot.com/-8g3Y3BSSt2A/VAM_HIG1OVI/AAAAAAAAALw/Gp-hPOpikGE/s1600/Ghost.png" width="100%" /></a></div>Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-2433015066137935032014-08-25T15:03:00.001+04:002014-08-25T15:03:55.076+04:00SSRS Deployment CenterI recently worked on an enterprise project that involved deploying SSRS reports and data sources to different environments throughout the organization. SSRS reports have always lacked proper deployment tooling compared to other project types. The <a href="http://msdn.microsoft.com/en-us/library/ms162839.aspx" target="_blank">RS.EXE</a> deployment tool from Microsoft makes it possible to create scripted deployments, but it's too complex for the average business user. And although it's possible to use it in conjunction with automated builds, it is not as easy as it should be. Because of this I created a set of GUI and command line tools called the SSRS Deployment Center that will hopefully ease future SSRS report deployments.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=SSRSDeploymentCenter&DownloadId=890635" imageanchor="1"><img border="0" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=SSRSDeploymentCenter&DownloadId=890635" width="100%" /></a></div><br />
I've put the project onto CodePlex, so if your in need of a set of tools to make report deployments easier, check it out here:<br />
<br />
<a href="https://ssrsdeploymentcenter.codeplex.com/" target="_blank">https://ssrsdeploymentcenter.codeplex.com/</a><br />
<br />
The CodePlex site has all of the information you need to download, configure and run the tools.<br />
<br />
<br />
Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-36454397393738370172014-07-28T16:00:00.000+04:002014-07-28T16:00:12.970+04:00SSRS Nested Subreport Gotcha!Recently I was working on a reporting solution for a client when I ran across an interesting problem. I was writing a report in SSRS using multiple nested subreports that would roll up into the report. This has been a fairly common approach that I've used in the past. The difference in this case was the number of nested subreports. Specifically I was using three levels of reports, all of which used some form of data binding. I ran into an issue where the "grand-child" report would not render.<br />
<br />
It is easiest to explain the situation using images. There are three reports. A top level, "master" report that we will refer to as the Parent. Within the Parent report there is a sub report that we will refer to as the Child. And within the Child report there is a sub report that we will refer to as the GrandChild.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-FQZRwgEOnuU/U8_GEWGn6EI/AAAAAAAAALA/eWFz1YSlFBg/s1600/SSRS_Bug_1.PNG" imageanchor="1"><img border="0" src="http://3.bp.blogspot.com/-FQZRwgEOnuU/U8_GEWGn6EI/AAAAAAAAALA/eWFz1YSlFBg/s1600/SSRS_Bug_1.PNG" width="100%" /></a></div>
<a name='more'></a><br />
When I initially created the report I had no issues. I was able to render the Parent, Child, and GrandChild without a problem. All of the reports bound to their respective data source and displayed the data that was expected of them. However, the moment that I added a rectangle to the Parent report, and set its "Add Page Break After" setting to True, problems arose.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-v9txmjwqyOk/U8_GEeJqC6I/AAAAAAAAAK0/oCAuPUpcG4o/s1600/SSRS_Bug_2.PNG" imageanchor="1"><img border="0" src="http://1.bp.blogspot.com/-v9txmjwqyOk/U8_GEeJqC6I/AAAAAAAAAK0/oCAuPUpcG4o/s1600/SSRS_Bug_2.PNG" width="100%" /></a></div>
<br />
With that setting checked, the GrandChild report would not render, and instead showed the error "Subreport could not be shown.".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-JKs5kMO2pgQ/U8_GEZknS6I/AAAAAAAAAK4/3HLDnmKmioQ/s1600/SSRS_Bug_3.PNG" imageanchor="1"><img border="0" src="http://2.bp.blogspot.com/-JKs5kMO2pgQ/U8_GEZknS6I/AAAAAAAAAK4/3HLDnmKmioQ/s1600/SSRS_Bug_3.PNG" width="100%" /></a></div>
<br />
After digging into the issue more, what I found seems to be an issue with the Child subreports data binding interfering with the GrandChild subreports data binding when the Parent subreport has the "Add Page Break After" attribute set to True on the containing element. If you need to run through that one again, don't sweat it... I had to go over it a few times before it really registered with me. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-TFtyegxlqhM/U8_GGt_U-XI/AAAAAAAAALM/Xpkzhc4ro5s/s1600/SSRS_Bug_4.PNG" imageanchor="1"><img border="0" src="http://1.bp.blogspot.com/-TFtyegxlqhM/U8_GGt_U-XI/AAAAAAAAALM/Xpkzhc4ro5s/s1600/SSRS_Bug_4.PNG" width="100%" /></a></div>
<br />
The Child subreport uses an expression to perform a lookup. Essentially this is a simple "the data source has the entity Id and we want to show the entity name" kind of lookup. If I remove this expression, the subreports return to working order.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-f9LBH0asbUk/U8_GG8hGhSI/AAAAAAAAALQ/j6mQ_Z1TICY/s1600/SSRS_Bug_5.PNG" imageanchor="1"><img border="0" src="http://2.bp.blogspot.com/-f9LBH0asbUk/U8_GG8hGhSI/AAAAAAAAALQ/j6mQ_Z1TICY/s1600/SSRS_Bug_5.PNG" width="100%" /></a></div>
<br />
<br />
Ultimately it turns out to be either an SSRS bug, or a limitation with the reporting framework. I've opened the following bug with Microsoft:<br />
<a href="https://connect.microsoft.com/SQLServer/feedback/details/917406/ssrs-nested-sub-reports-break-when-parent-contains-item-with-add-a-page-break-after-checked">https://connect.microsoft.com/SQLServer/feedback/details/917406/ssrs-nested-sub-reports-break-when-parent-contains-item-with-add-a-page-break-after-checked</a><br />
<br />
Whatever the deal, be careful when using nested sub reports in SSRS. If you've run into this issue before and you know of a work around, leave a comment and let me know. I'd love to be able to avoid this in the future!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-47704240189968959702014-07-21T18:25:00.000+04:002014-07-21T18:31:46.636+04:00Reminder: If you develop WebForms, you should be doing this…There are plenty of posts out there about using the aspnet_compiler.exe tool.<br />
<br />
<a href="http://mikehadlow.blogspot.com/2008/05/compiling-aspx-templates-using.html">http://mikehadlow.blogspot.com/2008/05/compiling-aspx-templates-using.html</a><br />
<a href="http://odetocode.com/blogs/scott/archive/2006/10/17/what-can-aspnet_compiler-exe-do-for-me.aspx">http://odetocode.com/blogs/scott/archive/2006/10/17/what-can-aspnet_compiler-exe-do-for-me.aspx</a><br />
<br />
I’m not writing this to shed any new light on the subject. I'm writing this to remind developers that you should be using this tool… period. <br />
<a name='more'></a><br />
It’s easy. <br />
<br />
1. Add it as a Post Build event in your project:<br />
<pre class="brush:ps">%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_compiler.exe -v / -p "$(ProjectDir)"</pre>Or<br />
<br />
2. Add it as an MSBUILD target in your projects XML configuration: <br />
<pre class="brush:xml"><target name="AfterBuild">
<message importance="high" text="Starting AspNetCompiler for $(ProjectName)" />
<aspnetcompiler clean="true" debug="true" force="true" physicalpath="$(ProjectDir)" targetpath="$(ProjectDir)\obj\aspnet_compiler" virtualpath="/" />
<message importance="high" text="Completed AspNetCompiler for $(ProjectName)" />
</target></pre><br />
After enabling pre-compilation you may just find that your project has more issues that you previously thought.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-Yu6vEkhTV2Y/U80h2DbC7FI/AAAAAAAAAKk/JR3svbgn6og/s1600/Errors.PNG" imageanchor="1"><img src="http://4.bp.blogspot.com/-Yu6vEkhTV2Y/U80h2DbC7FI/AAAAAAAAAKk/JR3svbgn6og/s1600/Errors.PNG" width="100%" /></a></div><br />
It makes too much sense to not do this. It will save your butt at least once… I promise.Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-21441395441425122702014-04-28T12:50:00.001+04:002014-07-21T18:34:08.909+04:00Improving WebSql interaction using jQuery DeferredI recently inherited a project that made heavy use of WebSql. Though<a href="http://caniuse.com/#search=web%20sql" target="_blank"> it has been deprecated</a> as a client side technology, this project was using it to create an offline web application that could be run on mobile devices. The immediate problem that I noticed with the project was that there was no consolidation of logic into a central JavaScript file. Each page included inline functions that essentially did the same thing, just using different queries. After profiling the site I realized that this "distribution" of logic also led to multiple instances of the web sql database being instantiated.... which, as you guessed, is a problem.<br />
<br />
To address this issue I started by creating a wrapper for the Web SQL functions that would make it easier to work with the technology. The wrapper removes the work of instantiating the database object and managing the transaction, and it uses jQuery Deferred objects to make it easier to work with the results returned by the Web SQL calls.<br />
<a name='more'></a><pre class="brush:js">/* Adding namespace objects to keep the code out of the global namespace */
var viaMacchina = viaMacchina || {};
viaMacchina.webSql = viaMacchina.webSql || {};
/* Database functions */
viaMacchina.webSql.Database = viaMacchina.webSql.Database || {};
viaMacchina.webSql.Database.open = function () {
if (viaMacchina.webSql.Database.db === null || viaMacchina.webSql.Database.db === undefined) {
var dbSize = 2 * 1024 * 1024;
viaMacchina.webSql.Database.db = openDatabase("viaMacchinaDatabase", "1.0", "viaMacchina WebSql Database", dbSize);
}
return viaMacchina.webSql.Database.db;
};
viaMacchina.webSql.Database.onSuccess = function (results) {
// Add default logging or anything else that may be appropriate.
};
viaMacchina.webSql.Database.onError = function (error) {
alert(error.message);
};
viaMacchina.webSql.Database.executeCommand = function (commandText, data, onSuccess, onError) {
var progress = $.Deferred();
if (data === undefined || data === null) {
data = [];
}
if (onSuccess === undefined || onSuccess === null) {
onSuccess = viaMacchina.webSql.Database.onSuccess;
}
if (onError === undefined || onError === null) {
onError = viaMacchina.webSql.Database.onError;
}
var db = viaMacchina.webSql.Database.open();
db.transaction(function (tx) {
tx.executeSql(commandText, data,
function (tx, results) {
progress.resolve(results);
onSuccess(results);
},
function (tx, error) {
progress.reject(error);
onError(error);
});
});
return progress;
};
</pre>Using the wrapper you can now call your Web SQL database like so:<br />
<pre class="brush: js">var command = "SELECT * FROM Locations WHERE BuildingID=" + buildingId + " ORDER BY LocationId";
viaMacchina.webSql.Database.executeCommand(command).done(function (results) {
// process the results from the database call.
});</pre><br />
As simple as it is, this minor addition resolved the majority of issues that the project was suffering from. But sometimes, the simpleset solutions are the best solutions!<br />
<br />
I hope this helps!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com1tag:blogger.com,1999:blog-7177774576397608454.post-56919087799903565372014-02-24T21:25:00.000+04:002014-02-24T21:25:00.319+04:00A Multi-Term jQuery Mobile Listview FilterIf you've done any development with jQuery Mobile you've likely used the <a href="http://api.jquerymobile.com/1.3/listview/" target="_blank">Listview Widget</a>. It's a handy little component that makes rendering list data a breeze. Even better, it has a built in search filter that you can enable by simply decorating your list element with <span style="font-family: inherit;"><b><span style="background-color: white; color: #333333; line-height: 1.5em;">data-filter="true"</span></b></span><br />
<span style="font-family: inherit;"><b><span style="background-color: white; color: #333333; line-height: 1.5em;"><br />
</span></b></span> <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-PUmDBT96lEg/UwQu1O1qmGI/AAAAAAAAAKA/T599hEdH74I/s1600/Search.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-PUmDBT96lEg/UwQu1O1qmGI/AAAAAAAAAKA/T599hEdH74I/s1600/Search.gif" /></a></div>
<span style="font-family: inherit;"><b><span style="background-color: white; color: #333333; line-height: 1.5em;"><br />
</span></b></span> The default behavior of the Listview Widget's filter is to match on a single term. The moment you add an additional space into your search string, look out! But, if you need multi-term search behavior in your filter, it's easy to add.<br />
<a name='more'></a><br />
Attach to jQuery Mobile's 'mobileinit' event and assign a new function to the $.mobile.listview.prototype.options.filterCallback. Remember, this event must occur <b>BEFORE </b>jQuery Mobile has been loaded, so this script will need to load before the jQuery Mobile JS file.<br />
<br />
<pre class="brush:javascript">$(document).on('mobileinit', function () {
$.mobile.listview.prototype.options.filterCallback = function (currentItem, searchString) {
var result = true;
var resultCount = 0;
var matches = $.trim(currentItem).split(/\s+/);
var tokens = $.trim(searchString).split(/\s+/);
var matchCount = matches.length;
$.each(matches, function (index, match) {
$.each(tokens, function (childIndex, token) {
if (match.toLowerCase().indexOf(token) >= 0) {
resultCount++;
}
});
});
if (resultCount === matchCount) {
result = false;
}
return result;
};
});
</pre>
<br />
The code splits out the incoming item and search string into a set of matches and tokens. If the text of the current item contains a token from the search string, we increment a result counter. After iterating through all of the matches, if the current items string match count equals the number of tokens in the search string, then we know we have a match on all terms.<br />
<br />
I hope this helps!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com8tag:blogger.com,1999:blog-7177774576397608454.post-78699099216322739182014-02-04T15:00:00.000+04:002014-02-18T21:01:34.343+04:00Quick and Compliant Email Templates using RazorEven in today's hyper connected world where you can Tweet someone's TV or Instagram your refrigerator, it seems like when it comes to business, Email is still king. If you need a simple, effective, and fast way to create a templated email in code, I suggest using Razor as your templating engine.<br />
<div><br />
</div><div>If you've done any MVC development since version 3, you've likely used the new Razor View Engine. Its concise and semantic, and it gets out of the way of your html. Best of all, it's not dependent on MVC, so you can re-purpose it in many different ways.</div><div><br />
</div><div>There are already a few existing projects that surround Razor templates. In this example I'm using the following project, but you can use whatever you prefer to get the job done:<br />
<br />
</div><div><a href="https://github.com/volkovku/RazorTemplates" target="_blank">https://github.com/volkovku/RazorTemplates</a><br />
<br />
I like the RazorTemplates project because like Razor itself, the syntax in the RazorTemplates project is concise and semantic.<br />
<a name='more'></a><br />
</div><div>We're going to need an email template as well, so I suggest using the following boiler plate template:<br />
<br />
</div><div><a href="http://htmlemailboilerplate.com/" target="_blank">http://htmlemailboilerplate.com/</a><br />
<br />
</div><b><span style="font-size: x-small;">One thing to note with this template. It contains a few "@" characters that will need to be escaped in order to work with the Razor engine. Fix this by adding an additional "@" symbol, resulting in a double "@@".</span></b><br />
<br />
Add the RazorTemplates assembly to your project using NuGet.<br />
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); box-sizing: border-box; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">PM> Install-Package RazorTemplates</code></pre><br />
With just a few lines of code we can load up our template file, compile it, and then render it.<br />
<br />
<pre class="brush:csharp">string templateFile = File.ReadAllText("/Templates/EmailTemplate.html");
var template = Template.Compile(templateFile);
string emailBody = template.Render(new { Name = "Everett" });
</pre><br />
To render data in our template file, we can use the Razor engine's Model syntax to access the object data that we have passed into the <b>template.Render()</b> method. In this case we can access the "<b>Name</b>" property on our object by adding the following line to out template file:<br />
<br />
<pre class="brush:csharp"><body>
Thanks for visiting our site @Model.Name
</body>
</pre><br />
And that's it! No more <b>string.Replace()</b>, no more XSLT, no more wasted time creating mail templates!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-77810125188807597302014-01-27T14:43:00.000+04:002014-01-27T14:48:38.929+04:00Generic Repositories Including Includes!The real credit for this solution goes to Steve Moss. He posted this article last year:<br />
<br />
<a href="http://www.appetere.com/Blogs/SteveM/May-2012/Passing-Include-statements-into-a-Repository" target="_blank">http://www.appetere.com/Blogs/SteveM/May-2012/Passing-Include-statements-into-a-Repository</a><br />
<br />
All that I've done is modified his work to fit in with the generic repository pattern that I often use.<br />
<br />
<a href="http://www.viamacchina.com/2013/07/a-generic-repository-for-entity.html" target="_blank">http://www.viamacchina.com/2013/07/a-generic-repository-for-entity.html</a><br />
<br />
One of the difficulties in using a repository to manage your data is that the repository itself can end up obscuring functionality from the underlying data provider. The pattern that I previously wrote about did just this. If you needed to Include relationships that were not originally established in your entity model, you had to create additional logic to retrieve that missing data.<br />
<br />
As the Entity Framework has matured, the support for Including data from related entities has improved. Specifically, since EF 4.1, you can use strongly typed lambda's to specify relationships to eagerly load data. This, in combination with the existing string "Include", make working with entity relationships much more convenient.<br />
<br />
To extend the original repository pattern to support dynamic includes, all that has to be done is to add an additional Select overload to the repository interface, and then make a quick change to the base class and the child classes that implement it.<br />
<br />
1. Update the interface definition. The overloaded Select method that accepts an array of Expressions is the key.<br />
<pre class="brush:csharp">public interface IRepository : IDisposable
{
void Insert<E>(E entity) where E : class;
void Update<E>(E entity) where E : class;
void Delete<E>(E entity) where E : class;
IQueryable<E> Select<E>() where E : class;
IQueryable<E> Select<E>(params Expression<Func<E, object>>[] includeExpressions) where E : class;
E Select<E>(object key) where E : class;
}</pre>
<a name='more'></a><br />
<br />
2. Update the BaseRepository class.<br />
<pre class="brush:csharp">public abstract class BaseRepository : IRepository, ISaveChanges
{
#region Methods
public abstract void Insert<E>(E entity) where E : class;
public abstract void Update<E>(E entity) where E : class;
public abstract void Delete<E>(E entity) where E : class;
public int SaveChanges()
{
return SaveChanges(true);
}
public abstract int SaveChanges(bool validateEntities);
public abstract IQueryable<E> Select<E>() where E : class;
public abstract IQueryable<E> Select<E>(params Expression<Func<E, object>>[] includeExpressions) where E : class;
public abstract E Select<E>(object key) where E : class;
public abstract void Dispose();
#endregion
}</pre>
<br />
3. Update any class that implements the BaseRepository. In this case I am working with a repository for a DbContext.<br />
<pre class="brush:csharp">public class DbContextRepository<T> : BaseRepository where T : DbContext
{
#region Fields
private T _context;
#endregion
#region Properties
public T Context
{
get
{
if (_context == null)
_context = Activator.CreateInstance<T>();
return _context;
}
}
#endregion
#region Methods
public override void Insert<E>(E entity)
{
Context.Set<E>().Add(entity);
}
public override void Update<E>(E entity)
{
Context.Set<E>().Attach(entity);
Context.Entry<E>(entity).State = EntityState.Modified;
}
public override void Delete<E>(E entity)
{
Context.Set<E>().Attach(entity);
Context.Entry<E>(entity).State = EntityState.Deleted;
}
public override int SaveChanges(bool validateEntities)
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception(NotificationManager.DatabaseOfflineMessage);
Context.Configuration.ValidateOnSaveEnabled = validateEntities;
return Context.SaveChanges();
}
public override IQueryable<E> Select<E>()
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception(NotificationManager.DatabaseOfflineMessage);
return Context.Set<E>();
}
public override IQueryable<E> Select<E>(params Expression<Func<E, object>>[] includeExpressions)
{
IQueryable<E> result = null;
if (includeExpressions.Any())
{
result = includeExpressions.Aggregate<Expression<Func<E, object>>, IQueryable<E>>
(Context.Set<E>(), (current, expression) => current.Include(expression));
}
return result;
}
public override E Select<E>(object key)
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception(NotificationManager.DatabaseOfflineMessage);
return Context.Set<E>().Find(key);
}
public override void Dispose()
{
if (Context != null)
Context.Dispose();
}
#endregion
}
</pre>
<br />
As you can see, Steve's use of Aggregating the parameterized Include Expressions are the body of our new Select overload.<br />
<br />
Now, when working with a repository object, eagerly loading data from related entities is as easy as specify the Include when you call Select:<br />
<br />
<pre class="brush:csharp">return Select(i => i.FileTypeHeader.FileTypeGroup, i => i.FileTemplateInputs);</pre>
<br />
This obviously makes working with the data much easier. More importantly, this allows the repository to get out of the way and focus on what it is designed to do, which is to abstract away the underlying data provider.<br />
<br />
So, thanks to Steve, and I hope this helps someone out!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-86834070992563422712013-10-24T18:37:00.002+04:002013-10-24T18:37:29.098+04:00Lights, Camera, Action Delegates!<div class="MsoNormal">
I recently worked on a project that a number of other developers collaborated on. All of the contributors to the project are very smart people, and I was impressed by much of the work that they had done. But one thing caught my eye, and I want to write a post about it since there are folks out there that are not familiar with this technique.<br />
<br />
The <b>Func</b> and <b>Action</b> delegates were introduced in .Net 3.5 and they have a variety of uses. One of the best uses that I have found for them is to separate procedural code from actual business logic. Specifically, I like to create methods that accept an action delegate to control database, Entity Framework, or Linq to Sql contexts, and WCF service operations.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In the project I was working on I repeatedly found classes in the data layer with code like this (in this example a connection object has already been created and passed into the class) :<br />
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">IDbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">CreateCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">CommandText</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #d69d85; font-family: Consolas; font-size: 9.5pt;">"Command_Text"</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">try</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">if</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">State</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">==</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">ConnectionState</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Closed</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Open</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ExecuteNonQuery</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">catch</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Exception</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Log</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">DefaultLog</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">WriteVerbose</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Message</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">+</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Environment</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">NewLine</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">+</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">StackTrace</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">throw</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">finally</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">if</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">!=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">null</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">&&</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">!=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">null</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">&&</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">State</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">==</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">ConnectionState</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Open</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Close</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br />
<a name='more'></a><br /></div>
<div class="MsoNormal">
Nothing wrong with the code itself, it was just the fact that it was repeated so many times throughout the project. I saw this and knew immediately that we could clean things up and centralize all of our connection and command management into one place by using Action delegates. All that was needed was to create a method that accepted and Action delegate as a parameter (and in this case a database connection and string for the command text as well) :<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">public</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">void</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ExecuteCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">IDbConnection</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">connection</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">, </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">string</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">commandText</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">, </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Action</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;"><</span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">IDbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">></span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">command</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">using</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">IDbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">CreateCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">())<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">CommandText</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">commandText</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">if</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">IsDatabaseOnline</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">())<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">try</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">if</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">State</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">==</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">ConnectionState</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Closed</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Open</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">command</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Invoke</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">catch</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Exception</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Log</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">DefaultLog</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">WriteVerbose</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Message</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">+</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Environment</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">NewLine</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">+</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">StackTrace</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">throw</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ex</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">finally</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">if</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> (</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Transaction</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">==</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">null</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">&&</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">!=</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">null</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">&&</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">State</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">==</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b8d7a3; font-family: Consolas; font-size: 9.5pt;">ConnectionState</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Open</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">dbCommand</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Connection</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">Close</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">else</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">throw</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #569cd6; font-family: Consolas; font-size: 9.5pt;">new</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #4ec9b0; font-family: Consolas; font-size: 9.5pt;">Exception</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: #d69d85; font-family: Consolas; font-size: 9.5pt;">"The database is not online."</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; mso-background-themecolor: text1;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">}</span><o:p></o:p></div>
<div class="MsoNormal">
<span style="font-size: xx-small;">BTW: The dbCommand.Connection.IsDatabaseOnline() method call is an extension method I use in many projects... it can be ignored in this example.</span><br />
<br /></div>
<div class="MsoNormal">
So, with this method in place, all that needed to be done in order to execute a command is the following:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: white; font-family: Consolas; font-size: 9.5pt;">ExecuteCommand</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">connection</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">, </span><span style="color: #d69d85; font-family: Consolas; font-size: 9.5pt;">"Command_Text_Goes_Here"</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">, </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">command</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">=></span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; margin-bottom: .0001pt; margin-bottom: 0in; mso-background-themecolor: text1; mso-layout-grid-align: none; text-autospace: none;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;"> </span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">command</span><span style="color: #b4b4b4; font-family: Consolas; font-size: 9.5pt;">.</span><span style="color: white; font-family: Consolas; font-size: 9.5pt;">ExecuteNonQuery</span><span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: black; mso-background-themecolor: text1;">
<span style="color: gainsboro; font-family: Consolas; font-size: 9.5pt;">});<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Now you don't have to worry about managing the connection, no more worrying about catching connection errors, no more worrying about logging, and finally, your code becomes cleaner and easier to read!<o:p></o:p></div>
<div class="MsoNormal">
<br />
But, this specific example is just that, an example of one way to use an Action delegate to centralize functionality. I have used the same principle to encapsulate EF, Linq to SQL, and WCF operations. And there are plenty of other areas this technique can be applied. The takeaway of this post is that if you need to centralize operation code, consider using the Action delegate. It may be exactly what you need to get the job done.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Here are some links if you would like to find out more:<o:p></o:p></div>
<div class="MsoNormal">
<a href="http://geekswithblogs.net/BlackRabbitCoder/archive/2011/11/03/c.net-little-wonders-the-generic-action-delegates.aspx">http://geekswithblogs.net/BlackRabbitCoder/archive/2011/11/03/c.net-little-wonders-the-generic-action-delegates.aspx</a><o:p></o:p><br />
<br /></div>
<div class="MsoNormal">
<a href="http://msdn.microsoft.com/en-us/library/018hxwa8.aspx">http://msdn.microsoft.com/en-us/library/018hxwa8.aspx</a><o:p></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-21265770955982372922013-08-27T03:46:00.000+04:002015-02-26T13:04:03.405+04:00Visual Studio 2012 Photo Start Page<a href="http://lh3.ggpht.com/-LM1mAeoKmcI/UhlFNZ20vJI/AAAAAAAAAGo/evnkYr472qo/s1600-h/VSStartPage%25255B8%25255D.png"><img alt="VSStartPage" border="0" src="http://lh6.ggpht.com/--hpWSvYd7QI/UhlFN_gR2II/AAAAAAAAAGw/sKJ12bWeL1c/VSStartPage_thumb%25255B6%25255D.png?imgmax=800" height="399" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="VSStartPage" width="646" /></a><br />
<a name='more'></a><i><span style="font-family: Georgia, Times New Roman, serif; font-size: x-small;">Quick download link for those who don't want to scroll: </span></i><br />
<span style="color: #0000ee; font-family: Georgia, Times New Roman, serif; font-size: x-small;"><u><a href="https://visualstudiogallery.msdn.microsoft.com/a012051a-9bd8-4103-9c51-fd0738752d50" target="_blank">https://visualstudiogallery.msdn.microsoft.com/a012051a-9bd8-4103-9c51-fd0738752d50</a></u></span><br />
<br />
So 2 years ago (or so) I created a Photo Start Page for Visual Studio 2010. There was no reason for it really. I saw that someone else out there had made something similar (I’ll post a link to it if I can find it) and I decided I wanted to make a start page that would show Bing’s daily image. So, that’s what I did, starting with the excellent blog post by Adrian Collier<br />
<br />
<a href="http://blogs.msdn.com/b/visualstudio/archive/2010/07/29/walkthrough-creating-a-custom-start-page-part-1.aspx">http://blogs.msdn.com/b/visualstudio/archive/2010/07/29/walkthrough-creating-a-custom-start-page-part-1.aspx</a><br />
<br />
as a guide. If you are running Visual Studio 2010 and would like to try out the Visual Studio 2010 Photo Start Page, you can find it here:<br />
<br />
<a href="http://visualstudiogallery.msdn.microsoft.com/7f51fe49-d725-419b-8eff-bca084e64c54">http://visualstudiogallery.msdn.microsoft.com/7f51fe49-d725-419b-8eff-bca084e64c54</a><br />
<br />
Enter Visual Studio 2012. VS 2012 is an awesome upgrade from 2010 and since I have been using it I’ve had a hard time going back to older versions when I have to boot up a VM older than a year. But… the old Start Page no longer works. No big deal, even thought I lost the old project code, I was able to recreate the project pretty quickly using the start page project template for VS 2010 found here:<br />
<br />
<a href="http://visualstudiogallery.msdn.microsoft.com/f655a5dc-1a2d-4eca-b774-76c352c03b87">http://visualstudiogallery.msdn.microsoft.com/f655a5dc-1a2d-4eca-b774-76c352c03b87</a><br />
<br />
and then upgrading the project for 2012 using the instructions found here:<br />
<br />
<a href="http://msdn.microsoft.com/en-us/library/vstudio/jj991932.aspx">http://msdn.microsoft.com/en-us/library/vstudio/jj991932.aspx</a><br />
<br />
One note about the upgrade instructions… I ran into an issue with the GetDTE method in the Utilities class. I had to update it to the following in order to get everything to work:<br />
<br />
<pre class="brush:csharp">public static DTE2 GetDTE(object dataContext)
{
DataSource source = dataContext as DataSource;
foreach (IPropertyDescription property in source.Properties)
{
if (property.Name == "DTE")
{
return source.GetValue("DTE") as DTE2;
}
}
return null;
}</pre>
<br />
I’m not sure if it is the Service Packs I have installed for VS 2012, or what may be the cause, but this was the work around.<br />
<br />
Anyway, I have created a new Visual Studio 2012 Photo Start Page project on CodePlex. If you would like to use the project as a starting point for your start page, please feel free.<br />
<br />
<a href="http://vs2012photostartpage.codeplex.com/" target="_blank">vs2012photostartpage.codeplex.com</a><br />
<br />
Also, I’d appreciate any feedback surrounding the project or the Start Page itself.<br />
<br />
Finally, if you don’t want to mess with the project, and you just want to install the extension, either visit the extension project page here:<br />
<br />
<a href="https://visualstudiogallery.msdn.microsoft.com/a012051a-9bd8-4103-9c51-fd0738752d50" target="_blank">https://visualstudiogallery.msdn.microsoft.com/a012051a-9bd8-4103-9c51-fd0738752d50</a><br />
<br />
or just open the Extensions and Updates manager:<br />
<br />
<a href="http://lh5.ggpht.com/-B1jD3CRnBqA/UhvlvnjjkRI/AAAAAAAAAHA/EyQl0CIvM78/s1600-h/Capture1%25255B2%25255D.jpg"><img alt="Capture1" border="0" src="http://lh3.ggpht.com/-YGqqZk5m8sw/UhvlwCxrPsI/AAAAAAAAAHI/sw8PE04oW8U/Capture1_thumb.jpg?imgmax=800" height="153" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Capture1" width="244" /></a><br />
<br />
and search Online for “Photo Start Page”<br />
<br />
<a href="http://lh3.ggpht.com/-AaZ5D9Uco6A/Uhvlwx-YAiI/AAAAAAAAAHQ/3Wg_KdMeXAk/s1600-h/Capture2%25255B4%25255D.jpg"><img alt="Capture2" border="0" src="http://lh5.ggpht.com/-qbHrtEkA3xg/UhvlxnsfwzI/AAAAAAAAAHY/AAINfl8OGNk/Capture2_thumb%25255B2%25255D.jpg?imgmax=800" height="307" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Capture2" width="597" /></a><br />
<br />
When you find it, select “Download” and follow all of the prompts to install the extension. Once it installs, you will need to restart Visual Studio, and then go to the Tools –> Options menu and select the “Startup” configuration options.<br />
<br />
<a href="http://lh4.ggpht.com/-U9lS02WaoBE/UhvlyLPwB-I/AAAAAAAAAHg/qQPUsRfxdIw/s1600-h/Capture3%25255B4%25255D.jpg"><img alt="Capture3" border="0" src="http://lh6.ggpht.com/-OkjKOMRqs50/Uhvly6q7IzI/AAAAAAAAAHo/Zy4aG4bh3wE/Capture3_thumb%25255B2%25255D.jpg?imgmax=800" height="363" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Capture3" width="593" /></a><br />
<br />
Select the “Visual Studio 2012 Photo Start Page” from the “Customize Start Page” drop down list, select OK, and you are good to go!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com4tag:blogger.com,1999:blog-7177774576397608454.post-53732045140922648812013-08-16T01:07:00.001+04:002013-08-16T02:30:55.932+04:00SSIS and SharePoint List Sources with Lookup FieldsIf you are not familiar with the Microsoft SQL Server Community Samples: Integration Services site, then I recommend that you check it out:<br />
<br />
<a href="https://sqlsrvintegrationsrv.codeplex.com/">https://sqlsrvintegrationsrv.codeplex.com/</a><br />
<br />
The site contains excellent SSIS samples and documentation on how to build solutions for everyday problems using SSIS. One of the most useful samples covers using a SharePoint list as a data source. A special SSIS Data Adapter project has been created and packaged so that you can run an installer and then use SharePoint lists in your SSIS package. The project can be found here:<a href="http://lh4.ggpht.com/-j-7tjUxcUeQ/Ug1DCekp2KI/AAAAAAAAAFQ/LwXBYdTkWds/s1600-h/image%25255B3%25255D.png"><img align="right" alt="image" border="0" height="222" src="http://lh6.ggpht.com/-j2CW2_16bFg/Ug1DC3AF5NI/AAAAAAAAAFY/iHVzzMe2EPA/image_thumb%25255B1%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin: 15px 0px 15px 15px;" title="image" width="190" /></a><br />
<br />
<a href="https://sqlsrvintegrationsrv.codeplex.com/releases/view/17652">https://sqlsrvintegrationsrv.codeplex.com/releases/view/17652</a><br />
<br />
After following the installation procedure you end up with a SharePoint List Source and SharePoint List Destination in your toolbox.<br />
You can then use these sources and destinations in any Data Flow Task.<br />
<a href="http://lh4.ggpht.com/-rIN0p3axZLE/Ug1DDO5GIFI/AAAAAAAAAFg/5Tr_PWn_Tko/s1600-h/image%25255B7%25255D.png"><img align="left" alt="image" border="0" height="184" src="http://lh4.ggpht.com/-6JEb98JCncw/Ug1DDrIQnnI/AAAAAAAAAFo/yxZKaSiUvEA/image_thumb%25255B3%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin: 15px 15px 15px 0px;" title="image" width="244" /></a> <br />
<br />
While using the SharePoint List sources today I ran into an issue using a lookup field. Essentially, my package was copying data from a SharePoint list into a local database table. The data exposed by the List Source included the internal SharePoint lookup value.<a href="http://lh3.ggpht.com/-4o66NEViUyM/Ug1DDw5qAjI/AAAAAAAAAFw/hX6SB-M8K-I/s1600-h/image%25255B11%25255D.png"><img align="right" alt="image" border="0" height="212" src="http://lh6.ggpht.com/-N3k5JTFoRGI/Ug1DERsfoRI/AAAAAAAAAF4/BMi-_KamQIQ/image_thumb%25255B5%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin: 15px 0px 15px 15px;" title="image" width="244" /></a> Essentially, it was including the field type and the separator (in this case ‘string;#’), which I did not want in my table. <br />
<br />
There is an easy fix to remedy this situation though. <br />
<a name='more'></a><br />
All that is needed to strip out the unwanted data is to add a Derived Column data flow transformation to your data flow task after your SharePoint List source. <br />
<br />
<br />
<a href="http://lh3.ggpht.com/-E-TZV1qVjBM/Ug1DEnK3_YI/AAAAAAAAAGA/kpsfee0gJOQ/s1600-h/image%25255B24%25255D.png"><span style="color: #dd7700;"></span><img alt="image" border="0" height="283" src="http://lh3.ggpht.com/-NnSSpN708ro/Ug1DFOolm8I/AAAAAAAAAGI/mc7QZ0ZCy8Q/image_thumb%25255B14%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin: 15px auto;" title="image" width="439" /></a><br />
In the Derived Column transformation you will need to use a string expression to strip out the unwanted data.<br />
<br />
<a href="http://lh3.ggpht.com/-HOYkjKhsc0o/Ug1DFi5XmqI/AAAAAAAAAGQ/R0dqMJyE1ME/s1600-h/image%25255B22%25255D.png"><img alt="image" border="0" height="400" src="http://lh3.ggpht.com/-sv6RweVlfMM/Ug1DGFq96YI/AAAAAAAAAGY/qNVX4G5ymXM/image_thumb%25255B12%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="image" width="428" /></a> <br />
<div style="text-align: center;">
<pre class="brush:xml">TRIM( SUBSTRING( YOUR_COLUMN_NAME, FINDSTRING( YOUR_COLUMN_NAME, “;#”, 1 ) +2, 255) )
</pre>
</div>
With this simple transformation, I was able to cleanse my lookup data and transfer it to the database without issue.<br />
<br />
I’d love to hear if anyone uses a different technique when dealing with this situation.<br />
I hope this helps!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com2tag:blogger.com,1999:blog-7177774576397608454.post-82202809091115402872013-08-08T00:08:00.000+04:002013-08-08T00:08:25.241+04:00SharePoint RenderingTemplate Gotcha!Recently when working on a SharePoint 2010 project I let a minor mistake cause a great deal of frustration. I was creating a custom field that used a custom rendering template. I have done this before, but I guess on this day my mind was in WebForms land and I ended up with a solution that would deploy, but when I went to access my custom field, it would not render. <br />
<br />
Here is a picture of my template… can you see the problem?<br />
<br />
<a href="http://lh5.ggpht.com/-IN5eanXWozY/UgKoNlM0AxI/AAAAAAAAAEo/WSg64dk2o70/s1600-h/image%25255B7%25255D.png"><img alt="image" border="0" height="284" src="http://lh3.ggpht.com/-pAvQsiADFeo/UgKoOEJwW2I/AAAAAAAAAEw/TDGZWoU5Y4Y/image_thumb%25255B3%25255D.png?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline;" title="image" width="624" /></a> <br />
<br />
I had included the button OnClick call in the control markup. If you do this, your template will not render because it does not know how to wire up that event to it’s respective event handler. Take note of the Control declaration. Unlike normal ASP.Net User Controls, there is no reference to a Code Behind or Inherited Type… therefore… any events that need to be wired up will have to be done in your RenderingTemplateControl class file, and not in the markup!<br />
<br />
I hope this helps!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-92173232720378647952013-08-02T20:43:00.001+04:002013-08-08T00:41:44.650+04:00The ChangelingToday’s society is moving faster than ever, and much like the rapidly evolving technology that we depend on, we ourselves are evolving faster and faster. Not physical evolution mind you, but mental evolution. Our minds are under enormous pressure to adapt, change, and evolve in order to keep pace with the ever-changing landscape that is modern life. Technology is driving this change, and those people who work with technology are most influenced by this “Hyper-evolution”. <br />
The saying: <br />
<blockquote>
<em><b>“it’s not what you know, but who you know”</b></em></blockquote>
…is still true in many ways, but the new world order requires a new saying:<br />
<blockquote>
<em><b>“It’s not what you know, it’s what you can learn”</b></em><br />
<a name='more'></a><br />
<a href="http://www.blogger.com/blogger.g?blogID=7177774576397608454" name="more"></a></blockquote>
More than ever the ability to quickly pick up a new skill is the key to success in any technological field. Just as generations in the computer industry have shrunk from 5 year cycles to 3 year cycles, to 1 year cycles, down to now quarterly cycles (which can clearly be seen in the smart phone industry), the workers in these industries have also had to adjust their ability to learn new skills as quickly as the products they support. In this environment work that was considered “revolutionary” 6 months ago may now be considered stale. A good example of this is the extreme evolution is JavaScript templating frameworks. Many of the frameworks that I discovered only 6 months ago have been made completely obsolete by more highly integrated, more functional libraries. <br />
<br />
Because of this, I think we as individuals need to rethink our approach to our careers, our positions, and more specifically rethink our approach to how we learn. I think that traditional institutions like the 4 year college lose value when viewed in the light of “hyper-evolution”. Dedicating the time to get a Master’s or PHD will almost certainly result in an individual being behind the curve. Certifications may become more important if they can accurately prove merit. But, the only thing that will have true value is experience. When faced with new problems, the only way to “master” them in the microscopic time frame that has become the new normal will be to face them head on. Though this will be the most difficult approach to qualify in terms of resumes, job interviews, and recording long term achievement, it will certainly be the most valuable.<br />
<br />
So… in light of all of this progress, I say to you…<br />
<blockquote>
<em><b>Learn while you are doing. </b></em><br />
<em><b>Fake it until you make it.</b></em><br />
<em><b>Be an amateur… because there are no experts.</b></em></blockquote>
“Hyper-evolution” has arrived. But I am sure that by the time I have posted this… it will be old news. Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-23286064665590086752013-06-15T03:13:00.000+04:002013-08-08T00:25:07.023+04:00Creating JavaScript List Repositories for the SharePoint REST APII've recently been working on a project where I've been leveraging the SharePoint 2010 REST API. It is a great experience especially after moving from SharePoint 2007 client side development. I've been able to develop web parts that are 100% client driven with no reliance on any server side code other than the REST services that SharePoint already exposes.<br />
<br />
As I began developing I quickly noticed that calls to jQuery <strong>ajax</strong> and <strong>getJSON</strong> functions were littering my code. Of course, these calls are completely necessary, but I wanted to find a way to reduce duplication, increase reuse, and create a better programming experience for the rest of the effort. I decided to take these calls and create a wrapper object that would be responsible for the plumbing of calling my lists. What I ended up with was an object very similar to a traditional repository used in other languages.<br />
<a name='more'></a><br />
The object looks like this:<br />
<br />
<pre class="brush:js">var TCSC = TCSC || {};
TCSC.SharePoint = TCSC.SharePoint || {};
TCSC.SharePoint.ListRepository = function (listName) {
if (this instanceof TCSC.SharePoint.ListRepository) {
this.url = window.location.protocol + "//" + window.location.host + "/_vti_bin/ListData.svc/" + listName;
} else {
return new TCSC.SharePoint.ListRepository(listName);
}
};
TCSC.SharePoint.ListRepository.prototype = function () {
function addParameters(url, parameters) {
if (url !== undefined && url !== '' && parameters !== undefined && parameters !== '') {
return url + '?' + parameters;
} else {
return url;
}
function deleteItem(id) {
var itemUrl = this.url + "(" + id + ")";
return jQuery.ajax({
url: itemUrl,
type: "DELETE"
});
}
function insertItem(item) {
return jQuery.ajax({
url: this.url,
type: "POST",
contentType: 'application/json',
data: JSON.stringify(item)
});
}
function selectAll(parameters) {
var listUrl = addParameters(this.url, parameters);
return jQuery.getJSON(listUrl);
}
function selectById(id, parameters) {
var itemUrl = addParameters(this.url + "(" + id + ")", parameters);
return jQuery.getJSON(itemUrl);
}
function updateItem(item) {
var itemUrl = this.url + "(" + item.Id + ")";
var etag = item.ETag;
delete item["ETag"];
beforeSendFunction = function (xhr) {
xhr.setRequestHeader("If-Match", etag);
xhr.setRequestHeader("X-HTTP-Method", 'MERGE');
}
return jQuery.ajax({
url: itemUrl,
type: "POST",
contentType: 'application/json',
beforeSend: beforeSendFunction,
processData: false,
data: JSON.stringify(item)
});
}
return {
deleteItem: deleteItem,
insertItem: insertItem,
selectAll: selectAll,
selectById: selectById,
updateItem: updateItem
}
} ();
</pre>
<br />
Namespace objects are created to house our repository definitions. We then create a constructor function that takes the name of the SharePoint List that we want to work with and assign it to a local variable. We then use the revealing module pattern to create an object to assign to our ListRepositories prototype. The revealing module pattern is too deep of a subject to cover in this post, so here are some links that cover what it is all about:<br />
<br />
Module Pattern:<br />
<a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript">http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript</a><br />
<br />
Revealing Module Pattern:<br />
<a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript">http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript</a><br />
<a href="http://www.bobholt.me/2012/08/using-the-revealing-module-pattern/">http://www.bobholt.me/2012/08/using-the-revealing-module-pattern/</a><br />
<br />
The gist of why I decided to use the revealing module pattern is that I wanted to create a functional JavaScript object that provides all of the functionality that the repository needs, but hides all of the plumbing while exposes the necessary functions in a clean and simple manner. The revealing module pattern makes this possible.<br />
<br />
After assignment, the prototype object contains all of the functions that we want to expose for use in our repository.<br />
<br />
To utilize the repository it is as simple as “newing” up a new repository object and calling a function….<br />
<br />
<pre class="brush:js">var repository = new TCSC.SharePoint.ListRepository(“People”);
repository.selectAll().done(function(data) {
var people = data;
});
</pre>
<br />
In this case I'm intentionally returning the jQuery <strong>Deferred</strong> objects because they give you more control over your processing. I initially attempted to populate "properties" on the object, but I found that approach quickly got out of hand. Using the deferred objects allows you to chain your operations together while still taking advantage of all of the asynchronous goodness that JavaScript has to offer. Again, the Deferred object is too complex to go into detail, so here are some links that should provide some background:<br />
<br />
<a href="http://api.jquery.com/category/deferred-object/">http://api.jquery.com/category/deferred-object/</a><br />
<a href="http://api.jquery.com/jQuery.Deferred/">http://api.jquery.com/jQuery.Deferred/</a><br />
<a href="http://eng.wealthfront.com/2012/12/jquerydeferred-is-most-important-client.html">http://eng.wealthfront.com/2012/12/jquerydeferred-is-most-important-client.html</a><br />
<br />
<em>HINT: if you need to access a lists Choice Field using the REST API, you call the REST functions using the List Name and Field Name concatenated together (ex. List Name-Person, Choice Field Name-Gender --- call using PersonGender).</em><br />
<br />
I hope this helps!<br />
<br />
<em>Note: This posts revolves around the SharePoint 2010 REST API, but all of the concepts covered can be easily ported to SharePoint 2013.</em><br />
<br />
<em>Another Note: I recently had an interesting conversation with a colleague about whether or not SharePoint 2010's listdata.svc is an actual REST implementation. While it uses HTTP Methods and much of it's convention follows traditional REST API's, I think I agree with him that technically it is not a true REST API... at least not in the sense that the 2013 REST API is. But, that being said, much of the web refers to this functionality as the SharePoint 2010 REST API, and in this blog past, I am going to follow suit.</em>Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-29408315681963747042013-03-07T04:46:00.000+04:002013-08-08T00:51:53.884+04:00Validating your data and your feelings with IE 10 and HTML 5On a recent project I ran into an unexpected issue while testing with IE 10. The site that I was working on has out of the box ASP.Net validators and custom ASP.Net validators. When I navigated to the page that contains the validators and attempted to submit the form, multiple validators fired. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-c6N9Syda-Mk/UgFgggeiFnI/AAAAAAAAAEM/sPb1iR-PYHg/s1600/Validate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="126" src="http://3.bp.blogspot.com/-c6N9Syda-Mk/UgFgggeiFnI/AAAAAAAAAEM/sPb1iR-PYHg/s640/Validate.png" width="640" /></a></div>
<br />
<a name='more'></a>My initial thought was "what have I done now?" But, after reassuring myself that I had tested this page in multiple browsers before IE 10 and had not run into any problems, I decided to get to the bottom of why IE 10 was throwing multiple validation warnings when it should not be.<br />
<br />
The first give away to determining the issue is that the validation warning was not using the default validation styling that I had defined in the sites CSS. In the picture above, you can see that the text box with the "Offer or Group Code" watermark has a bright red square highlight around it. This was not something I had done… so… apparently IE 10 was applying some validation of its own. As it turns out, IE 10 was attempting to apply validation according to the new HTML5 form validation standards. This is interesting because this pages DOCTYPE is specifically targeting XHTML Transitional, so I would not expect this behavior to be applied. If I can find any specifics regarding IE 10's stance on that, I'll update this post.<br />
<br />
So now that I knew the issue was IE 10 attempting to validate according to the new HTML 5 form validation standard, the fix was easy. I simply added the "<span style="color: black; font-family: Verdana; font-size: 9pt;">novalidate"</span> attribute to the pages <form> tag and viola, no more multiple validation warnings. The ASP.Net validators did their job as expected, and everybody was happy!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-86472143226282920312012-06-28T03:50:00.000+04:002013-08-08T00:42:58.868+04:00No ELMAH, forget about it!If you haven't heard of ELMAH you need to check out the following links pronto!<br />
<br />
<a href="http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx" target="_blank" title="Scott Hanselman on ELMAH">ELMAH - Home</a><br />
<a href="http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx" target="_blank" title="Scott Hanselman on ELMAH">Scott Hanselman on ELMAH</a><br />
<br />
I highly recommend using ELMAH whenever you can when developing custom applications. There are very few things that you can "bolt-on" to your application that will provide so much functionality with so little effort.<br />
<br />
But what happens when you can't use ELMAH? If for some reason you can't use ELMAH in your application (like, for say... you work with an old school architect who just doesn't get it, and doesn't want to try to get it) then what do you do? What you do is forget about it, and write your own!<br />
<br />
<a name='more'></a>Error handling modules are extremely simple, and so very easy to add to an existing .Net application. All that you need to do to create an error handling module is to create a new class that inherits from IHttpModule and register your new module in your applications web.config.<br />
<br />
Here is an example error handling module class:<br />
<br />
<pre class="brush:csharp">namespace Web.ErrorHandling
{
public class ErrorHandlingModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
if (context != null)
context.Error += new EventHandler(OnError);
}
#endregion
#region Event Handlers
public void OnError(object source, EventArgs e)
{
Exception ex = HttpContext.Current.Server.GetLastError();
if (ex == null)
return;
ErrorHandler.LogException(ex);
}
}
}
</pre>
<br />
In this example I am calling an existing error handling class (ErrorHandler.LogException) that I already had in my project, but you can do whatever is appropriate for you your app.<br />
<br />
Once you've finished writing your class, all you have to do is register the class in your applications web.configsection:<br />
<br />
<pre class="brush:xml"><httpModules>
<add name="ErrorModule" type="Web.ErrorHandling.ErrorHandlingModule, Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31ecffd9ef3ffd3e" />
</httpModules>
</pre>
<br />
And viola! You have ELMAH like error capturing capability in your app. Forget about it!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com1tag:blogger.com,1999:blog-7177774576397608454.post-72871724074267475682012-06-27T03:54:00.000+04:002013-08-08T00:44:39.859+04:00Using Pete Montgomery’s Universal Predicate Builder with a generic repositoryI gave a presentation the other day that included a generic repository class that I created and use in many of my Entity Framework projects. I like the implementation because it is flexible and easy to extend. I've created a blog post about the RepositoryBase class here:<br />
<br />
<a href="http://www.viamacchina.com/2013/07/a-generic-repository-for-entity.html" target="_blank">A Generic Repository for the Entity Framework</a><br />
<br />
After the presentation I was asked how I utilized the repository's Select methods. In this case, the Select Method is overridden a few times to match the select pattern used by ASP.Net ObjectDataSources. Essentially, I can create a repository class that will map 1 to 1 to an ObjectDataSource to enable paging and sorting. <br />
<a name='more'></a>Here is the generic entity repository class from my other post (Pay attention to the Select methods): <br />
<br />
<pre class="brush:csharp">public abstract class GenericEntityRepository<T>
where T : class
{
#region Properties
public BaseRepository Repository
{
get
{
return Activator.CreateInstance(BaseRepositoryType) as BaseRepository;
}
}
public abstract Type BaseRepositoryType { get; }
#endregion
#region Methods
public virtual void Delete(T entity)
{
ExecuteQuery(query =>
{
query.Delete<T>(entity);
query.SaveChanges();
});
}
internal virtual void ExecuteQuery(Action<IRepository> query)
{
if (query == null)
throw new ArgumentException("The query argument can not be null.");
using (var queryRepository = Repository)
{
query(queryRepository);
}
}
public virtual void Insert(T entity)
{
ExecuteQuery(query =>
{
query.Insert<T>(entity);
query.SaveChanges();
});
}
public virtual IList<T> Select()
{
List<T> results = new List<T>();
ExecuteQuery(query =>
{
results = query.Select<T>().ToList();
});
return results;
}
public virtual IList<T> Select(int rowNumber, int pageSize)
{
// There is a bug in the EF stack that does not allow you to call the Skip()
// function without calling the OrderBy() function.
string defaultSort = StaticMethods.CreateSortExpression<T>(string.Empty, string.Empty);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().AsQueryable<T>().OrderBy(defaultSort).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(string sortByColumnName, int rowNumber, int pageSize)
{
if (string.IsNullOrEmpty(sortByColumnName))
return Select(rowNumber, pageSize);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().OrderBy(sortByColumnName, false).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where)
{
if (where == null)
return Select();
List<T> results = new List<T>();
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where, int rowNumber, int pageSize)
{
if (where == null)
return Select(rowNumber, pageSize);
// There is a bug in the EF stack that does not allow you to call the Skip()
// function without calling the OrderBy() function.
string defaultSort = StaticMethods.CreateSortExpression<T>(string.Empty, string.Empty);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).AsQueryable<T>().OrderBy(defaultSort).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where, string sortByColumnName, int rowNumber, int pageSize)
{
if (where == null)
return Select(sortByColumnName, rowNumber, pageSize);
if (string.IsNullOrEmpty(sortByColumnName))
return Select(where, rowNumber, pageSize);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).AsQueryable<T>().OrderBy(sortByColumnName, false).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual T Select(object key)
{
if (key == null)
throw new ArgumentNullException("key", "The key argument can not be null.");
T entity = null;
ExecuteQuery(query =>
{
entity = query.Select<T>(key);
});
return entity;
}
public virtual int RowCount()
{
int rowCount = -1;
ExecuteQuery(query =>
{
rowCount = query.Select<T>().Count();
});
return rowCount;
}
public virtual int RowCount(Expression<func<bool>> where)
{
if (where == null)
return RowCount();
int rowCount = -1;
ExecuteQuery(query =>
{
rowCount = query.Select<T>().Where<T>(where).Count();
});
return rowCount;
}
public virtual void Update(T entity)
{
ExecuteQuery(query =>
{
query.Update<T>(entity);
query.SaveChanges();
});
}
#endregion
}
</pre>
<br />
Notice that in the repository class I have an overload of the Select method that accepts an expression (Expression<Func<T, bool>>). This method allows you to pass an expression as a parameter, and the expression will be used to filter your Select query. Here is where the Predicate Builder comes in. Technically, you can use any method you want to create your expression, but I happen to like Pete Montgomery's implementation found here:<br />
<br />
<a href="http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/">http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/</a><br />
<br />
As he mention's in his post, this Predicate Builder class doesn't require strange calls to AsExpandable(), which is required if you use the famous "Albahari" PredicateBuilder.<br />
<br />
Armed with Pete's PredicateBuilder you can implement something like the following in a calling class:<br />
<br />
<pre class="brush:csharp">var s1 = PredicateBuilder.True<status>();
var s2 = PredicateBuilder.True<status>();
var whereClause = PredicateBuilder.True<status>();
s1 = s => s.Name == "Everett";
s2 = s => s.Active == true;
whereClause = s1.And(s2);
Status_Repository repository = new Status_Repository();
var statuses = repository.Select(whereClause);
</pre>
<br />
This expression will filter the linq result set to only return Status objects where the Name property equals "Everett" and the Active property is true.<br />
<br />
In my opinion this a great way to add flexibility to a repository solution and can help lead to better separation of concerns between application layers. This can also lead to more testable code when compared to other query filtering solutions like the Linq Dynamic library. <br />
<br />
I'd love to hear feedback on this solution as well as other uses that you have found for any of the PredicateBuilder solutions out there!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0tag:blogger.com,1999:blog-7177774576397608454.post-8693241235824171992012-06-13T03:59:00.000+04:002013-08-08T00:46:51.861+04:00A Generic Repository for the Entity Framework<em>Note: In this post I am going to be demonstrating techniques that depend on the Entity Framework 4.1 or higher.</em><br />
<br />
Recently I’ve been using a simple Repository oriented architecture for my Entity Framework projects. Coupled with the new <strong>DbContext</strong> class and the entity code generation items added in the Entity Framework 4.1 release you can quickly scaffold up a data layer and business layer for your application.<br />
<br />
To start, let’s define an interface for our repository:<br />
<br />
<pre class="brush:csharp">public interface IRepository : IDisposable
{
void Insert<E>(E entity) where E : class;
void Update<E>(E entity) where E : class;
void Delete<E>(E entity) where E : class;
IQueryable<E> Select<E>() where E : class;
E Select<E>(object key) where E : class;
int SaveChanges();
int SaveChanges(bool validateEntities);
}
</pre>
<br />
<a name='more'></a>This interface is pretty standard, defining all of the usual CRUD operations with the addition of the SaveChanges methods that can be mapped to Linq to SQL or EF data contexts. Next we will need to create the base repository class:<br />
<br />
<pre class="brush:csharp">public abstract class BaseRepository : IRepository
{
public abstract void Insert<E>(E entity) where E : class;
public abstract void Update<E>(E entity) where E : class;
public abstract void Delete<E>(E entity) where E : class;
public int SaveChanges()
{
return SaveChanges(true);
}
public abstract int SaveChanges(bool validateEntities);
public abstract IQueryable<E> Select<E>() where E : class;
public abstract E Select<E>(object key) where E : class;
public abstract void Dispose();
}
</pre>
<br />
This <strong>BaseRepository</strong> class is abstract so that we can create child repositories for Linq to SQL, EF, and any other data provider we need. We will hide the explicit implementation from our callers using this class.<br />
<br />
Now we have to create an generic repository implementation for our data layer, which in this case is an Entity Framework layer that uses the new <strong>DbContext</strong> object.<br />
<br />
<pre class="brush:csharp">public class DbContextRepository<T> : BaseRepository where T : DbContext
{
#region Fields
private T _context;
private Type _contextType;
#endregion
#region Properties
public T Context
{
get
{
if (_context == null)
_context = Activator.CreateInstance<T>();
return _context;
}
}
#endregion
#region Methods
public override void Insert<E>(E entity)
{
Context.Set<E>().Add(entity);
}
public override void Update<E>(E entity)
{
Context.Set<E>().Attach(entity);
Context.Entry<E>(entity).State = EntityState.Modified;
}
public override void Delete<E>(E entity)
{
Context.Set<E>().Attach(entity);
Context.Entry<E>(entity).State = EntityState.Deleted;
}
public override int SaveChanges(bool validateEntities)
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception("The database is currently not online.");
Context.Configuration.ValidateOnSaveEnabled = validateEntities;
return Context.SaveChanges();
}
public override IQueryable<E> Select<E>()
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception("The database is currently not online.");
return Context.Set<E>();
}
public override E Select<E>(object key)
{
if (!Context.Database.Connection.IsDatabaseOnline())
throw new Exception("The database is currently not online.");
return Context.Set<E>().Find(key);
}
public override void Dispose()
{
if (Context != null)
Context.Dispose();
}
#endregion
}
</pre>
<br />
This generic implementation is a little bit more complex, so let’s break it down.<br />
<br />
First, this is a generic repository implementation for contexts that inherit from <strong>DbContext</strong>. We want to make this class generic so that we can use any data context that we may create, not just a specific context that we may "hardcode" into the class.<br />
<br />
Every method in this class uses the our EF data context, so we need to expose it. We are going to add a private property called Context of type <strong>T</strong>. Our context object is instantiated using the Reflection Activator.CreateInstance method. The overload that we are using to instantiate our object requires a Generic Type parameter, so we add that to the method call, Activator.CreateInstance<T>().<br />
<br />
Next we need to implement our <strong>BaseRepository</strong> abstract methods. We do this by adding the appropriate methods mapping them to the <strong>DbContext</strong>’s object Set collection operations.<br />
<br />
Finally we implement <strong>IDisposable</strong> to satisfy the <strong>IDisposable</strong> interface.<br />
<br />
So, from here we have the infrastructure to finally implement concrete repository classes. There are many approaches you can use, but I am going to outline one that I have used for ASP.Net based website projects. <em></em><br />
<br />
<em>Disclaimer: This implementation is heavily tied to WebForms and OOTB Microsoft Web Controls, so please do not think that this is the only way to proceed.</em><br />
<br />
So, I know I just said that we can finally implement concrete repository classes, but I am going to throw one more generic abstract class into the mix for good luck. For me, the purpose of this class is to consolidate business logic that may be central to the technology you have decided to use in your application.<br />
<br />
For instance, in this example, I am implementing methods that will map to ObjectDataSources that I am using in my presentation layer. So, when you see Select() methods with multiple overloads for paging, sorting, and filtering, or RowCount() methods, this has been done to accommodate ObjectDataSources that may use one of these repository objects as a datasource.<br />
<br />
Again, if you were using MVC for your presentation layer, or WinForms or webservices, you may implement this class differently.<br />
<br />
<pre class="brush:csharp">public abstract class GenericEntityRepository<T>
where T : class
{
#region Properties
public BaseRepository Repository
{
get
{
return Activator.CreateInstance(BaseRepositoryType) as BaseRepository;
}
}
public abstract Type BaseRepositoryType { get; }
#endregion
#region Methods
public virtual void Delete(T entity)
{
ExecuteQuery(query =>
{
query.Delete<T>(entity);
query.SaveChanges();
});
}
internal virtual void ExecuteQuery(Action<IRepository> query)
{
if (query == null)
throw new ArgumentException("The query argument can not be null.");
using (var queryRepository = Repository)
{
query(queryRepository);
}
}
public virtual void Insert(T entity)
{
ExecuteQuery(query =>
{
query.Insert<T>(entity);
query.SaveChanges();
});
}
public virtual IList<T> Select()
{
List<T> results = new List<T>();
ExecuteQuery(query =>
{
results = query.Select<T>().ToList();
});
return results;
}
public virtual IList<T> Select(int rowNumber, int pageSize)
{
// There is a bug in the EF stack that does not allow you to call the Skip()
// function without calling the OrderBy() function.
string defaultSort = StaticMethods.CreateSortExpression<T>(string.Empty, string.Empty);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().AsQueryable<T>().OrderBy(defaultSort).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(string sortByColumnName, int rowNumber, int pageSize)
{
if (string.IsNullOrEmpty(sortByColumnName))
return Select(rowNumber, pageSize);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().OrderBy(sortByColumnName, false).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where)
{
if (where == null)
return Select();
List<T> results = new List<T>();
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where, int rowNumber, int pageSize)
{
if (where == null)
return Select(rowNumber, pageSize);
// There is a bug in the EF stack that does not allow you to call the Skip()
// function without calling the OrderBy() function.
string defaultSort = StaticMethods.CreateSortExpression<T>(string.Empty, string.Empty);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).AsQueryable<T>().OrderBy(defaultSort).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual IList<T> Select(Expression<func<bool>> where, string sortByColumnName, int rowNumber, int pageSize)
{
if (where == null)
return Select(sortByColumnName, rowNumber, pageSize);
if (string.IsNullOrEmpty(sortByColumnName))
return Select(where, rowNumber, pageSize);
List<T> results = null;
ExecuteQuery(query =>
{
results = query.Select<T>().Where<T>(where).AsQueryable<T>().OrderBy(sortByColumnName, false).Skip(rowNumber).Take(pageSize).ToList();
});
return results;
}
public virtual T Select(object key)
{
if (key == null)
throw new ArgumentNullException("key", "The key argument can not be null.");
T entity = null;
ExecuteQuery(query =>
{
entity = query.Select<T>(key);
});
return entity;
}
public virtual int RowCount()
{
int rowCount = -1;
ExecuteQuery(query =>
{
rowCount = query.Select<T>().Count();
});
return rowCount;
}
public virtual int RowCount(Expression<func<bool>> where)
{
if (where == null)
return RowCount();
int rowCount = -1;
ExecuteQuery(query =>
{
rowCount = query.Select<T>().Where<T>(where).Count();
});
return rowCount;
}
public virtual void Update(T entity)
{
ExecuteQuery(query =>
{
query.Update<T>(entity);
query.SaveChanges();
});
}
#endregion
}
</pre>
<br />
The key to this class is the BaseRepositoryType abstract property. We will implement this property in concrete child classes. We will set the BaseRepository type to something like:<br />
<br />
<pre class="brush:csharp">typeof(DbContextRepository<OrderDbContext>);
</pre>
<br />
When called by the Activator.CreateInstance() method in getter of the Repository property, we will get a <strong>DbContextRepository</strong> object of Type <strong>OrderDbContext</strong>. Setting the BaseRepositoryType property is what allows us to switch Repository providers, and the specific contexts they may use.<br />
<br />
We can then leverage our <strong>BaseRepository</strong> object in all of our business logic by encapsulating it in the ExecuteQuery() method. This method takes an Action delegate as a parameter and allows us to use that <strong>BaseRepository</strong> object by simply calling:<br />
<br />
<pre class="brush:csharp">ExecuteQuery(query =>
{
//Place repository calls here.
});
</pre>
<br />
The query lambda syntax simplifies the need to declare and register a delegate, and ultimately leads to clean, readable code.<br />
<br />
The final piece to this equation is the concrete implementation to our repository. But, this is also the great part about this model. After all of this coding, we can create repository objects for any Type and any <strong>DbContext</strong> that we may have defined in our data layer by creating a simple class that inherits from the <strong>GenericEntityRepository</strong> class, and implements one property.<br />
<br />
<pre class="brush:csharp">public partial class OrderRepository : GenericEntityRepository<Order>
{
public override Type BaseRepositoryType
{
get
{
return typeof(DbContextRepository<OrderDbContext>);
}
}
}
</pre>
<br />
In this example, we have a <strong>DbContext</strong> type defined in our data layer named <strong>OrderDbContext</strong>, and a Type named <strong>Order</strong>. After implementing this class, we will have a <strong>OrderRepository</strong> object that we can use to Select, Insert, Update, and Delete <strong>Order</strong> objects from the <strong>OrderDbContext</strong>.<br />
<br />
With this pattern in place, we can save ourselves some time and some typing by placing our concrete class implementation into a T4 template. When pointed at an Entity Framework EDMX file, the template will auto generate these concrete repository classes for all of the tables defined in the EDMX file.<br />
<br />
I hope that you have found this pattern helpful, and I encourage you to send me feedback if you any ideas or improvements on this topic. Thanks!Everett Comstockhttp://www.blogger.com/profile/05679666580876010014noreply@blogger.com0