<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Kevin Miller</title><link>http://blogs.dovetailsoftware.com/blogs/kmiller/default.aspx</link><description /><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.2)</generator><geo:lat>30.406169</geo:lat><geo:long>-97.757438</geo:long><image><link>http://blogs.dovetailsoftware.com/blogs/kmiller/</link><url>http://farm2.static.flickr.com/1075/706129937_1a06f9f816_o.png</url><title>Kevin Miller - Early Onset</title></image><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/KevinMiller" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>Introducing Dovetail DataMap</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/ju_q6JqFoXA/introducing-dovetail-datamap.aspx</link><pubDate>Mon, 27 Apr 2009 15:46:29 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11737</guid><dc:creator>kmiller</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11737.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11737</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11737</wfw:comment><description>&lt;p&gt;&lt;img style="display:inline;margin-left:0px;margin-right:0px;" align="right" src="http://mkweb.bcgsc.ca/circos/tableviewer/images/data-view.png" width="240" height="240" /&gt;I want to introduce a handy library called &lt;strong&gt;Dovetail DataMap &lt;/strong&gt;created during the development of &lt;a href="http://www.dovetailsoftware.com/solutions/ClarifyMobile.aspx"&gt;Dovetail Mobile&lt;/a&gt;. DataMap makes it easy to populate model objects from a Clarify/Dovetail CRM database. It is a sort of one-way Clarify specific object relational mapper tool.&lt;/p&gt;  &lt;h3&gt;Your CRM data to Objects&lt;/h3&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;It is very common to need a way to pull data out of your CRM for creation of a user interface. What if you could define a &lt;strong&gt;plain old C# object&lt;/strong&gt; to receive data and &lt;strong&gt;a map&lt;/strong&gt; which defines how data in your Clarify database should populate that .Net class? Does this sound better than hand rolling your own code every time? I hope it does. Let’s take a look at a scenario.&lt;/p&gt;  &lt;h4&gt;A Plain Old C# Object&lt;/h4&gt;  &lt;p&gt;Let’s say you want to create a user interface that displays solutions. First thing you do is create a class with a public property for each piece of data about the solution you wish to display.&lt;/p&gt;  &lt;pre class="brush: csharp; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;"&gt;public class Solution
{
	public string ID { get; set; }
	public DateTime Created { get; set; }
	public string Title { get; set; }
	public string Description { get; set; }
	public bool IsPublic { get; set; }
}&lt;/pre&gt;

&lt;h4&gt;A Data Map&lt;/h4&gt;

&lt;p&gt;Next up you define a map for the Solution object that describes where the data for each Solution property comes from.&lt;/p&gt;

&lt;pre class="brush: csharp; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;"&gt;public class SolutionMap : DovetailMap&amp;lt;Solution&amp;gt;
{
	protected override void MapDefinition()
	{
		FromTable(&amp;quot;probdesc&amp;quot;)
			.Assign(d =&amp;gt; d.ID).FromIdentifyingField(&amp;quot;id_number&amp;quot;)
			.Assign(d =&amp;gt; d.Title).FromField(&amp;quot;title&amp;quot;)
			.Assign(d =&amp;gt; d.Description).FromField(&amp;quot;description&amp;quot;)
			.Assign(d =&amp;gt; d.Created).FromField(&amp;quot;creation_time&amp;quot;)
			.Assign(d =&amp;gt; d.IsPublic).BasedOnField(&amp;quot;public_ind&amp;quot;).Do(isPublic =&amp;gt; isPublic == &amp;quot;1&amp;quot;)
	}
}&lt;/pre&gt;

&lt;p&gt;In the map above you are seeing what is called a &lt;a href="http://www.martinfowler.com/bliki/FluentInterface.html"&gt;Fluent Interface&lt;/a&gt;. The idea is to make this map very readable while being easy to edit and extend. Hopefully after taking a few seconds to look between the Solution class and this map you’ll understand what is happening. &lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What table is the solution map getting its data from? The Solution object maps to the &lt;strong&gt;probdesc&lt;/strong&gt; database table. &lt;/li&gt;

  &lt;li&gt;Which field is the identifying field? The &lt;strong&gt;id_number&lt;/strong&gt; field maps to the ID property and is the identifying field. &lt;/li&gt;

  &lt;li&gt;When is the &lt;strong&gt;IsPublic&lt;/strong&gt; property true? When the public_ind field is “1”. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The map above acts as the glue between the Solution class and another object called an assembler.&lt;/p&gt;

&lt;h4&gt;Assembling Solutions &lt;/h4&gt;

&lt;p&gt;The assembler does all the hard repetitive ookie work for you. It does stuff like querying and retrieving data from the database, and with that data, creates Solution objects. The best part is all you have to do is create one and tell it which solutions you want. &lt;/p&gt;

&lt;h5&gt;Getting a solution by identifier &lt;/h5&gt;

&lt;pre class="brush: csharp; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;"&gt;//Use an assembler to retrieve a solution by id. 
string solutionID = &amp;quot;124&amp;quot;;
Solution solution = solutionAssembler.GetOne(solutionID));&lt;/pre&gt;

&lt;p&gt;Remember the identifying field in the map above? When the assembler is getting one Solution filtering by the &lt;strong&gt;id_number &lt;/strong&gt;field. Here is a view of the Solution object from the debugger.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/image_4E5736D8.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_thumb_549E0D66.png" width="729" height="173" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;

&lt;h5&gt;Solutions created in the last week&lt;/h5&gt;

&lt;pre class="brush: csharp; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;"&gt;Solution[] solutions = solutionAssembler.Get(FilterType.WithinDays(&amp;quot;creation_time&amp;quot;, 7));&lt;/pre&gt;

&lt;p&gt;Getting solutions from the assembler in an ad hoc manner requires that you give the assembler criteria about which solutions you want returned. Take a look at the documentation for &lt;a href="http://www.dovetailsoftware.com/user_guides/dovetailSDK/2.4.0.8316/html/fcSDK~FChoice.Foundation.Filters.FilterType_members.html"&gt;all the types of filters&lt;/a&gt; you can use and at &lt;a href="http://www.dovetailsoftware.com/user_guides/dovetailSDK/2.4.0.8316/html/fcsdk_advancedfiltering.html"&gt;Dovetail SDK advanced filtering&lt;/a&gt; if you want to learn how to chain these filters together.&lt;/p&gt;

&lt;h3&gt;DataMaps In Production&lt;/h3&gt;

