/src/Amptools/Public/labels/Amplify.aspx
ASP.NET | 883 lines | 654 code | 229 blank | 0 comment | 83 complexity | 56e4acff0feec0a6f9e94b4bc51abc82 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1<%@ Page Title="amptools.net" Language="C#" MasterPageFile="~/App/Views/Layouts/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="System.Web.Mvc.ViewPage" %> 2<asp:Content ID="Content2" ContentPlaceHolderID="Head" runat="server"> 3 <link href='/Content/default/css/syntax-highlighter.css' rel="stylesheet" type="text/css" /> 4 <link rel="alternate" type="application/rss+xml" title="amptools.net - RSS" href="http://feeds.feedburner.com/amptoolsnet" /> 5 <link rel="service.post" type="application/atom+xml" title="amptools.net - Atom" href="http://www.blogger.com/feeds/7043853799247804655/posts/default" /> 6 <link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://www.blogger.com/rsd.g?blogID=7043853799247804655" /> 7 <style type="text/css"> 8 @import url("http://www.blogger.com/css/blog_controls.css"); 9 @import url("http://www.blogger.com/dyn-css/authorization.css?targetBlogID=7043853799247804655"); 10 </style> 11 12 <script type="text/javascript" src="http://www.blogger.com/js/backlink.js"></script> 13 <script type="text/javascript" src="http://www.blogger.com/js/backlink_control.js"></script> 14 <script type="text/javascript">var BL_backlinkURL = "http://www.blogger.com/dyn-js/backlink_count.js";var BL_blogId = "7043853799247804655";</script> 15</asp:Content> 16 17<asp:Content ID="Content3" ContentPlaceHolderID="Javascript" runat="server"> 18 <script type="text/javascript" src="~/javascripts/prototype.js"></script> 19 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shCore.js"></script> 20 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushCSharp.js"></script> 21 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushRuby.js"></script> 22 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushJScript.js"></script> 23 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushCss.js"></script> 24 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushSql.js"></script> 25 <script type="text/javascript" src="~/javascripts/syntax-highlighter/shBrushXml.js"></script> 26 <script type="text/javascript"> 27 //<[CDATA[ 28 dp.SyntaxHighlighter.HighlightAll('code'); 29 //]]> 30 </script> 31</asp:Content> 32<asp:Content ID="Content1" ContentPlaceHolderID="main" runat="server"> 33 34 35 <div class="left"> 36 <h2 class="silver"> 37 "Working on the complexity that is always indirectly implied by simpilicity." 38 </h2> 39 40 41 42 <h2 class="blog-title"><a name="1099928894407582088"></a> 43 44 Simple Log in Amplify 45 46 <span class="blog-byline"> 47 by: michael herndon, at: 11/02/2008 11:54:00 PM 48 </span> 49 50 </h2> 51 <div class="blog-content"> 52 <p>As many people know rails has a simple logger inside the framework which really only outputs to the console. This comes in handy to see SQL output of your models, benchmarks and other cool little things inside the rails framework.  </p> <p>Of course people in the .Net realm tend to want things that scale and yet simplified for them.  So output only to the console won't fly in larger projects.  There is log4net, but you don't want to have it tightly coupled to the system, this caused an issue to one project that I worked on while still working for <a href="http://www.vivussoftware.com">Vivus Software</a> before I came <a href="http://www.opensourceconnections.com">OSC</a>. Where the framework and project both expected a certain version of Log4Net and there was no wrapper to put something else in place if need be.  </p> <span id="more-0"></span> <p>So the solution was to Build small static Log class, that takes a list of an interface ILog (different than log4net's) and then use that interface to build a wrapper around using the root Logger for log4net. This way people can still build their own loggers for amplify or build their own Appenders for Log4Net and amplify will still be able to leverage this. Of course there is also conditional symbol LOG4NET in amplify if you wish to build the amplify framework without any dependency on the log4net library.   </p> <p>To me this is more like the Console.WriteLine that is needed for a class library vs what log4net is typically used for in an application where it also spits out the type information of a class. To help with the testing using mock object is the <a href="http://code.google.com/p/moq/" rel="tag">Moq (Mock You)</a> which uses the Lambda expression and gets rid of all that crazy record/playback junk to which I loathe.  </p> <pre class="code csharp"> public void Test() 53 { 54 var id = 0; 55 Log.Sql("SELECT * FROM test"); 56 Log.Debug("The id is {0} ", id); 57 } 58 59 60 [It(Order = 2), Should("have a debug method that logs debug statements")] 61 public void Debug() 62 { 63 var calls = new List<string>(); 64 var log = CreateMockLog(); 65 66 log.Expect(o => o.Debug(Moq.It.IsAny<string>(), null)) 67 .Callback((string s, object[] args) => { calls.Add(s); }); 68 69 calls.Count.ShouldBe(0); 70 71 // simple and easily called from anywhere... and lets you 72 // format messages like 73 // Log.Debug("item id: {0} could not be saved", id); 74 Log.Debug("debug", null); 75 calls.Count.ShouldBe(1); 76 calls.Contains("debug").ShouldBeTrue(); 77 78 Log.IsDebug = false; 79 80 // its turned off so it won't output anything 81 Log.Debug("debug", null); 82 calls.Count.ShouldBe(1); 83 84 Log.IsDebug = true; 85 } 86 87 private static Mock<ILog> CreateMockLog() 88 { 89 var log = new Mock<ILog>(); 90 Log.Loggers.Clear(); 91 Log.Loggers.Add(log.Object); 92 return log; 93 } 94</pre> 95 96<p>Though keep in mind, one of the more trickier things to mock is the use of a method with the "params" keyword. I had to used a callback that you pass in an object array (object[] args) and I passed in null, otherwise the callback would not fire, and Lambdas don't allow the "params" keyword. Once that was figured out, I was able to use moq successfully to test the Log class successfully. </p> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/BHAG.aspx">BHAG</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/Mock Objects.aspx">Mock Objects</a>, <a rel='tag' href="http://www.amptools.net/labels/Moq.aspx">Moq</a>, <a rel='tag' href="http://www.amptools.net/labels/Unit Testing.aspx">Unit Testing</a></p> 97 </div> 98 <div class="blog-footer"> 99 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1099928894407582088"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1099928894407582088;><span style="text-transform:lowercase">0 Comments</span></a> 100 101 <a class="comment-link" href="http://www.amptools.net/2008/11/simple-log-in-amplify.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 102 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=1099928894407582088" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 103 104 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 105 </div> 106 107 108 109 <h2 class="blog-title"><a name="3448672103322818552"></a> 110 111 Gallio and Mb-Unit release v3.0.4 112 113 <span class="blog-byline"> 114 by: michael herndon, at: 10/29/2008 02:11:00 AM 115 </span> 116 117 </h2> 118 <div class="blog-content"> 119 <p>You can find many of the new <a href="http://blog.bits-in-motion.com/2008/10/announcing-gallio-and-mbunit-v304.html ">features over at Jeff Brown's blog</a>.  Some of the cooler features of note is the integration with the visual studio testing system, wrapping testing for exceptions into a delegate, and data store called Ambient, in which you can store state for your tests.  I've integrated this into amplify which is again, now on <a href="http://github.com/michaelherndon/amplify/tree/master">GitHub</a>. I did run into a few lil issues when setting this up though....</p> <span id="more-1"></span> <p>The biggest issue was finding out how to turn a project into a test project in order to get the visual studio integration working for Gallio. Basically you need to make modifications to the .csproj file and add an XML element of <ProjectTypeGuids> into the first <PropertyGroup> of the file.  </p> <pre class="code xml"><ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB}; 120{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 121</ProjectTypeGuids></pre> 122 123<p>After this, I could get the Gallio tests showing up in visual studio. </p> 124 125<p><a href="http://lh3.ggpht.com/amptools/SQf-bZnOv5I/AAAAAAAAABM/xPj4ncLlA3g/s1600-h/Gallio-Test-View%5B2%5D.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="212" alt="Gallio-Test-View" src="http://lh3.ggpht.com/amptools/SQf-b1n8cMI/AAAAAAAAABU/f0_tzJSs_Lg/Gallio-Test-View_thumb.png?imgmax=800" width="244" border="0" /></a> </p> 126 127<p>Now this coupled with <a href="http://testdriven.net/" rel="tag">Test Driven .Net</a> really helps with the testing process especially since with certain versions of Visual Studio, Test Driven .Net will let you run visual studio's code coverage with Gallio. </p> 128 129<p><a href="http://lh6.ggpht.com/amptools/SQf-cYepypI/AAAAAAAAABY/2DfBfKGZbZY/s1600-h/CodeCoverage%5B2%5D.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="207" alt="CodeCoverage" src="http://lh5.ggpht.com/amptools/SQf-cwXSDSI/AAAAAAAAABc/i2MQj8jhAQU/CodeCoverage_thumb.png?imgmax=800" width="244" border="0" /></a> </p> 130 131<p>Again, one of the cooler features was the improvements to Mb-Unit's Asserts (which did change the API, but its all good, cause I wrap the Asserts that I use in BDD style mixin Methods, so I just need to change them in one place). The one really change of note would be adding Assert.Throw and Assert.Throw<T>, to which you can wrap throwing an exception into a delegate.</p> 132 133<p><a href="http://lh6.ggpht.com/amptools/SQf-di2keqI/AAAAAAAAABg/KCLE5wy_nU4/s1600-h/WrappingExceptions%5B2%5D.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="178" alt="WrappingExceptions" src="http://lh3.ggpht.com/amptools/SQf-eB-bggI/AAAAAAAAABk/Tl816nR7vkw/WrappingExceptions_thumb.png?imgmax=800" width="244" border="0" /></a> </p> 134 135<p>All in all nice improvements to both Gallio and Mb-Unit, which are now incorporated into amplify. </p> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/BDD.aspx">BDD</a>, <a rel='tag' href="http://www.amptools.net/labels/BHAG.aspx">BHAG</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/Gallio.aspx">Gallio</a>, <a rel='tag' href="http://www.amptools.net/labels/Mb-Unit.aspx">Mb-Unit</a>, <a rel='tag' href="http://www.amptools.net/labels/Mixins.aspx">Mixins</a>, <a rel='tag' href="http://www.amptools.net/labels/Unit Testing.aspx">Unit Testing</a></p> 136 </div> 137 <div class="blog-footer"> 138 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=3448672103322818552"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=3448672103322818552;><span style="text-transform:lowercase">0 Comments</span></a> 139 140 <a class="comment-link" href="http://www.amptools.net/2008/10/gallio-and-mb-unit-release-v304.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 141 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=3448672103322818552" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 142 143 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 144 </div> 145 146 147 148 <h2 class="blog-title"><a name="1045031666772907229"></a> 149 150 Getting the |DataDirectory| folder in C sharp 151 152 <span class="blog-byline"> 153 by: michael herndon, at: 8/14/2008 02:19:00 PM 154 </span> 155 156 </h2> 157 <div class="blog-content"> 158 <p>One of the cool things about sql server express's connection strings is the ability to relatively path database files for sql express databases. Often one might see something in the connection string that looks like " AttachDbFilename=|DataDirectory|\file.mdf ". The pipes and DataDirectory is parsed to equate to a relative data directory file path that changes depending on the application type (i.e. an application deployed by click once, vs an application that is installed to a folder). </p> 159 160<p>However sometimes you might want to know where that data directory is, like in my case, I want to take a look at a connection string and attempt to create a database based off the connection string in order to speed up development using database migrations. This could also come in handy deploying small applications. </p> 161 162<p>So after some research via google, I found <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=702378&SiteID=1">this gem on msdn about where the DataDirectory is</a>, though not guaranteed to be accurate. It basically follows 3 simple hierarchal rules. If the AppDomain.SetData method has been used, then the directory is set there. Otherwise if the application is a click once application the data directory is set or its in the application root folder. </p> <p>So below is what the code might possibly look like in order to get the applications data directory no matter what type of application it is and it gives you ability to set the DataDirectory as well. </p> <p></p> 163 164<pre class="code csharp">namespace Amplify 165{ 166 using System; 167 using System.Configuration; 168 using System.Collections.Generic; 169 using System.Collections.Specialized; 170 using System.Deployment; 171 using System.Deployment.Application; 172 using System.IO; 173 using System.Text; 174 using System.Reflection; 175 using System.Web; 176 177 public class ApplicationContext { 178 179 private static NameValueContext s_properties; 180 181 182 public static bool IsWebsite { get; internal set; } 183 184 static ApplicationContext() 185 { 186 IsWebsite = (System.Web.HttpContext.Current != null); 187 188 } 189 190 public static string DataDirectory 191 { 192 get 193 { 194 string dataDirectory = GetProperty("DataDirectory") as string; 195 196 if (string.IsNullOrEmpty(dataDirectory)) 197 { 198 dataDirectory = AppDomain 199 .CurrentDomain.GetData("DataDirectory") as string; 200 201 if (dataDirectory == null) 202 { 203 if (ApplicationDeployment.IsNetworkDeployed) 204 dataDirectory = ApplicationDeployment 205 .CurrentDeployment.DataDirectory; 206 else 207 dataDirectory = Path.GetDirectoryName( 208 Assembly.GetExecutingAssembly() 209 .GetName().CodeBase); 210 } 211 dataDirectory = dataDirectory.Replace(@"file:\", ""); 212 SetProperty("DataDirectory", dataDirectory); 213 } 214 return dataDirectory; 215 } 216 set 217 { 218 string dataDirectory = GetProperty("DataDirectory") as string; 219 value = value.Replace(@"file:\", value); 220 221 if (!System.IO.Directory.Exists(dataDirectory)) 222 System.IO.Directory.CreateDirectory(dataDirectory); 223 224 SetProperty("DataDirectory", value); 225 AppDomain.CurrentDomain.SetData("DataDirectory", value); 226 } 227 } 228 229 230 public static object GetProperty(string propertyName) 231 { 232 if (IsWebsite) 233 return HttpContext.Current.Application[propertyName]; 234 else 235 return Context[propertyName]; 236 } 237 238 239 public static void SetProperty(string propertyName, object value) 240 { 241 if (IsWebsite) 242 HttpContext.Current.Application[propertyName] = value; 243 else 244 Context[propertyName] = value; 245 } 246 247 private static NameValueContext Context 248 { 249 get 250 { 251 if (s_properties == null) 252 s_properties = new NameValueContext(); 253 return s_properties; 254 } 255 } 256 } 257}</pre><p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a></p> 258 </div> 259 <div class="blog-footer"> 260 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1045031666772907229"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1045031666772907229;><span style="text-transform:lowercase">0 Comments</span></a> 261 262 <a class="comment-link" href="http://www.amptools.net/2008/08/getting-datadirectory-folder-in-c-sharp.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 263 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=1045031666772907229" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 264 265 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 266 </div> 267 268 269 270 <h2 class="blog-title"><a name="5634712196275175580"></a> 271 272 Amplify-Fusion, JavaScript libraries compatibility layer 273 274 <span class="blog-byline"> 275 by: michael herndon, at: 8/06/2008 03:35:00 PM 276 </span> 277 278 </h2> 279 <div class="blog-content"> 280 <p>As one might start guessing, I tend to jump around languages, projects, and clients (web/windows client not people).  After writing custom code for <a href="http://www.prototypejs.org/" rel="tag">prototype</a>, <a href="http://dojotoolkit.org/" rel="tag">dojo</a>, <a href="http://developer.yahoo.com/yui/" rel="tag">yui</a>, <a href="http://jquery.com/" rel="tag">jquery</a>, <a href="http://www.asp.net/ajax/" rel="tag">atlas (microsoft ajax library)</a> , it becomes a pain to remember which library has what and to port work you've previously done into another library.  Its one thing if it porting code from one language to another, but to have port code cause of the library/framework.... its a pain that isn't needed.  </p> <p>Granted the downside of writing a compatibility layer is extra code that could be duplicated else where or writing a smaller set of routines that are less in what other full blown libraries. However most people have broadband these days and the browsers have this cool thing called caching. Not to mention YUI has a great compressor and there are ways to include multiple files on the server rather than on the browser.  </p> <p>So I've begun to embark on a long journey of making JavaScript widgets build on a compatibility layer of different libraries. The layer it self will take some time, testing, and constant refinement while trying to keep the layer small.  However I believe this is needed, esp as a consultant that works on various projects and everyone has their favorite library to use...  </p> <p>you can find the beginnings of the code at <a title="http://code.google.com/p/amplify-fusion/" href="http://code.google.com/p/amplify-fusion/">http://code.google.com/p/amplify-fusion/</a>.</p> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/BHAG.aspx">BHAG</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/Javascript.aspx">Javascript</a></p> 281 </div> 282 <div class="blog-footer"> 283 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=5634712196275175580"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=5634712196275175580;><span style="text-transform:lowercase">0 Comments</span></a> 284 285 <a class="comment-link" href="http://www.amptools.net/2008/08/ampliy-fusion-javascript-libraries.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 286 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=5634712196275175580" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 287 288 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 289 </div> 290 291 292 293 <h2 class="blog-title"><a name="458120713693871790"></a> 294 295 Amplify's WCF Twitter API Client Library v0.2 296 297 <span class="blog-byline"> 298 by: michael herndon, at: 7/28/2008 12:33:00 AM 299 </span> 300 301 </h2> 302 <div class="blog-content"> 303 <p>After some playing around with WCF and Flickr, I decided to move everything internally for the twitter library to use WCF with it's <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.servicecontractattribute.aspx">ServiceContract</a>.  It was more of a pain that initially thought it would be. WCF is amazingly customizable but still has a couple of downfalls thanks to REST and people not writing their API for all technologies.  </p> <p>The biggest issue is that Twitter throws an Http 403 unknown exception when you call a page and something is incorrect in the url.  Well with WCF, it throws a <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.security.messagesecurityexception.aspx">System.ServiceModel.Security.MessageSecurityException</a>, due to the url returning a status code other than 200, and it wraps the inner exceptions inside of that. </p> <p>I'm sure there is a better way to get around this, but each proxy call is now wrapped by a try/catch in the client class methods in order to check for the <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.security.messagesecurityexception.aspx">MessageSecurityException</a>, then it checks the inner exception and determines if twitter sent back a response error message. If it does the library now throws a twitter exception with error information, otherwise you would only see the WCF exception that would mask the real issue.  </p> <p>Also I renamed the "Service" classes to "Client" seeing that is the proper term for them.  The tests are now updated as well to reflect the changes.  The tests are a good way of seeing of how to use the library. Last but not least, methods now return arrays instead of lists in order to be more REST friendly. </p> <p></p> <pre class="code csharp">using Amplify.Twitter; 304using System.Security; 305 306// ... stuff 307 308public void Test() 309{ 310 try { 311 string temp = "password"; 312 313 Twitter.SetCredentials("Username", temp); 314 315 StatusClient client = Twitter.CreateStatusClient(); 316 // or StatusClient client = new StatusClient("Username", temp); 317 318 Status[] tweets = service.GetUserTimeline(); 319 } catch (TwitterException ex) { 320 Log.Write(ex.ToString()); // write the twitter rest error. 321 } 322 323}</pre> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/BHAG.aspx">BHAG</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/JSON.aspx">JSON</a>, <a rel='tag' href="http://www.amptools.net/labels/REST.aspx">REST</a>, <a rel='tag' href="http://www.amptools.net/labels/Twitter.aspx">Twitter</a>, <a rel='tag' href="http://www.amptools.net/labels/WCF.aspx">WCF</a></p> 324 </div> 325 <div class="blog-footer"> 326 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=458120713693871790"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=458120713693871790;><span style="text-transform:lowercase">2 Comments</span></a> 327 328 <a class="comment-link" href="http://www.amptools.net/2008/07/amplify-wcf-twitter-api-client-library.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 329 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=458120713693871790" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 330 331 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 332 </div> 333 334 335 336 <h2 class="blog-title"><a name="8164722880411458481"></a> 337 338 Using WCF to access the Flickr JSON API 339 340 <span class="blog-byline"> 341 by: michael herndon, at: 7/17/2008 06:40:00 AM 342 </span> 343 344 </h2> 345 <div class="blog-content"> 346 <p>Even though there are quite a few opensource .Net libraries REST API out there, and a really impressive one for Flickr, they tend to never evolve or fork to use the newer framework technologies.  I think its great that they support 1.0, 2.0, and mono, but why not fork and support WCF?  I think part of the problem that .Net 3.5 use is not wide spread as it should be is not only information overload and bad naming for extension libraries, but also lack of opensource support to provide libraries and basis for current technology.  </p> <p>Flickr tends to be a little more tricky to use when calling it than some other REST APIs. WCF is a cool technology, but needs to be massaged when using it with Flickr due to a couple of gotchas and not so obvious things about WCF. In order to use Flickr, you need to register for an api_key and a shared secret code which is used to create an MD5 hash. You also need to create that MD5 each time you call a method, using the url parameters in a certain way. </p> <p>There are also a few not so obvious factors about using the JSON part of Flickr. All of the .Net libraries I've seen so far, use XML. While C# is king of parsing  XML, JSON tends to be more compact, which means less expense over the wire, though it might mean more time parsing once on the developers end point. So there are tradeoffs to using it. </p> <p>When I created the classes needed to use WCF to call Flickr, I have the actual "DataContract" object (DTO, Data Transfer Object), A Hash (Dictionary<string, object>) for adding the parameters to create the MD5 signature, A base class for creating the WCF proxy, a custom "WebContentTypeMapper", The "ServiceContract" interface, the actual "Client class", and a mixins class for creating the MD5. </p> <p>Here are some of the gotchas that I ran into while getting WCF to work with Flickr. </p> <p></p> <ol> <li>Flickr makes you create a md5 hash to send with every call for ALL parameters in alphabetical order using your "Shared Secret" Code as part of the md5.   </li> <li>Flickr's Json response "Content-Type" header is sent not as "application/json" but as "text/plain" which equates to "Raw" in WCF lingo.  </li> <li>The response object is wrapped and not obvious when it comes to json how the DataContract object should be formed.  </li> <li>Flickr will wrap the json response in a javascript function unless you pass in the parameter "nojsoncallback=1" </li> </ol> <p>To get around number one, I created an extension method that takes a Hash (Dictionary<string, object>) and adds method that converts the parameters into a MD5 string. </p> <pre class="code csharp">namespace Amplify.Linq 347{ 348 using System; 349 using System.Collections.Generic; 350 using System.Linq; 351 using System.Text; 352 353#if STANDALONE 354 public class Hash : Dictionary<string, object> 355 { 356 357 358 359 360 } 361#endif 362} 363// different file 364 365namespace Amplify.Linq 366{ 367 using System; 368 using System.Collections.Generic; 369 using System.Linq; 370 using System.Security.Cryptography; 371 using System.Text; 372 373 using Amplify.Linq; 374 375 public static class Mixins 376 { 377 378 379 380 public static string ToMD5(this Hash obj, string secret) 381 { 382 var query = from item in obj 383 orderby item.Key ascending 384 select item.Key + item.Value.ToString(); 385 386 StringBuilder builder = new StringBuilder(secret, 2048); 387 388 foreach (string item in query) 389 builder.Append(item); 390 391 MD5CryptoServiceProvider crypto = new MD5CryptoServiceProvider(); 392 byte[] bytes = Encoding.UTF8.GetBytes(builder.ToString()); 393 byte[] hashedBytes = crypto.ComputeHash(bytes, 0, bytes.Length); 394 return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); 395 } 396 397 } 398}</pre> 399 400<p>Well Gotcha number two, took a bit of research to figure it out. The error I got was that it was looking for Json or XML but could not do anything with "Raw". So the solution is to create a custom binding with a custom WebContentTypeManager. </p> 401 402<pre class="code csharp">namespace Amplify.Flickr 403{ 404 using System.ServiceModel; 405 using System.ServiceModel.Channels; 406 using System.Runtime.Serialization; 407 using System.ServiceModel.Web; 408 409 public class JsonContentMapper : WebContentTypeMapper 410 { 411 public override WebContentFormat GetMessageFormatForContentType(string contentType) 412 { 413 return WebContentFormat.Json; 414 } 415 } 416} 417 418// different file 419 420namespace Amplify.Flickr 421{ 422 using System; 423 using System.Collections.Generic; 424 using System.Linq; 425 using System.Text; 426 using System.ServiceModel; 427 using System.ServiceModel.Channels; 428 using System.ServiceModel.Description; 429 using System.ServiceModel.Web; 430 431 using Amplify.Linq; 432 433 public class FlickrClient<T> 434 { 435 protected string Secret { get; set; } 436 437 protected string Key { get; set; } 438 439 protected string Token { get; set; } 440 441 protected string Url { get; set; } 442 443 protected T Proxy { get; set; } 444 445 446 public FlickrClient(string key, string secret) 447 { 448 this.Key = key; 449 this.Secret = secret; 450 this.Url = "http://api.flickr.com/services/rest"; 451 this.Proxy = this.InitializeProxy(); 452 } 453 454 public FlickrClient(string key, string secret, string token) 455 :this(key, secret) 456 { 457 this.Token = token; 458 } 459 460 protected virtual T InitializeProxy() 461 { 462 // using custom wrapper 463 CustomBinding binding = new CustomBinding(new WebHttpBinding()); 464 WebMessageEncodingBindingElement property = binding.Elements.Find<WebMessageEncodingBindingElement>(); 465 property.ContentTypeMapper = new JsonContentMapper(); 466 467 468 ChannelFactory<T> channel = new ChannelFactory<T>( 469 binding, this.Url); 470 471 channel.Endpoint.Behaviors.Add( new WebHttpBehavior()); 472 return channel.CreateChannel(); 473 } 474 475 } 476}</pre> 477 478<p>The 3rd issue, to know that all objects are wrapped in a "Response" object. To create a DataContract for the getFrob method on flickr, it took looking at the json to see what it was returning. The object below is the basic form of the Json object that is returned, it also includes the properties in case an error object is returned. </p> 479 480<pre class="code csharp"> using System; 481 using System.Collections.Generic; 482 using System.Linq; 483 using System.Text; 484 using System.Runtime.Serialization; 485 486 [DataContract] 487 public class FrobResponse 488 { 489 [DataMember(Name = "frob")] 490 public JsonObject Frob { get; set; } 491 492 [DataContract(Name = "frob")] 493 public class JsonObject 494 { 495 [DataMember(Name = "_content")] 496 public string Content { get; set; } 497 498 } 499 500 [DataMember(Name = "stat")] 501 public string Status { get; set; } 502 503 [DataMember(Name = "code")] 504 public int Code { get; set; } 505 506 [DataMember(Name = "message")] 507 public string Message { get; set; } 508 } 509 510 // different file 511 512 513 using System; 514 using System.Collections.Generic; 515 using System.Linq; 516 using System.Text; 517 using System.ServiceModel; 518 using System.ServiceModel.Web; 519 520 [ServiceContract] 521 public interface IAuthClient 522 { 523 [OperationContract, 524 WebInvoke( 525 UriTemplate = "/?method=flickr.auth.getFrob&format=json&nojsoncallback=1&api_key={key}&api_sig={signature}", 526 ResponseFormat = WebMessageFormat.Json, 527 BodyStyle = WebMessageBodyStyle.Bare)] 528 FrobResponse GetFrob(string signature, string key); 529 530 [OperationContract, 531 WebInvoke( 532 UriTemplate = "/?method=flickr.auth.getToken&format=json&nojsoncallback=1&api_key={key}&frob={frob}&api_sig={signature}", 533 ResponseFormat = WebMessageFormat.Json, 534 BodyStyle = WebMessageBodyStyle.WrappedResponse)] 535 Auth GetToken(string signature, string key, string frob); 536 } 537 538 539 // the actual client class. 540 541 using System; 542 using System.Collections.Generic; 543 using System.Linq; 544 using System.Text; 545 546 using Amplify.Linq; 547 548 549 public class AuthClient : FlickrClient<IAuthClient> 550 { 551 552 public AuthClient(string key, string secret) 553 :base(key, secret) 554 { } 555 556 public AuthClient(string key, string secret, string token) 557 :base(key, secret, token) 558 { } 559 560 public string GetFrob() 561 { 562 string md5 = new Hash() { 563 {"api_key", this.Key}, 564 {"format", "json"}, 565 {"method", "flickr.auth.getFrob"}, 566 {"nojsoncallback", 1} 567 }.ToMD5(this.Secret); 568 569 FrobResponse response = this.Proxy.GetFrob(md5, this.Key); 570 if (response.Code > 0) 571 throw new Exception(response.Message); 572 573 return response.Frob.Content; 574 } 575 }</pre> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/Code.aspx">Code</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/Flickr.aspx">Flickr</a>, <a rel='tag' href="http://www.amptools.net/labels/JSON.aspx">JSON</a>, <a rel='tag' href="http://www.amptools.net/labels/REST.aspx">REST</a>, <a rel='tag' href="http://www.amptools.net/labels/WCF.aspx">WCF</a></p> 576 </div> 577 <div class="blog-footer"> 578 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=8164722880411458481"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=8164722880411458481;><span style="text-transform:lowercase">0 Comments</span></a> 579 580 <a class="comment-link" href="http://www.amptools.net/2008/07/using-wcf-to-access-flickr-json-api.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 581 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=8164722880411458481" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 582 583 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 584 </div> 585 586 587 588 <h2 class="blog-title"><a name="3907268195622759157"></a> 589 590 Amplify's TwitterN, Yet Another C# Twitter REST API Library 591 592 <span class="blog-byline"> 593 by: michael herndon, at: 7/13/2008 03:08:00 PM 594 </span> 595 596 </h2> 597 <div class="blog-content"> 598 <p><a href="http://code.google.com/p/twittern">http://code.google.com/p/twittern</a></p> <p>I put together a C# twitter library that uses WCF's <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx">DataContract</a> & <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.aspx">DataMembers</a> and .Net 3.5's <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx">DataContractJsonSerializer</a>.  Its currently in alpha stage, as I need to finish writing the tests, documentation and make a simple WPF client for twitter.  I needed a twitter library for a personal "secret" project of mine and found that most twitter libraries do not keep up with the twitter API and break when using them. Plus they tend to go to great lengths when parsing the xml.  Unfortunately I do not know if this will work on mono yet. It seems that they have "<a href="http://www.mono-project.com/WCF">Olive</a>", which is mono's version of WCF and I've seen where they have a path for the "DataContractJsonSerializer".  If there are any one familiar enough with mono, please by all means use the code, join the project.  </p> <p>One design decision that I made was to use Json rather than xml as its more compact and less data to transfer.  I used WCF because with DataContract & DataMembers you can parse Json and you can name your properties the property way with Pascal Case properties and then tell wcf what attributes of json they represent. This way you don't have properties that look like ruby like "screen_name" or  "profile_sidebar_border_color" in your <a href="http://martinfowler.com/eaaCatalog/dataTransferObject.html">Data Transfer Object</a> in order to deserialize the Json response from twitter. </p> <p> </p> <p>A basic sample of how to use the library is below. </p> <pre class="code csharp"> 599 600using Amplify.Twitter; 601using System.Security; 602 603// ... stuff 604 605public void Test() 606{ 607 SecureString password = new SecureString(); 608 string temp = "password"; 609 for (var i = 0; i < temp.Length; i++) 610 password.AppendChar(temp[i]); 611 612 password.MakeReadOnly(); 613 Twitter.SetCredentials("Username", password); 614 615 StatusService service = Twitter.CreateStatusService(); 616 // or StatusService service = new StatusService("Username", password); 617 618 List<Status> tweets = service.GetUserTimeline("MichaelHerndon"); 619} 620 621</pre> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/C#.aspx">C#</a>, <a rel='tag' href="http://www.amptools.net/labels/CSharp.aspx">CSharp</a>, <a rel='tag' href="http://www.amptools.net/labels/DataContractJsonSerializer.aspx">DataContractJsonSerializer</a>, <a rel='tag' href="http://www.amptools.net/labels/JSON.aspx">JSON</a>, <a rel='tag' href="http://www.amptools.net/labels/REST.aspx">REST</a>, <a rel='tag' href="http://www.amptools.net/labels/Twitter.aspx">Twitter</a>, <a rel='tag' href="http://www.amptools.net/labels/WCF.aspx">WCF</a></p> 622 </div> 623 <div class="blog-footer"> 624 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=3907268195622759157"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=3907268195622759157;><span style="text-transform:lowercase">3 Comments</span></a> 625 626 <a class="comment-link" href="http://www.amptools.net/2008/07/amplify-twittern-yet-another-c-twitter.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 627 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=3907268195622759157" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 628 629 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 630 </div> 631 632 633 634 <h2 class="blog-title"><a name="1442753008958214520"></a> 635 636 Thoughts on Developing a Linq To SQL Layer 637 638 <span class="blog-byline"> 639 by: michael herndon, at: 4/09/2008 04:46:00 PM 640 </span> 641 642 </h2> 643 <div class="blog-content"> 644 <p>Amplify is going to have a Data Namespace that will serve as a basis for dealing with data including, validation, database migrations & fixtures similar to rails and even give some basic patterns to use.  One of these patterns will be an Active Record pattern and another will be a Repository pattern, since the repository tends to favor <a href="http://msdn2.microsoft.com/en-us/library/bb425822.aspx">Linq to Sql.</a> </p> <p>One of the biggest debates on <a href="http://msdn2.microsoft.com/en-us/library/bb425822.aspx" rel="tag">Linq to Sql</a> is whether or not it supports the <a href="http://www.lhotka.net/weblog/ShouldAllAppsBeNtier.aspx" rel="tag">NTier</a> scenario. Of course many people are still confusing the difference between physical Tiers and virtual layers in code (DAL is a data access layer, not a tier). Linq to Sql can support layers out of the box, depending on how the developer uses the generated objects.   Linq to Sql does not support WCF out of the box and you pretty much have to really massage Linq to Sql in order work disconnected, even within a Web Environment the default tight <a href="http://www.west-wind.com/weblog/posts/162336.aspx">relationship between an object and the DataContext</a> can be a bit of a pain. Linq to Sql does follow Domain Driven Design heavily. </p> <p>With <a href="http://www.asp.net/mvc/">Asp.Net Mvc</a> on the horizon, I think it would be proper for a Linq to Sql to have some form of an <a href="http://martinfowler.com/eaaCatalog/activeRecord.html" rel="tag">Active Record</a> harness that provides much of the same functionality as possible as the <a title="tag" href="http://wiki.rubyonrails.org/rails/pages/ActiveRecord">Rail's Active Record</a> does. Obviously c# is not ruby and a developer can not do everything the same way in c# that you can in ruby.  A good reason to get as close as possible to rails api for active record is to simply help developers who know specific concept like rails active record to hit the ground running with Amplify.  </p> <p>The reason I see for doing creating an Active Record for Linq to SQL is so that you can wrap validation, business/domain logic and encapsulate it into one object, versus having to know where the separate rules are kept for data, which can create a simpler api for the developer and clue a person to in to what the domain entity is capable of and should be able to do.  This would hopefully shield the developer from being tempted to do the following with Linq to Sql, which could lead to invalid data or even worse.  </p> <pre class="code csharp"> private void Bind() { // bad practice 645 using(DataContext db = CreateContext()) { 646 this.datagrid.DataSource = (from o in db.Clients select o); 647 this.datagrid.DataBind(); 648 } 649 }</pre> 650 651<p>Which is a good reason for Amplify to help provide some basic interfaces and even base classes / wrappers for Linq. Now the question is, what do you support? Do support the linq objects that sqlmetal generates, provide your own CodeGen tool for Visual Studio?  Since <a href="http://msdn2.microsoft.com/en-us/library/bb386987.aspx" rel="tag">SqlMetal</a> generates the partial on<strong><em>PropertyName</em></strong>Change methods which are crude and does not help to reduce code or really support code reuse. If you support classes that SqlMetal generates, you need to use some form of Reflection or a Linq Expression to do any kind of business rules evaluation of the incoming values.  Do you create your own kind of UnitOfWork pattern and force Linq To Sql to work disconnected and keep track of your own changes, if not what is the best way of keeping track of the DataContext that your object is now tied to?  </p> 652 653<p>And if you do your own UnitOfWork pattern, you have to either add a binary timestamp or int version to the table or you have to keep the intial values of the object stashed or you have to fetch the current object in the database in order to use DataContext.GetTable<T>().Attach(entity, oldEntity); Not exactly what I would call very performant (yes I know, its not in the dictionary, but I like the word performant). </p> 654 655<p>If you have any comments, suggestions, questions, feel free to drop me a line. </p> <p class="blogger-labels">Labels: <a rel='tag' href="http://www.amptools.net/labels/Active Record.aspx">Active Record</a>, <a rel='tag' href="http://www.amptools.net/labels/Amplify.aspx">Amplify</a>, <a rel='tag' href="http://www.amptools.net/labels/Linq.aspx">Linq</a>, <a rel='tag' href="http://www.amptools.net/labels/Linq To Sql.aspx">Linq To Sql</a></p> 656 </div> 657 <div class="blog-footer"> 658 <a class="comment-link" href="https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1442753008958214520"location.href=https://www.blogger.com/comment.g?blogID=7043853799247804655&postID=1442753008958214520;><span style="text-transform:lowercase">0 Comments</span></a> 659 660 <a class="comment-link" href="http://www.amptools.net/2008/04/thoughts-on-developing-linq-to-sql.aspx#links"><span style="text-transform:lowercase">Links to this post</span></a> 661 <span class="item-action"><a href="http://www.blogger.com/email-post.g?blogID=7043853799247804655&postID=1442753008958214520" title="Email Post"><img class="icon-action" alt="" src="http://www.blogger.com:80/img/icon18_email.gif" height="13" width="18"/></a></span> 662 663 <script type="text/javascript" src="http://w.sharethis.com/widget/?tabs=web%2Cemail&charset=utf-8&services=reddit%2Cdigg%2Cfacebook%2Cmyspace%2Cdelicious%2Cstumbleupon%2Ctechnorati%2Cpropeller%2Cblinklist%2Cmixx%2Cnewsvine%2Cgoogle_bmarks%2Cyahoo_myweb%2Cwindows_live%2Cfurl&style=rotate&publisher=baa2824f-9291-46c0-87b4-6d5a72508a05&headerbg=%23dddddd&inactivebg=%231bb601&inactivefg=%2370eb00%09&linkfg=%231bb601"></script> 664 </div> 665 666 667 668 <h2 class="blog-title"><a name="8687698153489958182"></a> 669 670 Gallio & Mb-Unit 3 With BDD style tests, I mean specs 671 672 <span class="blog-byline"> 673 by: michael herndon, at: 4/05/2008 08:49:00 PM 674 </span> 675 676 </h2> 677 <div class="blog-content"> 678 <p><a href="http://www.amptools.net/content/users/michaelherndon/images/GallioMbUnit3withBDDstyletestsImeanspecs_11EDC/behaviordrivendevelopmentex.jpg"><img style="margin: 0px 0px 10px 25px" height="148" alt="Only sky is the limit" src="http://www.amptools.net/content/users/michaelherndon/images/GallioMbUnit3withBDDstyletestsImeanspecs_11EDC/behaviordrivendevelopmentex_thumb.jpg" width="240" align="right" /></a><a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development">BDD</a>, Behavior Driven Development, is basically a fusio…
Large files files are truncated, but you can click here to view the full file