&lt;p&gt;How would you use something like this? The Solution class we are using here is very similar to what Dovetail Mobile is using. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/image_583C2843.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_thumb_7E9E0B8E.png" width="559" height="358" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Our Dovetail Mobile product is using the new &lt;a href="http://www.asp.net/mvc/"&gt;ASP.Net Model View Controller&lt;/a&gt; (MVC) framework. We use the DataMaps library to create the Model part of this equation. Here is an example of all the work our Mobile solutions controller needs to create the screen you see above.&lt;/p&gt;

&lt;pre class="brush: csharp; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; ruler: false; smart-tabs: true; tab-size: 4; toolbar: true;"&gt;public ActionResult Show(string id)
{
	var solutionViewModel = _solutionAssembler.GetOne(id);

	return View(&amp;quot;ShowSolution&amp;quot;, solutionViewModel);
}&lt;/pre&gt;

&lt;p&gt;DataMaps makes doing the data retrieval for this controller action quite simple.&lt;/p&gt;

&lt;h3&gt;Finally – What do you think?&lt;/h3&gt;

&lt;p&gt;Are you a Dovetail SDK customer? Can you see how Dovetail DataMap might be handy in your application? Do you want to know more? I’ve just hit the surface with this post. We want to see if there is more interest before dedicating more time and resources to making something like this available. Post a reply or &lt;a href="http://twitter.com/kevm/"&gt;send me a Tweet&lt;/a&gt; with what you think.&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11737" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/ju_q6JqFoXA" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/fcSDK/default.aspx">fcSDK</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/dovetail/default.aspx">dovetail</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/mobile+agent/default.aspx">mobile agent</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/datamap/default.aspx">datamap</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/04/27/introducing-dovetail-datamap.aspx</feedburner:origLink></item><item><title>Syntax Highligher</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/4IPikdpkidw/syntax-highligher.aspx</link><pubDate>Fri, 24 Apr 2009 13:40:51 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11735</guid><dc:creator>kmiller</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11735.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11735</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11735</wfw:comment><description>&lt;p&gt;This is a sample post using Alex’s very nice looking &lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter"&gt;Syntax Highlighter&lt;/a&gt; for code snippets. Let’s see if I can get Community Server to behave.&lt;/p&gt;  &lt;h3&gt;Teaser Code&lt;/h3&gt;  &lt;pre class="brush:csharp"&gt;public class SolutionMap : DovetailMap
{
	protected override void MapDefinition()
	{
		FromTable(&amp;quot;probdesc&amp;quot;)
			.Assign(d =&amp;gt; d.SolutionID).FromIdentifyingField(&amp;quot;id_number&amp;quot;)
			.MapMany().To(d =&amp;gt; d.Resolutions).ViaRelation(&amp;quot;probdesc2workaround&amp;quot;, workaround =&amp;gt; workaround
				.Assign(d =&amp;gt; d.DatabaseIdentifier).FromField(&amp;quot;objid&amp;quot;)
			);
	}
}&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Looks like I got it working. Sorry about the guinea pig post. The code above is actually something I have been wanting to post about for some time. Maybe now with these super sweet code blocks I’ll get’er done.&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11735" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/4IPikdpkidw" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/technology/default.aspx">technology</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/04/24/syntax-highligher.aspx</feedburner:origLink></item><item><title>Installing Visual SVN Server</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/uebSTWI4-a4/installing-visual-svn-server.aspx</link><pubDate>Wed, 15 Apr 2009 18:59:48 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11723</guid><dc:creator>kmiller</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11723.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11723</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11723</wfw:comment><description>&lt;p&gt;After being forced to re-install a Apache hosted Subversion service I decided to use &lt;a href="http://www.visualsvn.com/server/"&gt;Visual SVN Server&lt;/a&gt; and found the process of migrating from a existing multi-repository Apache Subversion service to be quite straight forward. I did learn a few things along the way that I wanted to share.&lt;/p&gt;  &lt;h3&gt;Why I Had to do it. (a.k.a Whoops!)&lt;/h3&gt;  &lt;p&gt;I did a dumb thing. I attempted to upgrade the version of our own existing Apache Subversion service in the middle of the day. You likely know how the rest goes, terribly. I Backed up all the files I was going to touch and dropped in the new .dlls in the right places and viola upgrade! right?. Not so much. The server didn’t start. So I reverted to my backups and the server still didn’t start. &lt;strong&gt;Oh noes!!!111one&lt;/strong&gt;. Apache puked and people started to complain that they couldn’t commit. &lt;/p&gt;  &lt;p&gt;The repositories were backed up and intact so I was not worried about losing data just getting the service back up. I had wanted to move to Visual SVN Server since they announced so now I had my chance.&lt;/p&gt;  &lt;h3&gt;&lt;img src="http://www.visualsvn.com/images/VisualSVN_125x37.gif" /&gt;&lt;/h3&gt;  &lt;h3&gt;Installation&lt;/h3&gt;  &lt;p&gt;The installer was quite easy to use and defaults to installing a secure self-signed Apache hosted Subversion web server. There is a wonderful MMC Snap-in that you use to manage the installation. Don’t worry. You can figure it out. They made it easy.&lt;/p&gt;  &lt;h4&gt;User and Groups Migration&lt;/h4&gt;  &lt;p&gt;We had pre-existing groups and users files in our repository root folder. All I had to do was rename the group file to &lt;strong&gt;authz&lt;/strong&gt; and the user file to &lt;strong&gt;htpasswd&lt;/strong&gt;. Unfortunately the hash used in the htpasswd file was different than our previous one so our users had to reset their passwords. This was only a minor pain.&lt;/p&gt;  &lt;h4&gt;SSL Certificate&lt;/h4&gt;  &lt;p&gt;We have a SSL certificate we use to secure our Subversion server. To get our pre-existing SSL certificate to be used by Visual SVN Server I had to combine our &lt;strong&gt;.key&lt;/strong&gt; and &lt;strong&gt;.cer&lt;/strong&gt; files into one file called &lt;strong&gt;server.pem &lt;/strong&gt;found in &lt;em&gt;C:\Program Files\VisualSVN Server\conf&lt;/em&gt;&lt;/p&gt;  &lt;h4&gt;Hosting Custom Web Content&lt;/h4&gt;  &lt;p&gt;Your Visual SVN Server has a nice front end to your repository that authenticates and allows users to navigate source code. It is easy to customize the look and feel of this code browser by editing the images or css that is in your Visual SVN Server configuration directory. Found in: &lt;em&gt;C:\Program Files\VisualSVN Server\htdocs&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;We have a post commit hook which generates an RSS with details about each commit. This feed file needs to be served up by the web server. Easy, just copy the file somewhere under the &lt;strong&gt;htdocs &lt;/strong&gt;directory and all is good. &lt;/p&gt;  &lt;h4&gt;Speaking of Hooks&lt;/h4&gt;  &lt;p&gt;The management snap-in has a nice UI for editing your SVN hooks batch file: &lt;em&gt;Repository –&amp;gt; All Tasks&amp;#160; -&amp;gt; Manage Hooks&lt;/em&gt;.&lt;/p&gt;  &lt;h3&gt;Conclusion&lt;/h3&gt;  &lt;p&gt;My hat is off to he Visual SVN team for giving the community this excellent &lt;strong&gt;free&lt;/strong&gt; tool for setting up and managing a Subversion server. Thank You.&lt;/p&gt;  &lt;p&gt;I am sure their strategy in this is to grow a community of users who will pay for their excellent &lt;a href="http://www.visualsvn.com/visualsvn/"&gt;Visual SVN&lt;/a&gt; tool. I’ve been a user of Visual SVN for many years and have nothing bad to say about it. It just makes using Subversion flow naturally. They keep it up-to-date, and they keep adding features What else do you need? Apparently, you’ll need 49$ for a license but I tell you it is very much worth it.&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11723" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/uebSTWI4-a4" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/subversion/default.aspx">subversion</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/04/15/installing-visual-svn-server.aspx</feedburner:origLink></item><item><title>Can your CRM place a phone call?</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/PJSn5E1SOXs/can-your-crm-place-a-phone-call.aspx</link><pubDate>Mon, 13 Apr 2009 17:30:59 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11707</guid><dc:creator>kmiller</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11707.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11707</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11707</wfw:comment><description>&lt;p&gt;When new cases come in we have many ways of having our support team notified so we can provide speedy assistance to our customers. &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;We get an email &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2008/05/05/having-rulemanager-send-sms-notifications-via-twitter.aspx"&gt;Twitter sends us direct messages&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2008/02/13/more-campfire-integration.aspx"&gt;Our campfire (chat) gets notified&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Recently we needed a better way to notify on-call support agents of new cases. Our existing notification mechanisms work great during office hours but at night a spam email getting past my filters better not wake me up at 2am just because it might be a new support case. We needed something better. The simplest thing we could think of was to have our CRM &lt;strong&gt;make a phone call&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;Phone calls are cross platform. No special smart phone client application beats a phone call. Better yet spammers don’t place calls&amp;#160; at 2am pitching “Enlargement”. At night I can confidently leave my phone volume set to 11 with a screeching &lt;a href="http://www.youtube.com/watch?v=gBRBaRXZ908"&gt;Ride of the Valkyries&lt;/a&gt; ring tone and I sleep in peace knowing that &lt;strong&gt;if my phone rings at 2am it is truly important. &lt;/strong&gt;Or else I should get friends that don’t drunk dial me. Stop it guys. You know who you are.&lt;/p&gt;  &lt;h3&gt;What I Want To Happen&lt;/h3&gt;  &lt;p&gt;We need a rule in our CRM system that places a phone call to the current on-call support agent when an Urgent or High priority case comes in. &lt;/p&gt;  &lt;p&gt;We already have Rulemanager which we have talked about &lt;a href="http://blogs.dovetailsoftware.com/search/SearchResults.aspx?q=rulemanager"&gt;many times before&lt;/a&gt;. The new thing here is having a rule action that places the phone call. Kudos and bonus points if it could say something catchy like “There is a new Urgent or High priority support case.” The Agent receiving the call at this point can use our &lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/01/23/mobile-agent-1-1-now-with-better-access-to-your-knowledge.aspx"&gt;Dovetail Mobile&lt;/a&gt; web application to work the case.&lt;/p&gt;  &lt;h3&gt;What Didn't Work&lt;/h3&gt;  &lt;p&gt;We don’t have a fancy PBX or the Telephony skills and hardware to setup our own &lt;a href="http://www.asterisk.org/"&gt;Asterisk server&lt;/a&gt;.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.skype.com/"&gt;Skype&lt;/a&gt; seemed like a good inexpensive solution as they have Skype Out and a &lt;a href="https://developer.skype.com/Docs/Skype4COM"&gt;Skype4Com API&lt;/a&gt; that seems pretty nice. Sadly my attempts to get the Skype API working were only intermittently successful and I have learned to avoid COM whenever possible so I moved on. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.jajah.com/"&gt;Jajah&lt;/a&gt; looked interesting. &lt;a href="http://www.ribbit.com/"&gt;Ribbit&lt;/a&gt; I just don’t understand. &lt;a href="http://www.cdyne.com/products/phone-notify.aspx"&gt;PhoneNotify&lt;/a&gt; seemed perfect but expensive and the API looks a little klunky.&lt;/p&gt;  &lt;h3&gt;Then I Found Web + Telephony Simplicity &lt;/h3&gt;  &lt;p&gt;&lt;a href="http://www.twilio.com/"&gt;Twilio&lt;/a&gt; is a telephony web service that is simple to use, quite powerful, and priced very reasonably. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.twilio.com/"&gt;&lt;img title="Twilio Homepage" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="481" alt="Twilio Homepage" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_2280B156.png" width="644" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Our &lt;strong&gt;make a phone call &lt;/strong&gt;requirement appears to be Twilio’s most basic capability. To do this essentially all you need to do is call the &lt;a href="http://www.twilio.com/docs/api_reference/REST/making_calls"&gt;Calls Rest API&lt;/a&gt; giving it a number to call and the public URL of a script which will control the conversation.&lt;/p&gt;  &lt;h4&gt;Start A Call&lt;/h4&gt;  &lt;p&gt;To have our CRM place a call we need to have Rulemanager run a console application giving it a number of the current on-call agent. To do this we created a rule whose action calls a console application passing it a phone number as the first argument.&lt;/p&gt;  &lt;p&gt;This &lt;strong&gt;Support-Dialer&lt;/strong&gt; console application will invoke the Twilio Calls Rest API. Twilio makes it easy for .Net developers by having a thin C# &lt;a href="http://www.twilio.com/docs/libraries/"&gt;helper library&lt;/a&gt; with good examples. Here are the important bits of the Support-Dialer console application.&lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Main(&lt;span class="kwrd"&gt;string&lt;/span&gt;[] args)&lt;/pre&gt;

  &lt;pre&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var logger = GetLogger();&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt;(args.Length &amp;lt; 1 )&lt;/pre&gt;

  &lt;pre&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; message = &lt;span class="str"&gt;&amp;quot;Could not dial number as no arguments were supplied&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;        logger.ErrorFormat(message);&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    var phoneNumberToCall = args[0];&lt;/pre&gt;

  &lt;pre class="alt"&gt;    logger.InfoFormat(&lt;span class="str"&gt;&amp;quot;Calling {0}.&amp;quot;&lt;/span&gt;, phoneNumberToCall);&lt;/pre&gt;

  &lt;pre&gt;    &lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// Create Twilio REST account object using Twilio account ID and token&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;    var account = &lt;span class="kwrd"&gt;new&lt;/span&gt; Account(AccountSid, AccountToken);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="rem"&gt;// Initiate a new outbound call &lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var restCallUrl = String.Format(&lt;span class="str"&gt;&amp;quot;/{0}/Accounts/{1}/Calls&amp;quot;&lt;/span&gt;, ApiVersion, AccountSid);&lt;/pre&gt;

  &lt;pre&gt;    var parameters = &lt;span class="kwrd"&gt;new&lt;/span&gt; Hashtable&lt;/pre&gt;

  &lt;pre class="alt"&gt;        {&lt;/pre&gt;

  &lt;pre&gt;            {&lt;span class="str"&gt;&amp;quot;Caller&amp;quot;&lt;/span&gt;, DovetailSupportCallerId},&lt;/pre&gt;

  &lt;pre class="alt"&gt;            {&lt;span class="str"&gt;&amp;quot;Called&amp;quot;&lt;/span&gt;, phoneNumberToCall},&lt;/pre&gt;

  &lt;pre&gt;            {&lt;span class="str"&gt;&amp;quot;Url&amp;quot;&lt;/span&gt;, SupportDialerConversationUrl},&lt;/pre&gt;

  &lt;pre class="alt"&gt;            {&lt;span class="str"&gt;&amp;quot;Method&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;}&lt;/pre&gt;

  &lt;pre&gt;        };&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var requestResult = account.request(restCallUrl, &lt;span class="str"&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;, parameters);&lt;/pre&gt;

  &lt;pre&gt;    logger.Debug(requestResult);&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;All this really does is make a web request to Twilio’s Calls Rest API giving it the number to call, the callerId of the number the call should appear to come from, and the URL of the conversation script XML.&lt;/p&gt;

&lt;h4&gt;Scripting the Conversation&lt;/h4&gt;

&lt;p&gt;Once a call is in progress Twilio needs to know what to do. Twilio has an &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/"&gt;XML markup&lt;/a&gt; for controlling what happens during a call. In our case all I want to happen is that the support agent is made aware of a new urgent case so this static XML does the trick. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&amp;lt;?xml version=&lt;span class="str"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; encoding=&lt;span class="str"&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt; ?&amp;gt;  &lt;/pre&gt;

  &lt;pre&gt;&amp;lt;Response&amp;gt;  &lt;/pre&gt;

  &lt;pre class="alt"&gt;    &amp;lt;Say loop=&lt;span class="str"&gt;&amp;quot;3&amp;quot;&lt;/span&gt;&amp;gt;There &lt;span class="kwrd"&gt;is&lt;/span&gt; a &lt;span class="kwrd"&gt;new&lt;/span&gt; urgent or high priority support &lt;span class="kwrd"&gt;case&lt;/span&gt;. Please check Dovetail mobile.&amp;lt;/Say&amp;gt;  &lt;/pre&gt;

  &lt;pre&gt;&amp;lt;/Response&amp;gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If we wanted to we could do more. Have the script &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/say"&gt;Say&lt;/a&gt; details of the case to or allow the agent to &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/dial"&gt;Dial&lt;/a&gt; the customer contact immediately. &lt;/p&gt;

&lt;h4&gt;Wrap Up&lt;/h4&gt;

&lt;p&gt;I am very impressed with Twilio. Given my use case was very simple I went from knowing nothing about Twilio to having an integration with our enterprise CRM completed in about 3 hours doing everything from getting an account setup and charged to leveraging their example code and integrating it with our business processes. Their website is a great resource for learning how to use the Twilio service with plenty of examples to get you going. &lt;/p&gt;

&lt;p&gt;Looking at the 5 primary verbs &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/say"&gt;Say&lt;/a&gt;, &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/play"&gt;Play&lt;/a&gt;, &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/gather"&gt;Gather&lt;/a&gt;, &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/record"&gt;Record&lt;/a&gt;, and &lt;a href="http://www.twilio.com/docs/api_reference/TwiML/dial"&gt;Dial&lt;/a&gt; has my brain spinning with ideas for where we could be using Twilio to create great customer experiences when the web is just not good enough.&lt;/p&gt;

&lt;h5&gt;Minor Complaint and or Suggestion&lt;/h5&gt;

&lt;p&gt;It would be nice if Twilio provided a way when invoking their Calls Rest API to specify a static conversation script. It was weird to have to host a static file on a web site just to have the call say something to the receiver. &lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11707" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/PJSn5E1SOXs" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/customer+service/default.aspx">customer service</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/rulemanager/default.aspx">rulemanager</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/mobile+agent/default.aspx">mobile agent</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/telephony/default.aspx">telephony</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/04/13/can-your-crm-place-a-phone-call.aspx</feedburner:origLink></item><item><title>Searching For Motivation</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/qUmCA-JpFmQ/searching-for-motivation.aspx</link><pubDate>Fri, 20 Feb 2009 17:26:28 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11651</guid><dc:creator>kmiller</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11651.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11651</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11651</wfw:comment><description>&lt;p&gt;People love search. I love search. I have to admit though that I am guilty of not using my own tools to the best of their ability. &lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2008/06/13/exposing-knowledge-using-dovetail-seeker.aspx"&gt;Dovetail Seeker&lt;/a&gt; is a search product we created to find important knowledge stuck in your Clarify/Dovetail CRM. At Dovetail we use our own software to support our customers and Seeker is always indispensible, when working new cases, effectively mining our solutions and previous cases for knowledge.&lt;/p&gt;  &lt;p&gt;That said, I am stuck in my old ways. I’ve been supporting our product line since before we had an effective search tool and whenever I get a support question I am quick to rack my brain and &lt;u&gt;not&lt;/u&gt; our knowledge base. &lt;/p&gt;  &lt;p&gt;To combat this affliction I &lt;a href="http://bighugelabs.com/flickr/motivator.php"&gt;created this motivational poster&lt;/a&gt; in effort to remind me to hit the search button before I think too hard. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/motivator83551621_2BCDAF0C.jpg"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="motivator8355162 (1)" border="0" alt="motivator8355162 (1)" src="http://blogs.dovetailsoftware.com/blogs/kmiller/motivator83551621_thumb_31A852A5.jpg" width="754" height="604" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11651" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/qUmCA-JpFmQ" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/customer+service/default.aspx">customer service</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/search/default.aspx">search</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/dovetail/default.aspx">dovetail</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/02/20/searching-for-motivation.aspx</feedburner:origLink></item><item><title>When a Guid is not a Guid?</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/Hy7oV9uG-fA/when-a-guid-is-not-a-guid.aspx</link><pubDate>Wed, 18 Feb 2009 15:17:08 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11645</guid><dc:creator>kmiller</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11645.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11645</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11645</wfw:comment><description>&lt;p&gt;Databases are fun things. Assigning identifiers to things in your database can be important. Sometimes it is handy to have an &lt;strong&gt;Uber &lt;/strong&gt;alternate key that is guaranteed globally unique. This can be useful in many ways one of which is database replication.&lt;/p&gt;  &lt;h4&gt;Globally Unique Identifiers&lt;/h4&gt;  &lt;p&gt;I just ran into a spot in a legacy database where it seems they wanted to future proof globally identifying database rows. They have quite a few tables with a field called &lt;strong&gt;guid&lt;/strong&gt;. That might ring a bell. In case it doesn’t &lt;a href="http://en.wikipedia.org/wiki/Globally_Unique_Identifier"&gt;GUIDs&lt;/a&gt; are a Microsoft implementation of globally unique identifiers. Easy to create. 16 bytes wide and lovely to look at. &lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; guid = Guid.NewGuid().ToString(); &lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;value&lt;/span&gt;: ba72c396-4b49-4d95-b9d7-03d42d5d5141&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Funny thing about this “guid” database field is that it’s a Unicode string 31 characters wide. You might notice the Guid value above is 36 characters. Ok, Get rid of the dashes and it is 32 characters. &lt;/p&gt;

&lt;p&gt;Crap still too big. Wait, I need to shove this hex encoded value into a 31 character &lt;u&gt;Unicode &lt;/u&gt;field? &lt;/p&gt;

&lt;p&gt;Sigh, There is a lot wrong with that statement. Something smells wrong. Good thing you can base64 encode the raw bytes of a Guid into 24 characters.&lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;string&lt;/span&gt; base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;value&lt;/span&gt;: JD/V9w5370OzsVK0RQ4u3A==&lt;/pre&gt;
&lt;/div&gt;

&lt;h4&gt;Wait a second&lt;/h4&gt;

&lt;p&gt;But still that’s fishy why do I have so much spare room left over. What are they getting at? I started looking at what values were actually in these &lt;strong&gt;guid&lt;/strong&gt; fields and found a surprise.&amp;#160; &lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12345678zzABCTowingbbbbb00000123 &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What the heck? &lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Oh look, 12345678 is part of the database identifier for the row. &lt;/li&gt;

  &lt;li&gt;Oh look, 0123 is the id for the database table (long story.) &lt;/li&gt;

  &lt;li&gt;Is that a company name in there? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why are they manually creating “globally unique identifiers” with what is technically a &lt;a href="http://en.wikipedia.org/wiki/Compound_key"&gt;compound key&lt;/a&gt;. Sure they are technically globally unique in this database but collisions could easily occur with other databases (for the same company). &lt;/p&gt;

&lt;h4&gt;So much wrong with this. &lt;/h4&gt;

&lt;p&gt;To set the “guid” field you have to know the database row’s identifier. Which means you need to insert the row. Get the identifier back create the faux guid and then update the row again. Yuck, that is a pretty chatty create mechanism.&lt;/p&gt;

&lt;p&gt;Worse yet. What if someone couples to the information encoded into the faux guids? &lt;/p&gt;

&lt;p&gt;Do I need to replicate their broken technique? Nah, I’ll just shove Base64 encoded Guids in there.&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11645" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/Hy7oV9uG-fA" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/02/18/when-a-guid-is-not-a-guid.aspx</feedburner:origLink></item><item><title>Setup ASP.Net caching using a super simple fluent interface</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/9rU71NJL_h8/setup-asp-net-caching-using-a-super-simple-fluent-interface.aspx</link><pubDate>Tue, 10 Feb 2009 18:13:11 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11635</guid><dc:creator>kmiller</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11635.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11635</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11635</wfw:comment><description>&lt;p&gt;While reviewing what I did to &lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/02/06/creating-rss-feeds-using-asp-net.aspx"&gt;Create an RSS Feed Using ASP.Net&lt;/a&gt;. We got worried that overzealous customers or feed readers might hit this feed kind of often, and since the data served up by the feed does not change too a lot we could easily do some caching. Luckily this is easy to do using ASP.Net’s built in caching support. Unfortunately the code to, programmatically, &lt;a href="http://support.microsoft.com/kb/323290"&gt;setup the caching is a little ugly&lt;/a&gt;. To make things easier to read I added a couple extension methods to create a very poor man’s DSL.&lt;/p&gt;  &lt;h4&gt;Following the Microsoft code example is ugly&lt;/h4&gt;  &lt;p&gt;Seeing the code below in my HttpHandler made me cringe. Hopefully it makes you cringe too.&lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;TimeSpan freshness = Timespan.FromHours(1);&lt;/pre&gt;

  &lt;pre&gt;DateTime now = DateTime.Now; &lt;/pre&gt;

  &lt;pre class="alt"&gt;context.Response.Cache.SetExpires(now.Add(freshness)); &lt;/pre&gt;

  &lt;pre&gt;context.Response.Cache.SetMaxAge(freshness); &lt;/pre&gt;

  &lt;pre class="alt"&gt;context.Response.Cache.SetCacheability(HttpCacheability.Server); &lt;/pre&gt;

  &lt;pre&gt;context.Response.Cache.SetValidUntilExpires(&lt;span class="kwrd"&gt;true&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre class="alt"&gt;context.Response.Cache.SetValidUntilExpires(&lt;span class="kwrd"&gt;true&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;context.Response.Cache.VaryByParams[&lt;span class="str"&gt;&amp;quot;days&amp;quot;&lt;/span&gt;] = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The essence of what I want to do is to have ASP.Net cache the content of the feed for a few minutes. It should also cache requests having the “days” parameter the handler accepts. I added that nifty “feature” without blogging about it. Please forgive me.&lt;/p&gt;

&lt;h4&gt;A Better Way&lt;/h4&gt;

&lt;p&gt;Let’s wrap the essence of what is being done into a couple of extension methods making the icky code above a bit easier to read and understand.&lt;/p&gt;

&lt;pre class="csharpcode"&gt;context.Response.CacheFor(30.Minutes()).OnParameters(&lt;span class="str"&gt;&amp;quot;days&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

&lt;h4&gt;Fluenty Goodness&lt;/h4&gt;

&lt;p&gt;The, hopefully, easier to ready version of the cache setup code is accomplished with extension methods that use the &lt;a href="http://www.martinfowler.com/bliki/FluentInterface.html"&gt;fluent interface&lt;/a&gt; “compliant” &lt;a href="http://martinfowler.com/dslwip/MethodChaining.html"&gt;Method Chaining&lt;/a&gt; technique. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; WebCachingExtension&lt;/pre&gt;

  &lt;pre&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; HttpResponse CacheFor(&lt;span class="kwrd"&gt;this&lt;/span&gt; HttpResponse response, TimeSpan timeSpan)&lt;/pre&gt;

  &lt;pre&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;        response.Cache.SetExpires(DateTime.Now.Add(timeSpan));&lt;/pre&gt;

  &lt;pre&gt;        response.Cache.SetMaxAge(timeSpan);&lt;/pre&gt;

  &lt;pre class="alt"&gt;        response.Cache.SetCacheability(HttpCacheability.Server);&lt;/pre&gt;

  &lt;pre&gt;        response.Cache.SetValidUntilExpires(&lt;span class="kwrd"&gt;true&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; response;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; HttpResponse OnParameters(&lt;span class="kwrd"&gt;this&lt;/span&gt; HttpResponse response, &lt;span class="kwrd"&gt;params&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] parameters)&lt;/pre&gt;

  &lt;pre&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; parameter &lt;span class="kwrd"&gt;in&lt;/span&gt; parameters)&lt;/pre&gt;

  &lt;pre&gt;        {&lt;/pre&gt;

  &lt;pre class="alt"&gt;            response.Cache.VaryByParams[parameter] = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;        }&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; response;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; TimeSpan Minutes(&lt;span class="kwrd"&gt;this&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; numberOfMinutes)&lt;/pre&gt;

  &lt;pre&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; TimeSpan.FromMinutes(numberOfMinutes);&lt;/pre&gt;

  &lt;pre&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Ok, so there is also a little integer extension method in there to make a pretty TimeSpan. I use extensions like this all the time where they make sense and improve the readability of the code.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The key here is that the extension method returns the object being extended allowing the method calls to be chained together. The extended object in this case is the HttpResponse. The context being modified by the fluent interface in this case is the Cache object. Say you need some extra behavior? Just add another extension method. One downside to this trick is the lack of readability when you switch around the ordering of the calls. &lt;/p&gt;

&lt;pre class="csharpcode"&gt;context.Response.OnParameters(“days”).CacheFor(30.minutes()) &lt;/pre&gt;

&lt;p&gt;Yuck. While, legal and correct it is at all easy to understand. I’ll punt on solving that problem in this blog post but I will say the solution lies in going to the next level and using an &lt;a href="http://martinfowler.com/dslwip/ExpressionBuilder.html"&gt;Expression Builder&lt;/a&gt; and potentially controlling the ordering of the method chain using interfaces.&lt;/p&gt;

&lt;p&gt;Oops, I’ve said too much and slipped into more advanced waters that &lt;a href="http://www.lostechies.com/blogs/chad_myers/archive/2008/10/26/alt-net-workshops-internal-dsl-draft-outline-notes.aspx"&gt;have been visited by smarter people that myself&lt;/a&gt;. I will bid you adieu. &lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11635" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/9rU71NJL_h8" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/asp.net/default.aspx">asp.net</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/internal+dsl/default.aspx">internal dsl</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/02/10/setup-asp-net-caching-using-a-super-simple-fluent-interface.aspx</feedburner:origLink></item><item><title>Creating RSS Feeds Using ASP.Net</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/607JX6WaLyE/creating-rss-feeds-using-asp-net.aspx</link><pubDate>Fri, 06 Feb 2009 23:33:55 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11628</guid><dc:creator>kmiller</dc:creator><slash:comments>12</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11628.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11628</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11628</wfw:comment><description>&lt;p&gt;Warning: This is one of those late to the party posts where I show off some cool thing that everyone already likely knows about. I just feel special right now because I was able to push out something we’ve wanted to do for quite some time in an afternoon. &lt;/p&gt;  &lt;p&gt;We wanted to have an RSS feed so customers could keep up to date with new or recently updated &lt;a href="http://www.dovetailsoftware.com/resources/solutions/most_recent.aspx" target="_blank"&gt;Dovetail Knowledge Base&lt;/a&gt; articles. My personal mental gate was that I really didn’t want to try to figure out how to properly create an RSS or Atom feed and I didn’t want to bother with complex heavy handed frameworks like &lt;a href="http://www.codeplex.com/Argotic" target="_blank"&gt;Argotic&lt;/a&gt;. Then one day &lt;a href="http://www.lostechies.com/blogs/joshuaflanagan/" target="_blank"&gt;Josh&lt;/a&gt; introduced me to the &lt;a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.aspx" target="_blank"&gt;System.ServiceModel.Syndication&lt;/a&gt; namespace (introduced in .Net 3.5) and my life was never the same. &lt;/p&gt;  &lt;p&gt;Simple stuff really. Create a SyndicatedFeed filled with feed items and hand that to a Feed Formatter.&lt;/p&gt;  &lt;h4&gt;Generating an RSS feed&lt;/h4&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ProcessRequest(HttpContext context)&lt;/pre&gt;

  &lt;pre&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;    SyndicationFeed feed = CreateRecentSolutionsFeed();&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var output = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringWriter();&lt;/pre&gt;

  &lt;pre&gt;    var writer = &lt;span class="kwrd"&gt;new&lt;/span&gt; XmlTextWriter(output);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;new&lt;/span&gt; Rss20FeedFormatter(feed).WriteTo(writer);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    context.Response.ContentType = &lt;span class="str"&gt;&amp;quot;application/rss+xml&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre class="alt"&gt;    context.Response.Write(output.ToString());&lt;/pre&gt;

  &lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;But I get ahead of myself. We have to serve this feed from somewhere. Not really being an ASP.Net expert Josh got me started with a Generic Handler. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/image_4E4A4970.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_thumb_37A7FB29.png" width="644" height="424" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Which seems a thin construct for simply spiting content out of a URL. Perfect.&lt;/p&gt;

&lt;h5&gt;A Syndication Feed&lt;/h5&gt;

&lt;p&gt;To build the syndication I created this little helper method. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; SyndicationFeed CreateRecentSolutionsFeed()&lt;/pre&gt;

  &lt;pre class="alt"&gt;{&lt;/pre&gt;

  &lt;pre&gt;    var syndicationItems = GetRecentOrModifiedSolutionSyndicationItems(TimeSpan.FromDays(30));&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SyndicationFeed(syndicationItems)&lt;/pre&gt;

  &lt;pre class="alt"&gt;               {&lt;/pre&gt;

  &lt;pre&gt;                   Title = &lt;span class="kwrd"&gt;new&lt;/span&gt; TextSyndicationContent(&lt;span class="str"&gt;&amp;quot;Dovetail Software Knowledge Base&amp;quot;&lt;/span&gt;),&lt;/pre&gt;

  &lt;pre class="alt"&gt;                   Description = &lt;span class="kwrd"&gt;new&lt;/span&gt; TextSyndicationContent(&lt;span class="str"&gt;&amp;quot;Recently created or modified solutions regarding products offered by Dovetail Software.&amp;quot;&lt;/span&gt;),&lt;/pre&gt;

  &lt;pre&gt;                   ImageUrl = &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(&lt;span class="str"&gt;&amp;quot;http://www.dovetailsoftware.com/images/header_logo.gif&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre class="alt"&gt;               };&lt;/pre&gt;

  &lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;h5&gt;Assembling a Syndication Item&lt;/h5&gt;

&lt;p&gt;I’ll skip the gory Dovetail SDK data access code that goes out to the Clarify database and materializes *poof* any solutions that have been created or modified in the last 30 days. Unless, of course, one of my 2 readers asks nicely for it. The interesting bit in any case is that it basically loops over all the solutions found and assembles a Syndication Item for each one.&lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; SyndicationItem AssembleSolutionSyndicationItem(ClarifyDataRow solution)&lt;/pre&gt;

  &lt;pre&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var id = solution[&lt;span class="str"&gt;&amp;quot;id_number&amp;quot;&lt;/span&gt;].ToString();&lt;/pre&gt;

  &lt;pre&gt;    var title = String.Format(&lt;span class="str"&gt;&amp;quot;[{0}] {1}&amp;quot;&lt;/span&gt;, id, solution[&lt;span class="str"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;]);&lt;/pre&gt;

  &lt;pre class="alt"&gt;    var content = solution[&lt;span class="str"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;].ToString();&lt;/pre&gt;

  &lt;pre&gt;    var url = &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(String.Format(&lt;span class="str"&gt;&amp;quot;http://www.dovetailsoftware.com/resources/solutions/{0}.aspx&amp;quot;&lt;/span&gt;, id));&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SyndicationItem(title, &lt;span class="kwrd"&gt;null&lt;/span&gt;, url) { Summary = &lt;span class="kwrd"&gt;new&lt;/span&gt; TextSyndicationContent(content) };&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;h3&gt;Results&lt;/h3&gt;

&lt;p&gt;The end result is a totally hard coded HTTP Handler generating an hopefully useful RSS feed for Dovetail’s customers. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.dovetailsoftware.com/resources/knowledgeBaseFeed.ashx"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="Dovetail KB feed" border="0" alt="Dovetail KB feed" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_568F90F3.png" width="647" height="772" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Next up was to add a meta link to the feed in the html header of our knowledgebase pages. This makes the little RSS icon shows in the address bar of honest and decent hard working web browsers.&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;link&lt;/span&gt; &lt;span class="attr"&gt;rel&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;alternate&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;application/rss+xml&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;/resources/knowledgeBaseFed.ashx&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Dovetail Knowledgebase Feed&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;pre class="csharpcode"&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/image_01F3B1EE.png"&gt;&lt;img style="border-bottom:0px;border-left:0px;display:inline;border-top:0px;border-right:0px;" title="image" border="0" alt="image" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_thumb_136412C6.png" width="818" height="77" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11628" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/607JX6WaLyE" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/dovetail/default.aspx">dovetail</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/RSS/default.aspx">RSS</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/02/06/creating-rss-feeds-using-asp-net.aspx</feedburner:origLink></item><item><title>Mobile Agent 1.1 - Now with better access to your knowledge</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/lnvq7IXKYM0/mobile-agent-1-1-now-with-better-access-to-your-knowledge.aspx</link><pubDate>Fri, 23 Jan 2009 21:33:59 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11616</guid><dc:creator>kmiller</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11616.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11616</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11616</wfw:comment><description>&lt;p&gt;Today we are releasing a better &lt;a href="http://www.dovetailsoftware.com/solutions/ClarifyMobile.aspx" target="_blank"&gt;Dovetail Mobile Agent&lt;/a&gt;. In this release we focused on exposing the knowledge contained in your Dovetail CRM to your mobile agents and public users.&amp;#160; For more details please take a look at &lt;a href="http://www.dovetailsoftware.com/user_guides/dovetailmobileagent/1.1.0.2092/html/index.htm" target="_blank"&gt;the documentation&lt;/a&gt; but to sum up I’ve composed for you a sort of visual what’s new in this release.&lt;/p&gt;  &lt;h4&gt;Agents can now…&lt;/h4&gt;  &lt;p&gt;Search for and use solutions while working cases. Solutions found to apply to a case being worked can easily be sent and linked to the case. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11agentsearchresults_0BD3E93A.png"&gt;&lt;img title="mobile-1-1-agent-searchresults" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="329" alt="mobile-1-1-agent-searchresults" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11agentsearchresults_thumb_3235CC85.png" width="310" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Searching now returns both solution and cases.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilesendsolutiontocase_3B9E8F5D.png"&gt;&lt;img title="mobile-send-solution-to-case" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;margin:20px 0px 0px;border-right-width:0px;" height="180" alt="mobile-send-solution-to-case" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilesendsolutiontocase_thumb_542E2CAD.png" width="313" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Once a solution is found details about it can be easily sent to a case. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilesendsolutiontocase2_6CBDC9FD.png"&gt;&lt;img title="mobile-send-solution-to-case2" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;margin:20px 0px 0px;border-right-width:0px;" height="355" alt="mobile-send-solution-to-case2" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilesendsolutiontocase2_thumb_7A23DD03.png" width="296" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Because sending emails from a mobile device can be time consuming, Mobile Agent fills in most details of the email for the agent. The solution is also linked to the case giving support managers feedback about hot support topics.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/image_24900E21.png"&gt;&lt;img title="image" style="border-right:0px;border-top:0px;display:inline;margin:20px 0px 0px;border-left:0px;border-bottom:0px;" height="269" alt="image" src="http://blogs.dovetailsoftware.com/blogs/kmiller/image_thumb_0408CE6F.png" width="308" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilecaseslinkedtoasolution_7873112F.png"&gt;&lt;img title="mobile-cases-linked-to-a-solution" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="280" alt="mobile-cases-linked-to-a-solution" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobilecaseslinkedtoasolution_thumb_57EBD17D.png" width="311" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Agents can easily browse and navigate to all cases linking to a given solution.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11casesummary_376491CB.png"&gt;&lt;img title="mobile-1-1-case-summary" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;margin:20px 0px 0px;border-right-width:0px;" height="304" alt="mobile-1-1-case-summary" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11casesummary_thumb_2FD9225E.png" width="298" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Likewise agents viewing a case can now see which solution a case is linked to and optionally unlink the solution from the case.&lt;/p&gt;  &lt;h4&gt;Public users can now…&lt;/h4&gt;  &lt;p&gt;New for this release is the ability for public, non-authenticated, users to search for and view public solutions found in the knowledgebase. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11homepage_3D3F3564.png"&gt;&lt;img title="mobile-1-1-home-page" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="102" alt="mobile-1-1-home-page" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobile11homepage_thumb_0EE5AFB7.png" width="317" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The home page now welcomes public users.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobileknowledgebaseseach_0E797CC2.png"&gt;&lt;img title="mobile-knowledgebase-seach" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;margin:20px 0px 0px;border-right-width:0px;" height="209" alt="mobile-knowledgebase-seach" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobileknowledgebaseseach_thumb_3BFA9C85.png" width="307" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Searching for public solutions. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.dovetailsoftware.com/blogs/kmiller/mobileknowledgebaseresults_269CE71D.png"&gt;&lt;img title="mobile-knowledgebase-results" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;margin:20px 0px 0px;border-right-width:0px;" height="201" alt="mobile-knowledgebase-results" src="http://blogs.dovetailsoftware.com/blogs/kmiller/mobileknowledgebaseresults_thumb_3F2C846D.png" width="313" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Viewing a public solution.&lt;/p&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11616" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/lnvq7IXKYM0" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/mobile+agent/default.aspx">mobile agent</category><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/dovetail+release/default.aspx">dovetail release</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/01/23/mobile-agent-1-1-now-with-better-access-to-your-knowledge.aspx</feedburner:origLink></item><item><title>Avoid Mickey Mouse Email Loops</title><link>http://feedproxy.google.com/~r/KevinMiller/~3/OB9RCePG4NM/avoid-mickey-mouse-email-loops.aspx</link><pubDate>Mon, 12 Jan 2009 17:16:22 GMT</pubDate><guid isPermaLink="false">5c171a62-8cd2-4aac-8f20-46c3ac3f3269:11608</guid><dc:creator>kmiller</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.dovetailsoftware.com/blogs/kmiller/comments/11608.aspx</comments><wfw:commentRss>http://blogs.dovetailsoftware.com/blogs/kmiller/commentrss.aspx?PostID=11608</wfw:commentRss><wfw:comment>http://blogs.dovetailsoftware.com/blogs/kmiller/rsscomments.aspx?PostID=11608</wfw:comment><description>&lt;p&gt;Say you have an application (i.e. bot, script, automaton, service)&amp;#160; polling an email account (POP3 or IMAP) for emails. And say that your application sends an automated response back to the sender of the email. Be careful. You can easily get into situations that will create an &lt;a href="http://en.wikipedia.org/wiki/E-mail_loop" target="_blank"&gt;email loop&lt;/a&gt; or worse yet the &lt;a href="http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome" target="_blank"&gt;Sorcerer’s Apprentice Syndrome&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;After sending email from an automaton it is quite common for the receiving mail servers to bounce emails back or accounts to temporarily respond with an “Out of Office” response. If your service is not smart enough to ignore automated responses to it’s own emails? Well. The streams will cross and poorly written email services will implode under the weight of your unending email loop. Kittens die. System administrators call you. This is bad.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://tools.ietf.org/html/rfc3834" target="_blank"&gt;RFC 3834&lt;/a&gt; comes to the rescue with this sage advice: &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;An automatic responder MUST NOT blindly send a response for every message received.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Thank you Captain Obvious. Thankfully this RFC sets up a standard way to detect and inform your email service in order to avoid the dreaded email loop.&lt;/p&gt;  &lt;h3&gt;Being a good email citizen&lt;/h3&gt;  &lt;p&gt;The RFC provides guidance on informing your senders that you are indeed an automation. The crux of the guidance is that your email response should include the &lt;strong&gt;Auto-Submitted&lt;/strong&gt; email header.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Return-Path: &amp;lt;admin@kevin&amp;gt;      &lt;br /&gt;Received: from Kevin ([192.168.0.106])       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; by KEVIN       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; with hMailServer ; Mon, 12 Jan 2009 10:21:47 -0600       &lt;br /&gt;&lt;strong&gt;Auto-Submitted: auto-replied&lt;/strong&gt;       &lt;br /&gt;X-FC-SentBySECNET: Dovetail-Email-Agent-Service-Instance-001       &lt;br /&gt;From: Dovetail Software Email Agent &amp;lt;admin@kevin&amp;gt;       &lt;br /&gt;To: barney &amp;lt;barney@kevin&amp;gt;       &lt;br /&gt;Date: Mon, 12 Jan 2009 16:21:47 GMT       &lt;br /&gt;Message-ID: &amp;lt;e75d9c16520b461e8d10a1f63c029639@Kevin&amp;gt;       &lt;br /&gt;Subject: RE: send me an automated response? - About case 12&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;To accomplish this I created a little extension method used when creating emails. Note, the MailMessage type is part of an extinct .Net Email API we are currently saddled with: &lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; AutoSubmittedHeaderTitle = &lt;span class="str"&gt;&amp;quot;Auto-Submitted&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ShouldBeAutoReplied(&lt;span class="kwrd"&gt;this&lt;/span&gt; MailMessage message)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;  message.Headers.Add(AutoSubmittedHeaderTitle, &lt;span class="str"&gt;&amp;quot;auto-replied&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;h3&gt;Being a good email consumer&lt;/h3&gt;

&lt;p&gt;On the flip side your email application should have a plan for what to do when it gets an automated message. For our usage we’ve updated our Email Agent service to detect and ignore auto submitted emails. Again I use an extension method to test each email.&lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsAutoSubmitted(&lt;span class="kwrd"&gt;this&lt;/span&gt; MailMessage message)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;return&lt;/span&gt; (message.Headers.Contains(AutoSubmittedHeaderTitle) &amp;amp;&amp;amp;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  message.Headers[AutoSubmittedHeaderTitle].Value != &lt;span class="str"&gt;&amp;quot;no&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;&lt;img src="http://blogs.dovetailsoftware.com/aggbug.aspx?PostID=11608" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/KevinMiller/~4/OB9RCePG4NM" height="1" width="1"/&gt;</description><category domain="http://blogs.dovetailsoftware.com/blogs/kmiller/archive/tags/email+agent/default.aspx">email agent</category><feedburner:origLink>http://blogs.dovetailsoftware.com/blogs/kmiller/archive/2009/01/12/avoid-mickey-mouse-email-loops.aspx</feedburner:origLink></item></channel></rss>
