<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9665608</id><updated>2026-04-15T02:03:08.088-07:00</updated><title type='text'>The way less traveled by...</title><subtitle type='html'>hopefully?</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default?alt=atom'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default?alt=atom&amp;start-index=26&amp;max-results=25'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>151</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9665608.post-4555203417764890301</id><published>2017-03-02T09:52:00.000-08:00</published><updated>2017-03-02T09:52:08.964-08:00</updated><title type='text'>Dynamics 365 Web API Create and Update</title><content type='html'>&lt;script src=&quot;//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
pre.prettyprint {
border:none;
background-color: #F2F2F2;
padding: 16px;
}
&lt;/style&gt;
&lt;br /&gt;
&lt;h2&gt;
Recap&lt;/h2&gt;
&lt;div&gt;
In the &lt;a href=&quot;http://camtucker.blogspot.com/2016/12/how-to-build-your-own-dynamics-365-web.html&quot;&gt;previous post&lt;/a&gt; we built a C# client for Dynamics 365 Web API using &lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt;. Then we talked about &lt;a href=&quot;http://camtucker.blogspot.ca/2016/12/dynamics-365-web-api-custom-c-client.html&quot;&gt;reading multiple result sets&lt;/a&gt;, allowing us to do this:
&lt;pre class=&quot;prettyprint&quot;&gt;var contact = Client.Get&amp;lt;Contact&amp;gt;(&quot;/contacts(&quot; + contactGuid + &quot;)&quot;);
var contacts = ContactRepository.GetContacts();
&lt;/pre&gt;
&lt;div&gt;
Now let&#39;s do a couple more items in CRUD (create-read-update-delete), namely create and update:&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
var updatedContact = ContactRepository.Update(contact);
&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Create and Update&lt;/h2&gt;
&lt;div&gt;
ServiceStack makes this very easy, though there are a couple of gotchas.
&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
public string create(Contact contactToCreate)
{
    // as mentioned before you&#39;ll want to insulate your model from the web api,
    // requiring conversion back and forth
    var odataContact = toCRMContact(contactToCreate);

    // ServiceStack helper to get us something that we can manipulate before we send it
    var contactDictionary = odataContact.ToStringDictionary();

    // gotcha #1
    // if you post with a contactid it will likely error,
    // as this isn&#39;t part of the &#39;create&#39; contract web api
    contactDictionary.Remove(&quot;contactid&quot;);

    // happily we never have to worry about authentication or serialization
    // thanks to our custom c# web api client
    var result = Client.Post&lt;HttpWebResponse&gt;(&quot;/contacts&quot;, contactDictionary);

    // custom helper
    var guid = getGuidFromODataWebResponse(result);

    // gotcha #2
    // if you don&#39;t dispose the HttpWebResponse you&#39;ll eventually run out of sockets
    // you could also wrap this in a &quot;using&quot; block
    result.Dispose();

    return guid;
}

public string update(Contact contactToCreate)
{   
    var result = Client.Patch&lt;HttpWebResponse&gt;(&quot;/contacts(&quot; + contactToUpdate.guid + &quot;)&quot;,
        toCRMContact(contactToUpdate));
    var guid = getGuidFromODataWebResponse(result);
    result.Dispose();

    return guid;
}

// example of the conversion
private static ODataContact toCRMContact(Contact contact)
{
    // generated enums
    // https://msdn.microsoft.com/en-ca/library/hh547435.aspx
    var genderCode = (int)ContactGenderCode.Unknown;
    if(contact.gender == &quot;M&quot;)
        genderCode = (int)ContactGenderCode.Male;
    else if(contact.gender == &quot;F&quot;)
        genderCode = (int)ContactGenderCode.Female;

    return new ODataContact
    {
        contactid = Guid.Parse(contact.guid),
        gendercode = genderCode
    }
}

// helper to grab the guid from the response
private string getGuidFromODataWebResponse(HttpWebResponse response)
{
    var entityId = response.Headers[&quot;OData-EntityId&quot;].Split(&#39;(&#39;).Last();
    return entityId.Remove(entityId.Length - 1, 1);
}
&lt;/pre&gt;
&lt;h2&gt;But what about @odata.bind?&lt;/h2&gt;
&lt;div&gt;
OData has an interesting way of dealing with relationships which unfortunately doesn&#39;t lend itself well to strongly-typed objects; &quot;somefield@odata.bind&quot; isn&#39;t a legal c# field name. For these cases we&#39;ll use ServiceStack&#39;s JsonObject. In this example we&#39;ll do an update to the contact&#39;s owner.
&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
public void setContactOwner(string contactGuid, string username)
{
    var response = Client.Patch&lt;HttpWebResponse&gt;(&quot;/contacts(&quot; + contactGuid + &quot;)&quot;,
        new JsonObject
        {
            [&quot;ownerid@odata.bind&quot;] = &quot;/systemusers(&quot; + getSystemUserByUsername(username) + &quot;)&quot;,
        });
    response.Dispose();
}

protected string getSystemUserByUsername(string username)
{
    var userId = Client.userId;

    var users = Client.Get&lt;SystemUsers&gt;(&quot;/systemusers?$filter=startswith(domainname,&#39;&quot; + username + &quot;&#39;)&quot;);
    if (users.Value != null &amp;&amp; users.Value.Any())
        userId = users.Value.First().systemuserid.ToString();
    return userId;
}

public class SystemUsers
{
    public List&lt;SystemUser&gt; Value { get; set; }
}

public class SystemUser
{
    public Guid? systemuserid { get; set; }
    public string firstname { get; set; }
    public string lastname { get; set; }
    public string title { get; set; }
    public string domainname { get; set; }
}
&lt;/pre&gt;
&lt;h2&gt;Activities&lt;/h2&gt;
&lt;div&gt;
Dynamics365 activities have a couple of gotchas worth mentioning. First, you can&#39;t create completed activities with the Web API (you could with the SDK), and second, you can&#39;t update completed activities (so you&#39;ll have to open them first).
&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
private string createEmail(Email email, string entityName)
{
    var userId = getSystemUserByUsername(email.related_username);

    // generated enums
    // https://msdn.microsoft.com/en-ca/library/hh547435.aspx
    var emailParties = new List&lt;JsonObject&gt;
    {
        new JsonObject
        {
            [&quot;partyid_systemuser@odata.bind&quot;] = &quot;/systemusers(&quot; + userId + &quot;)&quot;,
            [&quot;participationtypemask&quot;] = 
                  Soap.ActivityPartyParticipationTypeMask.Sender.ToString(&quot;D&quot;)
        },
        new JsonObject
        {
            [&quot;partyid_&quot; + entityName + &quot;@odata.bind&quot;] = &quot;/&quot; + entityName + 
                  &quot;s(&quot; + email.regarding_guid + &quot;)&quot;,
            [&quot;participationtypemask&quot;] = 
                  Soap.ActivityPartyParticipationTypeMask.ToRecipient.ToString(&quot;D&quot;)
        }
    };

    var emailToCreate = new JsonObject
    {
        [&quot;subject&quot;] = email.subject,
        [&quot;description&quot;] = email.description.Replace(&quot;\n&quot;,&quot;&lt;br&gt;&quot;),
        [&quot;torecipients&quot;] = email.email,
        [&quot;regardingobjectid_&quot; + entityName + &quot;_email@odata.bind&quot;] = &quot;/&quot; + entityName + 
                       &quot;s(&quot; + email.regarding_guid + &quot;)&quot;,
        [&quot;emailsender_&quot; + entityName + &quot;@odata.bind&quot;] = &quot;/&quot; + entityName + 
                       &quot;s(&quot; + email.regarding_guid + &quot;)&quot;,
        [&quot;email_activity_parties&quot;] = emailParties.ToJson(), // ServiceStack helper
        [&quot;directioncode&quot;] = &quot;false&quot;, // inbound
        [&quot;ownerid@odata.bind&quot;] = &quot;/systemusers(&quot; + getSystemUserByUsername(email.related_username) + &quot;)&quot;
    };

    // if your payload has optional values you shouldn&#39;t include an entry unless you&#39;ve got something to send
    if (email.import_id.HasValue)
        emailToCreate[&quot;importsequencenumber&quot;] = email.import_id.ToString();

    if (email.is_outbound)
        emailToCreate[&quot;directioncode&quot;] = &quot;true&quot;;
    
    // date conversions to the web api aren&#39;t fun
    if(email.added_date.HasValue)
        emailToCreate[&quot;overriddencreatedon&quot;] = 
              DateTime.SpecifyKind(email.added_date.GetValueOrDefault(), DateTimeKind.Utc).ToString(&quot;O&quot;);

    var result = Client.Post&lt;HttpWebResponse&gt;(&quot;/emails&quot;, emailToCreate);

    var entityId = getGuidFromODataWebResponse(result);
    result.Dispose();

    // gotcha!
    // if you want to complete the email you must do it in a second operation
    if (email.is_completed)
        completeActivity(entityId, &quot;emails&quot;);

    return entityId;
}
private string updateEmail(Email email)
{
    if (string.IsNullOrEmpty(email.guid))
        throw new WebServiceException(&quot;Unable to update a email without a guid&quot;);

    openActivity(email.guid, &quot;emails&quot;);

    var stateCode = &quot;0&quot;;
    if (email.is_completed)
        stateCode = &quot;1&quot;;


    // just allow updating the basics
    var CRMActivityToUpdate = new JsonObject
    {
        [&quot;importsequencenumber&quot;] = email.import_id.ToString(),
        [&quot;subject&quot;] = email.subject,
        [&quot;description&quot;] = email.description,
        [&quot;directioncode&quot;] = &quot;false&quot;,
        [&quot;statecode&quot;] = stateCode
    };

    // did I mention that dates are a pain?
    if (email.added_date.HasValue)
        CRMActivityToUpdate[&quot;overriddencreatedon&quot;] = yourDateToUTC(email.added_date);

    if (email.is_outbound)
        CRMActivityToUpdate[&quot;directioncode&quot;] = &quot;true&quot;;

    var response = Client.Patch&lt;HttpWebResponse&gt;(&quot;/emails(&quot; + email.guid + &quot;)&quot;, CRMActivityToUpdate);
    response.Dispose();

    return email.guid;
}
private void openActivity(string guid, string entityPluralName)
{
    var response = Client.Patch&lt;HttpWebResponse&gt;(&quot;/&quot; + entityPluralName + 
                   &quot;(&quot; + guid + &quot;)&quot;, new { statecode = 0 });
    response.Dispose();
}
private void completeActivity(string guid, string entityPluralName)
{
    var response = Client.Patch&lt;HttpWebResponse&gt;(&quot;/&quot; + entityPluralName + 
                   &quot;(&quot; + guid + &quot;)&quot;, new { statecode = 1 });
    response.Dispose();
}
protected string yourDateToUTC(DateTime? date)
{
    if (!date.HasValue || date.GetValueOrDefault().Year &lt; 1900)
        date = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
    
    var convertedDate = TimeZoneInfo.ConvertTimeToUtc(date.GetValueOrDefault(), TimeZoneInfo.Local);
    return convertedDate.ToString(&quot;O&quot;);
}
&lt;/pre&gt;
&lt;h2&gt;Integers and Edm.String&lt;/h2&gt;
&lt;div&gt;OData is rather protective of its strings - don&#39;t you dare submit an integer! Sigh. ServiceStack can serialize anonymous types, which work! &lt;a href=&#39;http://docs.servicestack.net/http-utils&#39;&gt;http://docs.servicestack.net/http-utils&lt;/a&gt;&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
// some custom entity
// slightly different pattern to show how you might manage exceptions
// with ServiceStack&#39;s generic ResponseError
public ResponseError updatePayment(string guid, string financeId)
{
    if (string.IsNullOrEmpty(guid))
        return new ResponseError { Message = &quot;Unable to update payment. Guid must be provided.&quot; };

    // can&#39;t use jsonobject because OData can&#39;t figure out how to convert the int to Edm.String
    var paymentToUpdate = new
    {
        new_finance_id = financeId
    };

    try
    {
        var response = Client.Patch&lt;HttpWebResponse&gt;(&quot;/payment_entities(&quot; + guid + &quot;)&quot;, paymentToUpdate);
        response.Dispose();
    }
    catch (WebServiceException webEx)
    {
        return new ResponseError { ErrorCode = webEx.ErrorCode, Message = webEx.Message };
    }
    return new ResponseError();
}
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/4555203417764890301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/4555203417764890301' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4555203417764890301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4555203417764890301'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2017/03/dynamics-365-web-api-create-and-update.html' title='Dynamics 365 Web API Create and Update'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-5621942249744741447</id><published>2016-12-22T18:07:00.000-08:00</published><updated>2017-03-01T15:09:23.423-08:00</updated><title type='text'>Dynamics 365 Web API Custom C# Client Result Sets</title><content type='html'>&lt;script src=&quot;//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
pre.prettyprint {
border:none;
background-color: #F2F2F2;
padding: 16px;
}
&lt;/style&gt;
&lt;br /&gt;
&lt;h2&gt;
Recap&lt;/h2&gt;
&lt;div&gt;
In the &lt;a href=&quot;http://camtucker.blogspot.com/2016/12/how-to-build-your-own-dynamics-365-web.html&quot;&gt;previous post&lt;/a&gt; we built a C# client for Dynamics 365 Web API using &lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt;. The benefits include: deserialization to strongly-typed objects, baked-in authentication, and with a repository pattern we essentially treat the Web API as a database, translating contexts and safe-guarding against changes that may come in the future.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Baking this cake takes some time, but the results are beautiful!&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;var contact = Client.Get&amp;lt;Contact&amp;gt;(&quot;/contacts(&quot; + contactGuid + &quot;)&quot;);
&lt;/pre&gt;
&lt;div&gt;
The next step is to query multiple results at once:&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;var contacts = ContactRepository.GetContacts();
&lt;/pre&gt;
&lt;div&gt;
Notice anything different here? We can&#39;t simply deserialize a result because we need to deal with multiple pages of results, which is going to take several roundtrips to the Web API. We&#39;ll hide all that heavy lifting inside our repository.
&lt;/div&gt;
&lt;h2&gt;
Paged Results&lt;/h2&gt;
When the Web API has &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/gg334767.aspx&quot;&gt;multiple results&lt;/a&gt;, and their number exceeds the page size (which cannot be greater than 5,000), then it returns the results in pages, aka result sets. OData actually does something great here. Most APIs will give us the current page, the total number of pages, and the results on each page. Navigating through the pages is kind of a pain. Happily, Web API provides an @odata.nextLink, which is all we need to get the next page!
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;// The JSON &quot;Value&quot; in the result set is our list of contacts
public class Contacts
{
    public List&amp;lt;Contact&amp;gt; Value { get; set; } 
}

public List&amp;lt;Contact&amp;gt; getContactsPsuedo()
{
    // a holding tank for our results
    var result = new List&amp;lt;Contact&amp;gt;();

    // we make our initial query and just throw it in a string for now
    var contactsString = Client.Get&amp;lt;string&amp;gt;(&quot;/contacts&quot;);

    // ServiceStack has a bunch of great helper function like .FromJson&lt;t&gt;
    var contacts = contactsString.FromJson&amp;lt;Contacts&amp;gt;();

    // grab the results from this page
    result.AddRange(contacts.Value);

    if (contactsString.Contains(&quot;@odata.nextLink&quot;))
    {
        // (another ServiceStack helper)
        // parse the string into a dictionary and grab the link
        var nextUrl = JsonObject.Parse(contactsString).Get(&quot;@odata.nextLink&quot;);

        // the url will have https://&amp;lt;yourtenant&amp;gt;.crm.dynamics.com/api/etc
        // but our client already has this as it&#39;s base url, so it just needs the query
        var nextLink = nextUrl.Replace(Client.apiUrl, &quot;/&quot;);
        contactsString = Client.Get&amp;lt;string&amp;gt;(nextLink);

        // then we iterate!
    }
}

// here&#39;s the real thing
// though this should probably be refactored to accommodate any entity
public List&amp;lt;Contact&amp;gt; getContacts()
{
    var result = new List&amp;lt;Contact&amp;gt;();

    var contactsString = Client.Get&amp;lt;string&amp;gt;(&quot;/contacts&quot;);

    while (true)
    {
        var contacts = contactsString.FromJson&amp;lt;Contacts&amp;gt;();

        if (contacts.Value.Count &amp;gt; 0)
        {
            result.AddRange(contacts.Value);
        }

        // when the result set has a nextLink keep going
        if (contactsString.Contains(&quot;@odata.nextLink&quot;))
        {
            var nextUrl = JsonObject.Parse(contactsString).Get(&quot;@odata.nextLink&quot;);

            var nextLink = nextUrl.Replace(Client.apiUrl, &quot;/&quot;);
            contactsString = Client.Get&amp;lt;string&amp;gt;(nextLink);
        }
        else // otherwise we&#39;re done
        {
            break;
        }
    }
    return result;
}&lt;/t&gt;&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/5621942249744741447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/5621942249744741447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5621942249744741447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5621942249744741447'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2016/12/dynamics-365-web-api-custom-c-client.html' title='Dynamics 365 Web API Custom C# Client Result Sets'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1561981819350566213</id><published>2016-12-06T17:21:00.005-08:00</published><updated>2017-02-21T07:22:11.673-08:00</updated><title type='text'>Build Your Own Dynamics 365 Web API C# Client on ServiceStack</title><content type='html'>&lt;script src=&quot;//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
pre.prettyprint {
border:none;
background-color: #F2F2F2;
padding: 16px;
}
&lt;/style&gt;
&lt;br /&gt;
&lt;h2&gt;
Build Your Own Client&lt;/h2&gt;
&lt;div&gt;
The days of writing code with the Dynamics 365 SDK are &lt;a href=&quot;https://msdn.microsoft.com/en-ca/library/dn281891.aspx&quot;&gt;coming to an end&lt;/a&gt;. The Web API lets us work on any platform, but it has some short-comings. &amp;nbsp;First, there is a high degree of complexity and &lt;a href=&quot;http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part1-protocol.html&quot;&gt;challenging documentation&lt;/a&gt; (is that 8 pt font?); the price you pay for a one-size-fits-all querying language. Second, Microsoft &lt;a href=&quot;https://msdn.microsoft.com/en-ca/library/mt593051.aspx&quot;&gt;does not provide a client&lt;/a&gt;:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;Because the Web API is built on open standards, we don’t provide assemblies for a specific developer experience. You can compose HTTP requests for specific operations or use third-party libraries to generate classes for whatever language or platform you want. You can find a list of libraries that support OData version 4.0 at&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://www.odata.org/libraries/&quot; style=&quot;color: #00709f; font-family: &amp;quot;Segoe UI&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Verdana, Arial, Helvetica, sans-serif; font-size: 13px; text-decoration: none;&quot;&gt;http://www.odata.org/libraries/&lt;/a&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;.&lt;/span&gt;&lt;/blockquote&gt;
&lt;br /&gt;
After spending a few days with the various options, and hitting show-stopping roadblocks, I was not in love with the Web API. Microsoft, if you&#39;re listening, the OData clients out there aren&#39;t very good - please fix. In this series of blog posts I&#39;ll take you through implementing your own client using &lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt;. Here&#39;s a short example of the result to keep you motivated. :)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
A Simple Example&lt;/h2&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;var contact = Client.Get&amp;lt;Contact&amp;gt;(&quot;/contacts(&quot; + contactGuid + &quot;)&quot;);
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
There are three things to note here:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;All of the OData complexity is hidden behind our client, including authorization. The only complexity left to us is constructing proper OData queries, and a few special situations where we can&#39;t automatically deserialize the stuff that OData gives us (for example when we have paged data sets).&lt;/li&gt;
&lt;li&gt;Deserialization from JSON to our strongly-typed object is done for us.&lt;/li&gt;
&lt;li&gt;We need to define our strongly-typed objects by hand. Small amount of pain, big win I promise.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
There are several concerns here though. We really don&#39;t want to expose the Dynamics 365 field names to the rest of our application, and heaven forbid any other applications that might use this intermediate API. Why?&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;We can&#39;t control whether Microsoft decides to change field names, or for that matter if they change to another API. &lt;strong&gt;Always insulate things that are likely to change.&lt;/strong&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;OData is going to give us integers for option sets, unless we do some pretty fancy querying. &lt;strong&gt;Always translate language when you cross to different domains.&lt;/strong&gt; In other words, the language that Dynamics 365 uses to describe an option set is meaningless elsewhere, so don&#39;t force anything else to know it.&lt;/li&gt;
&lt;/ul&gt;
All of this is happily solved by using the repository pattern.
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;public class ContactRepository
{
   protected IDynamicsCRMClient Client { get; set; }

   public ContactRepository(IDynamicsCRMClient client)
   {
      Client = client; // our magical client which we&#39;ll build below
   }

   public MyContact getByGuid(string contactGuid)
   {
      return toMyContact(Client.Get&amp;lt;Contact&amp;gt;(&quot;/contacts(&quot; + contactGuid + &quot;)&quot;));
   }

   public MyContact toMyContact(Contact contact)
   {
      if (contact == null)
         return new MyContact();

      // OptionSets can be generated using the Dynamics 365 SDK
      // \SampleCode\CS\CrmSvcUtilExtensions\GeneratePicklistEnums

      var gender = &quot;&quot;;
      if (contact.gendercode == (int)ContactGenderCode.Male)
         gender = &quot;M&quot;;
      else if (contact.gendercode == (int)ContactGenderCode.Female)
         gender = &quot;F&quot;;

      return new MyContact
      {
         guid = contact.contactid.ToString(),
         first_name = contact.firstname,
         middle_name = contact.middlename,
         last_name = contact.lastname,
         gender = gender // etc
      }
   }
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Still interested? This approach might involve too much insulation for simple applications, but if we&#39;re building for the enterprise, we need to invest in good code. Now let&#39;s take care of actually talking with the Web API.&lt;br /&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;h2&gt;
Extending the ServiceStack Client&lt;/h2&gt;
&lt;div&gt;
ServiceStack comes with a &lt;a href=&quot;http://docs.servicestack.net/csharp-client&quot;&gt;JSON client&lt;/a&gt; that can be extended.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;public class DynamicsCRMClient : ServiceClientBase
{
   // there are a bunch of settings you&#39;ll need to inject.
   // OAuth 2.0 is fairly cumbersome
   private static string _crmUrl;
   private static string _apiUrl;
   private static string _oauthUrl;
   // ...

   public DynamicsCRMClient(string crmUrl, string apiVersion, string oauthUrl...)
   {
      _crmUrl = crmUrl;
      _oauthUrl = oauthUrl;
      _apiUrl = crmUrl + &quot;/api/data/&quot; + apiVersion + &quot;/&quot;;
      // the rest of the settings...

      // all our calls to the client will use this base url
      SetBaseUri(_apiUrl);

      // add these headers to every outgoing request
      this.RequestFilter = httpReq =&amp;gt;
      {
         // request a token for every call is best practice according to
         // https://msdn.microsoft.com/en-us/library/gg327838.aspx
         // rather than checking expiration. But it&#39;s up to you.
         // I&#39;ve settled on checking the expiration.
         httpReq.Headers.Add(&quot;Authorization&quot;, &quot;Bearer &quot; + getToken());
         httpReq.Headers.Add(&quot;OData-MaxVersion&quot;, &quot;4.0&quot;);
         httpReq.Headers.Add(&quot;OData-Version&quot;, &quot;4.0&quot;);
      };
   }

   private static string getToken()
   {
      // if the token hasn&#39;t expired simply reuse it
      var unixTime = getUnixTime();
      if (expiresOn.HasValue &amp;amp;&amp;amp; unixTime + 600 &amp;lt; expiresOn.GetValueOrDefault())
         return accessToken;

      return GetAuthenticationResponse().access_token;
   }

   private static int getUnixTime()
   {
      var dateTimeOffset = new DateTimeOffset(DateTime.UtcNow);
      return (int) dateTimeOffset.ToUnixTimeSeconds();
   }

   // OAuth will give us all this if we authenticate
   private class AuthenticationResponse
   {
      public string token_type { get; set; }
      public string scope { get; set; }
      public int expires_in { get; set; }
      public int expires_on { get; set; }
      public int not_before { get; set; }
      public string resource { get; set; }
      public string access_token { get; set; } // but this is really all we need
      public string refresh_token { get; set; }
      public string id_token { get; set; }
   }

   private static AuthenticationResponse GetAuthenticationResponse()
   {
      var vals = new NameValueCollection
      {
         {&quot;client_id&quot;, _clientId},
         {&quot;resource&quot;, _crmUrl},
         // all those settings we injected above
      };

      var response = _oauthUrl.PostToUrl(vals.ToFormUrlEncoded(),
         requestFilter: webReq =&amp;gt;
         {
            webReq.Headers[&quot;Cache-Control&quot;] = &quot;no-cache&quot;;
         }
      );

      var authResponse = response.FromJson&amp;lt;AuthenticationResponse&amp;gt;();
      expiresOn = authResponse.expires_on;
      accessToken = authResponse.access_token;
      return authResponse;
   }

   // there are a few methods that we need to overload to
   // patch in our authentication scheme
   // I&#39;ll post the source soon
}
&lt;/pre&gt;
&lt;div&gt;
With that client we can simply ask OData questions by doing Client.Get&amp;lt;TheObjectWeAreExpecting&amp;gt;(&quot;ourODataQuery&quot;), .Post, .Patch, etc.
&lt;br /&gt;
&lt;br /&gt;
Other OData clients may get you that far. But as soon as you need to deal with paged data sets, or $expand&#39;ing into related data, you are going to have to get down to the metal anyway.&lt;br /&gt;
&lt;br /&gt;
In the next post we&#39;ll deal with the paged data sets!&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1561981819350566213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1561981819350566213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1561981819350566213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1561981819350566213'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2016/12/how-to-build-your-own-dynamics-365-web.html' title='Build Your Own Dynamics 365 Web API C# Client on ServiceStack'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1290742364557840962</id><published>2016-03-02T15:04:00.000-08:00</published><updated>2016-12-04T12:39:11.891-08:00</updated><title type='text'>Ruby Gem Your ServiceStack</title><content type='html'>&lt;script src=&quot;//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
pre.prettyprint {
border:none;
background-color: #F2F2F2;
padding: 16px;
}
&lt;/style&gt;
&lt;br /&gt;
&lt;h2&gt;
TL;DR&lt;/h2&gt;
&lt;div&gt;
Here is a &lt;a href=&quot;https://github.com/RegentCollege/regis-ruby&quot;&gt;Ruby Gem&lt;/a&gt;&amp;nbsp;example of how to talk to your ServiceStack.&lt;/div&gt;
&lt;h2&gt;
Let&#39;s Be Platform and Language Agnostic&lt;/h2&gt;
&lt;div&gt;
One of the big wins of a data API built on&amp;nbsp;&lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt;&amp;nbsp;is that our clients can be on any platform and language, not just C#. After all, we&#39;re just talking JSON! This means we can pick the best-of-breed language depending on the task at hand. C# is great on the backend, but ASP.NET Razor can be weak when compared to a client-side Javascript framework like Angular, and some would argue Ruby-on-Rails (I&#39;m just going to call it Ruby from here on). Let&#39;s have a go at Ruby then shall we?&lt;/div&gt;
&lt;h2&gt;
A Ruby Gem for ServiceStack&lt;/h2&gt;
&lt;div&gt;
Ruby is still pretty foreign to me. I need to keep things as simple as possible. Sure, we could just fire HTTP calls at ServiceStack (I do that all the time with PHP and cURL), but I really want to separate the concerns of my API, like logging in, caching, and JSON serialization. That way I can conserve my precious brain space for the problem I&#39;m working on and just call one thing to get my data.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
One of Ruby&#39;s claims to fame is that it is an opinionated language. There is a &quot;Ruby way&quot; to do things. But that&#39;s not really true when you look at the different gems built to consume APIs. Twitter, Stripe, and Instagram all tackle the problem differently, and are way too complicated.&lt;/div&gt;
&lt;h3&gt;
Pick an Approach&lt;/h3&gt;
&lt;div&gt;
The simplest gem I could find was &lt;a href=&quot;https://github.com/Yelp/yelp-ruby&quot;&gt;Yelp&lt;/a&gt;. Basically you define three things: your data models, your endpoints for the different ways the models can be populated, and your response objects that populate the models. Pretty standard stuff.&lt;br /&gt;
&lt;br /&gt;
Here is how we&#39;re going to lay it out. I live in higher ed, so I&#39;ve gone with sections we offer in a term with instructors:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwmIzQSEUhuTzalcP7smi7mxPj9gFiiPbh5NZ_kCldme-86_dmiZHRMubh7pGtVyMs0uhJBdyTSYGbpzyUEvxkYLUvJ5FPBYIEvvuieGBtafTPtlac8jOVv7Q7NXmDctQI2_C18g/s1600/models.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;579&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwmIzQSEUhuTzalcP7smi7mxPj9gFiiPbh5NZ_kCldme-86_dmiZHRMubh7pGtVyMs0uhJBdyTSYGbpzyUEvxkYLUvJ5FPBYIEvvuieGBtafTPtlac8jOVv7Q7NXmDctQI2_C18g/s640/models.jpeg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
The gem will work like this:&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;require &#39;servicestack&#39;

ServiceStack.client.configure do |config|
  config.url = &quot;https://theurl&quot;
  config.username = &quot;the_username&quot;
  config.password = &quot;the_password&quot;
  config.cache = ServiceStack::Cache.new(Dalli::Client.new(&#39;localhost:11211&#39;, { :namespace =&amp;gt; &quot;servicestack&quot;, :compress =&amp;gt; true, :expires_in =&amp;gt; 3600 }))
end

@single_section_response = ServiceStack.client.section_get_by_uuid(&quot;the_section_uuid&quot;)
p @single_section_response.section.secUUID # here it is!

@multiple_section_response = ServiceStack.client.sections_get_by_reporting_term(&quot;the_term&quot;)
p @multiple_section_response.sections.count # more than 1!&lt;/pre&gt;
&lt;h3&gt;
Caching&lt;/h3&gt;
&lt;div&gt;
This particular gem will be used by a high-volume website and I don&#39;t want it hammering my API, which services lots of mission critical stuff. So baking in caching was very important. I left it up to the implementer to inject whatever caching engine they liked using config.cache - it just has to respect a few simple methods: initialize, write, read, delete.&lt;br /&gt;
&lt;br /&gt;
One important caveat: Caching only works when the URL is idempotent. i.e. you can&#39;t use POST. The problem is that the URL is the cache key, but POST is not part of the URL! So asking different questions is going to get you just one answer. There are definitely ways around this, like hashing the POST payload into the URL, but for proof of concept I&#39;m just using GET requests.&lt;/div&gt;
&lt;h3&gt;
The Guts of Calling ServiceStack&lt;/h3&gt;
&lt;h4 style=&quot;clear: both; text-align: left;&quot;&gt;
Configuring the client&lt;/h4&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;a href=&quot;https://github.com/lostisland/faraday&quot;&gt;Faraday&lt;/a&gt;&amp;nbsp;will do the heavy lifting for us. &lt;a href=&quot;https://github.com/dwaite/cookiejar&quot;&gt;CookieJar&lt;/a&gt; will remember our &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/Sessions&quot;&gt;ServiceStack session&lt;/a&gt;. It&#39;s worth going over this in a bit of detail, so here is a snippet from our client:&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;def connection
return @connection if instance_variable_defined?(:@connection)
 @connection = Faraday.new(@configuration.url, :ssl =&amp;gt; {:verify =&amp;gt; false}) do |faraday|
  # ssl verify false for self-signed certificates

  faraday.basic_auth(@configuration.username, @configuration.password)
  # ServiceStack supports other auth, but this is the simplest

  faraday.request :json
  # our HTTP header content-type on the way out

  faraday.response :json, :content_type =&amp;gt; /\bjson$/
  # expect content-type JSON on the way in

  #faraday.response :logger # uncomment if you want to see what&#39;s going on
  
  # faraday has some middleware that handles our cache
  # https://github.com/lostisland/faraday_middleware/wiki/Caching
  faraday.response :caching do
   @configuration.cache
  end
      
  faraday.use :cookie_jar # Preserve the ServiceStack session
  faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
 end
end
&lt;/pre&gt;
&lt;br /&gt;
&lt;h4 style=&quot;clear: both;&quot;&gt;
Making the call&lt;/h4&gt;
Our endpoints are injected with the client. Then they&#39;ll use our connection method to grab the data. Here is the endpoint we used in the above example:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;class Section
 def initialize(client)
    @client = client
  end
  def section_get_by_uuid(uuid)
    response = @client.connection.get &quot;/Section/#{uuid}&quot;, { :format =&amp;gt; &#39;json&#39; }
    # I&#39;m hard-coding my ServiceStack endpoint to this method. This feels brittle to me.
    # I also had to add the :format json here too, which I shouldn&#39;t have to

    # define which response handles deserialization
    Response::Section.new(response.body)
  end
end
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;h4&gt;
Deserializing&lt;/h4&gt;
&lt;div&gt;
Our base response will step through the JSON to deliver the class &quot;klass&quot; we inject:&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;class Base
  def initialize(json)
    return if json.nil?

    json.each do |key, value|
      instance_variable_set(&quot;@#{key}&quot;, value)
    end
  end

  private

  def parse(json, klass)
    return json.collect { |j| klass.new(j) } if json.is_a?(Array)
    return klass.new(json) if json
    nil
  end
end
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
Then our section response simply inherits from the Base and injects the model to populate:&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;class Section &amp;lt; Base
  attr_reader :section
 
  def initialize(json)
    @section = parse(json, Model::Section)
  end
end
&lt;/pre&gt;
&lt;br /&gt;
And finally we have our model which has the same field names that we deserialized:&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;class Section &amp;lt; Response::Base
  attr_reader :secUUID
  attr_reader :course_name
  attr_reader :course_description
  attr_reader :course_title
  attr_reader :section_code
  ...
  # instructors are embedded in each section
  attr_reader :instructors

  def initialize(json)
    super(json)
 
    @instructors = parse(@instructors, Instructor)
  end

  ...
&lt;/pre&gt;
&lt;br /&gt;
That&#39;s it! Now we can grab our section with a one-liner: ServiceStack.client.section_get_by_uuid(&quot;the_section_uuid&quot;). Our data API is decoupled because it&#39;s limited to this gem.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
The Demon Behind the Curtain&lt;/h2&gt;
&lt;div&gt;
If you&#39;re coming from a C# background you need to know something very important about Ruby: It abuses objects. In other words, the class you define can change dynamically during runtime. C# tends to keep our hands tied, but in Ruby you can do some pretty weird stuff, like monkey patching, and in this case method dispatching.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Consider the client again:&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;  @single_section_response = ServiceStack.client.section_get_by_uuid(&quot;the_section_uuid&quot;)
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Doesn&#39;t it seem strange to you that our &lt;a href=&quot;https://github.com/RegentCollege/regis-ruby/blob/master/lib/regis/client.rb#L55&quot;&gt;client&lt;/a&gt; is somehow inheriting our section endpoint?&lt;br /&gt;
&lt;br /&gt;
At runtime the client loops through all the endpoints you&#39;ve defined and attaches their methods to itself! Being a Ruby newbee I left it that way, assuming that&#39;s how things are done in this language. Who am I to question its opinions?&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1290742364557840962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1290742364557840962' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1290742364557840962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1290742364557840962'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2016/03/ruby-gem-your-servicestack.html' title='Ruby Gem Your ServiceStack'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwmIzQSEUhuTzalcP7smi7mxPj9gFiiPbh5NZ_kCldme-86_dmiZHRMubh7pGtVyMs0uhJBdyTSYGbpzyUEvxkYLUvJ5FPBYIEvvuieGBtafTPtlac8jOVv7Q7NXmDctQI2_C18g/s72-c/models.jpeg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-411800133688433227</id><published>2016-03-01T14:02:00.003-08:00</published><updated>2016-07-04T08:04:20.225-07:00</updated><title type='text'>Test Drive Angular 2 with Meteor</title><content type='html'>&lt;h2&gt;
TL;DR&lt;/h2&gt;
&lt;div&gt;
Angular 2 is worth taking another look at Angular. It has simpler concepts and less jargon to learn. And &lt;a href=&quot;http://www.angular-meteor.com/tutorials/socially/angular2/bootstrapping&quot;&gt;the Meteor tutorial&lt;/a&gt; is a great way to do it.&lt;/div&gt;
&lt;h2&gt;
The Internet is Slow&lt;/h2&gt;
Client-side Javascript frameworks solve one of the biggest problems for web-based apps: round-trips to your server. Every time a user navigates around your app, their browser has to ask for data. In a mobile-first world this can be quite painful.&lt;br /&gt;
&lt;br /&gt;
One of the ways to make this a bit better is to selectively update the DOM with JS, rather than doing a whole page refresh.&lt;br /&gt;
&lt;br /&gt;
Take a simple shopping cart example. When the customer selects a product, adds shipping in checkout, and selects a payment method we want to update the shopping cart somewhere on the page. Instead of refreshing everything when they make a selection, we update the shopping cart with jQuery (or vanilla JS).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0EvDlpktNudIHGAWRXp1qgOi1umsl_yhcvIF9D5YAVBtZWtjFWapNtZXOHGF0SlVvkmv3L5lDR9H35R756C1cvnzgyXXa4p9wT50Gpk6PxeMItmm_3ZjqaV9nqMzcxMG_jkAhKA/s1600/MVC_jquery.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;257&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0EvDlpktNudIHGAWRXp1qgOi1umsl_yhcvIF9D5YAVBtZWtjFWapNtZXOHGF0SlVvkmv3L5lDR9H35R756C1cvnzgyXXa4p9wT50Gpk6PxeMItmm_3ZjqaV9nqMzcxMG_jkAhKA/s640/MVC_jquery.jpeg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
The only abstraction between us and the DOM at this point are the jQuery helpers - onChange(), val(), etc. In other words, what you see displayed on the page is roughly what you see in your HTML, CSS, and JS. And life is good because it&#39;s relatively simple and speeds things up a bit.&lt;br /&gt;
&lt;br /&gt;
But this isn&#39;t great. Think of how many times you might go back and forth between different products, over to your checkout to see whether you qualify for free shipping yet, etc. And if you&#39;re on mobile (or your server is under heavy load), every time you do something you might be waiting several seconds. And your customer loses interest.&lt;br /&gt;
&lt;h2&gt;
Javascript to the Rescue (again)&lt;/h2&gt;
Instead of asking the server for the data, let&#39;s give more data to the browser in the first place and let it decide when to render it. In fact, let&#39;s put an MVC into Javascript! The user can navigate around without chatting with your server. To do that JS needs to change the DOM in very complex ways, much more than jQuery and CSS can do. But what&#39;s the best way to do it? There&#39;s the rub. Backbone, Knockout, Ember, Angular, React, take your pick, mix and match. And the price is added complexity - &lt;i&gt;a lot of it&lt;/i&gt;.&lt;br /&gt;
&lt;h3&gt;
Angular&lt;/h3&gt;
In Angular&#39;s world you don&#39;t change the DOM. Instead, your job is to interact with the &quot;Shadow DOM&quot; and let Angular do the heavy-lifting. Here is a very rough idea of what our shopping cart example might look like in Angular:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4SRlKBlESVgFhpnAc1B4fZEVka2fgTdq-vdopEPiLUbyd_GJEz_bW31q4ajA0uzlLFgIFQxo5ajNWkL3EFcTGYGFOh8OiYNSBOOxE3nS032EEMhytGI6-wOeC1iiouZUrYNObCA/s1600/angular.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;227&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4SRlKBlESVgFhpnAc1B4fZEVka2fgTdq-vdopEPiLUbyd_GJEz_bW31q4ajA0uzlLFgIFQxo5ajNWkL3EFcTGYGFOh8OiYNSBOOxE3nS032EEMhytGI6-wOeC1iiouZUrYNObCA/s640/angular.jpeg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
There is still HTML and CSS at play here, but the state of the application is not reflected by static HTML files (with jQuery sugar on top) but by views (and various templates) rendered by our controller.&lt;br /&gt;
&lt;br /&gt;
The general idea is that we drop in some HTML placeholders and then write Angular code (distinct from JS) against the shadow DOM - a single-source of truth that Angular syncs to the DOM for us. When the user selects a product and goes to the checkout then Angular on their device tells the browser everything it needs to know, instead of asking the server. The customer gets a very responsive experience and hopefully makes it to actually paying.&lt;br /&gt;
&lt;br /&gt;
But in the end, Angular&#39;s complexity is just too high, even with all the other stuff it brings to the table like two-way data-binding and unit testing. The concepts sound like MVC, but once you dive in you encounter some pretty weird stuff: $scope, $broadcast, and $emit to name some at random. And we aren&#39;t writing JS anymore - it&#39;s Angular, and like any language you&#39;re going to need to devote additional brain-space to it. I felt like I got burned when I built something with Angular and I haven&#39;t gone back.&lt;br /&gt;
&lt;h3&gt;
Angular 2&lt;/h3&gt;
&lt;div&gt;
The second iteration of Angular is worth giving it another shot. Goodbye controllers and scope, hello components. Here is our shopping cart in Angular 2:&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWpvzUf-aV1NDfvvLTR6qh7hJ2mk0WnDzMggvHyGfd9JQh_kpHFdcnka1N-q5cMa5HGDyWpiY8qeYCKU8_KSHA0PcvxcxTMiNcrLOeSxDgNFxSDVMEqUFWlA0JblGl7V8E8mJABA/s1600/angular2.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;232&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWpvzUf-aV1NDfvvLTR6qh7hJ2mk0WnDzMggvHyGfd9JQh_kpHFdcnka1N-q5cMa5HGDyWpiY8qeYCKU8_KSHA0PcvxcxTMiNcrLOeSxDgNFxSDVMEqUFWlA0JblGl7V8E8mJABA/s640/angular2.jpeg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
The cohesiveness of each part of the app is better. What a product &quot;does&quot; is no longer spread between the controller, the model, and the view, instead most of it is in the component.&lt;br /&gt;
&lt;br /&gt;
Angular 2 apps can also be built using TypeScript, which feels like a good investment. And it also feels like I have to learn less Angular jargon, but that&#39;s just my first impression. I took a stab at a couple of tutorials during the Alpha phase, and I wondered again, &quot;is this worth it?&quot; I just wish it was easier to get something off the ground that I can iterate on later. The&amp;nbsp;&lt;a href=&quot;https://angular.io/docs/ts/latest/quickstart.html&quot;&gt;official&amp;nbsp;quickstart&lt;/a&gt;&amp;nbsp;for Angular 2 is just awful.&lt;br /&gt;
&lt;h2&gt;
Meteor!&lt;/h2&gt;
Meteor is a full-stack NodeJS webserver, Mongo database, and Angular client-side ecosystem. It is very easy to get something off the ground. I would recommend it for three reasons:&lt;br /&gt;
&lt;h3&gt;
1. Meteor has a killer Angular 2 tutorial&lt;/h3&gt;
&lt;a href=&quot;http://www.angular-meteor.com/tutorials/socially/angular2/bootstrapping&quot;&gt;The tutorial&lt;/a&gt; is very very good (though at the time of this writing it&#39;s only 80% done). You get to play with Angular 2 almost immediately, build something cool, and get spoon-fed each piece as you go. And each step is versioned on GitHub!&lt;br /&gt;
&lt;h3&gt;
2. Meteor bakes the database into the client-side for you&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
Just tell the data API about your types and how it can be accessed and away you go! Just imagine never having to write custom HTTP, cURL, AJAX calls again.&lt;/div&gt;
&lt;h3&gt;
3. Meteor has packages&lt;/h3&gt;
&lt;div&gt;
How many login systems have you written? Account creation, password retrieval/reset, etc, bleh. But in Meteor we can drop in the &lt;a href=&quot;https://www.meteor.com/accounts&quot;&gt;account package&lt;/a&gt; and configure it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;strike&gt;BUT, and it could be a big one, Meteor&#39;s ecosystem isn&#39;t going to let you drop in a package for NodeJS without &lt;a href=&quot;https://meteorhacks.com/complete-npm-integration-for-meteor.html&quot;&gt;some wrangling like this&lt;/a&gt;. This can be a major turnoff because if you&#39;d built your app with Angular2 and NodeJS you wouldn&#39;t have that problem.&lt;/strike&gt;&amp;nbsp;Meteor 1.3+ now uses NPM, so that&#39;s nice.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Oh and Meteor&#39;s ecosystem isn&#39;t all there for Angular 2, so you might want to wait before you pull the trigger on building production apps. :)&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/411800133688433227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/411800133688433227' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/411800133688433227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/411800133688433227'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2016/03/incoming-meteor-and-angular2.html' title='Test Drive Angular 2 with Meteor'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0EvDlpktNudIHGAWRXp1qgOi1umsl_yhcvIF9D5YAVBtZWtjFWapNtZXOHGF0SlVvkmv3L5lDR9H35R756C1cvnzgyXXa4p9wT50Gpk6PxeMItmm_3ZjqaV9nqMzcxMG_jkAhKA/s72-c/MVC_jquery.jpeg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-8528775016549396909</id><published>2015-03-15T10:30:00.001-07:00</published><updated>2016-12-04T12:40:01.271-08:00</updated><title type='text'>Composable ServiceStack</title><content type='html'>&lt;script src=&quot;//cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
pre.prettyprint {
border:none;
background-color: #F2F2F2;
padding: 16px;
}
&lt;/style&gt;
You&#39;ve got a set of &lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack&lt;/a&gt; services and DTOs. You want to share these out with other teams, let them add/customize if needed, but they should still be able to get fixes/new services from you by updating from your repository. Turns out this is relatively easy. They just need to manually register their inherited services in Global.asax.cs.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
(if you just want them to add services of their own you could &lt;a href=&quot;http://bhameyie.com/2013/09/03/servicestack-extensibility-using-mef/&quot;&gt;give this a try&lt;/a&gt;)&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Using virtual/override inheritance isn&#39;t going to work straight out of the box, as DTOs and their handlers are mapped 1:1 &lt;a href=&quot;https://groups.google.com/forum/#!topic/servicestack/A9T6xPDcn-Y&quot;&gt;according to myth&lt;/a&gt;. I know for sure because I tried this:&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;[Route(&quot;/Test&quot;, &quot;POST&quot;)]
public class TestRequest
{
    public string item { get; set; }
}

public class TestResponse
{
    public string response { get; set; }
}

// TestService.cs
public class TestService : Service
{
    public virtual TestResponse Post(TestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m the basic response&quot; };
    }
}

// ImprovedTestService.cs
public class ImprovedTestService : TestService
{
    public override TestResponse Post(TestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m an improved response&quot; };
    }
}
&lt;/pre&gt;
&lt;br /&gt;
You&#39;ll get this error:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Additional information: Could not register Request &#39;REGISDataService.Services.TestRequest&#39; with service &#39;REGISDataService.Services.SomeImprovedTestService&#39; as it has already been assigned to another service.&lt;br /&gt;
Each Request DTO can only be handled by 1 service.&lt;/blockquote&gt;
Fair enough! It made me pause, read everything related I could find on StackOverflow, and conclude I still had a decent use case.&lt;br /&gt;
&lt;br /&gt;
You just need to register the services manually.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;public class TestAppHost : AppHostHttpListenerBase
{
    //public TestAppHost(): base(&quot;TestService&quot;, typeof(TestService).Assembly)
    // give AppHost the basic ServiceStack assembly instead of yours
    // (you can&#39;t give it nothing)
    public TestAppHost(): base(&quot;TestService&quot;, typeof(Service).Assembly)
    { 
    }
 
    // you could also try overloading ServiceController here, but I got no joy

    public override void Configure(Funq.Container container)
    {
        // some config stuff
    }
}

protected void Application_Start(object sender, EventArgs e)
{
    var appHost = new TestAppHost().Init();

    // register each .cs that has your service
    appHost.RegisterService(typeof(ImprovedTestService));
}
&lt;/pre&gt;
&lt;br /&gt;
Done! Your derived class is in place. You can add/change services to the base class, overload them, and add services too:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;[Route(&quot;/Test&quot;, &quot;POST&quot;)]
public class TestRequest
{
    public string item { get; set; }
}

[Route(&quot;/Test/Base&quot;, &quot;POST&quot;)]
public class BasicTestRequest
{
    public string item { get; set; }
}

[Route(&quot;/Test/Another&quot;, &quot;POST&quot;)]
public class AnotherTestRequest
{
    public string item { get; set; }
}

public class TestResponse
{
    public string response { get; set; }
}

// TestService.cs
public class TestService : Service
{
    public virtual TestResponse Post(TestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m the basic response&quot; };
    }

    public virtual TestResponse Post(BasicTestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m a response that doesn&#39;t get overloaded&quot; };
    }
}

// ImprovedTestService.cs
public class ImprovedTestService : TestService
{
    public override TestResponse Post(TestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m an improved response&quot; };
    }

    public TestResponse Post(AnotherTestRequest request)
    {
        return new TestResponse { response = &quot;I&#39;m a response that isn&#39;t in the base service&quot; };
    }
}
&lt;/pre&gt;
&lt;br /&gt;
Of course you could farm out bits and pieces of your DTO-bound services to underlying services and then subclass those. I&#39;m probably going to need to do this for more complex stuff, but the above seems to me a clean and simple way to do most of the work.</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/8528775016549396909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/8528775016549396909' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/8528775016549396909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/8528775016549396909'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2015/03/composable-servicestack.html' title='Composable ServiceStack'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-5739986536609567939</id><published>2014-01-02T12:31:00.000-08:00</published><updated>2014-04-08T17:13:05.254-07:00</updated><title type='text'>Updating to ServiceStack v4.0</title><content type='html'>Updating ServiceStack to v4.0 was my task for yesterday. It wasn&#39;t as simple as firing up NuGet and updating - I got this error:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Unable to find a version of &#39;ServiceStack.OrmLite.SqlServer&#39; that is compatible with &#39;ServiceStack.Common 4.0.5&#39;.&lt;/blockquote&gt;
But it wasn&#39;t too bad once I dived in. You&#39;ll need to browse through the &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/Release-Notes&quot;&gt;release notes&lt;/a&gt; for the breaking changes.&lt;br /&gt;
&lt;br /&gt;
Here are the steps that worked for me:&lt;br /&gt;
&lt;h4&gt;
Reinstall the libraries&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Remove all ServiceStack 3.x libraries from the project(s)&lt;/li&gt;
&lt;li&gt;Add ServiceStack 4.x libraries (including the new ServiceStack.Interfaces)&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
Refactor OrmLite&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Add &quot;using ServiceStack.Data&quot; to all repositories that need IDbConnectionFactory&lt;/li&gt;
&lt;li&gt;Remove the &quot;OrDefault&quot; stuff from the Db API (e.g. &lt;b&gt;SelectOrDefault &lt;/b&gt;to &lt;b&gt;Select&lt;/b&gt;, &lt;b&gt;FirstOrDefault &lt;/b&gt;to &lt;b&gt;Single&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;Change &lt;b&gt;Db.Select&lt;/b&gt; to &lt;b&gt;Db.SelectFmt&lt;/b&gt; (i.e. your select statements that are sql strings)&lt;/li&gt;
&lt;li&gt;Remove the &quot;Param&quot; stuff from the Db API as it&#39;s now the default (e.g. &lt;b&gt;SelectParam &lt;/b&gt;to &lt;b&gt;Select&lt;/b&gt;, &lt;b&gt;UpdateParam &lt;/b&gt;to &lt;b&gt;Update&lt;/b&gt;, &lt;b&gt;DeleteByIdParam to&lt;/b&gt;&amp;nbsp;&lt;b&gt;DeleteById&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;Some of my &lt;b&gt;.Select()&lt;/b&gt; statements starting throwing nulls, but worked when I changed them to &lt;b&gt;.Where()&lt;/b&gt; statements.&lt;/li&gt;
&lt;li&gt;Change &lt;b&gt;Db.GetById&lt;/b&gt; to &lt;b&gt;Db.SingleById&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Change &lt;b&gt;Db.GetLastInsertId&lt;/b&gt; to &lt;b&gt;Db.LastInsertId&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Change &lt;b&gt;Db.List&lt;/b&gt; to &lt;b&gt;Db.SqlList&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Change &lt;b&gt;ExpressionVisitor &lt;/b&gt;to &lt;b&gt;SqlExpression&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;b&gt;Refactor &#39;using&#39; statements&lt;/b&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Change &quot;using ServiceStack.ServiceInterface&quot; to &quot;using ServiceStack&quot;. Other libraries in that stack must also be changed, e.g. ServiceStack.Auth, ServiceStack.Validation.&lt;/li&gt;
&lt;li&gt;Change &quot;using ServiceStack.Common&quot; to &quot;using ServiceStack&quot;.&lt;/li&gt;
&lt;li&gt;Change &quot;using ServiceStack.ServiceHost&quot; to &quot;using ServiceStack&quot;.&lt;/li&gt;
&lt;li&gt;Change &quot;using ServiceStack.ServiceClient&quot; to &quot;using ServiceStack&quot;.&lt;/li&gt;
&lt;li&gt;Remove &quot;ServiceStack.CacheAccess.x&quot;. Add &quot;ServiceStack.Caching&quot;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
Miscellaneous&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Change&amp;nbsp;&lt;b&gt;TranslateTo&lt;/b&gt;&amp;nbsp;to&amp;nbsp;&lt;b&gt;ConvertTo&lt;/b&gt;. I also had to add a &quot;using ServiceStack&quot; for this step because AutoMapping has moved namespaces. You&#39;ll get conflicts if you are using MVC 5 attribute routing, in which case you&#39;ll have to use aliases.&lt;/li&gt;
&lt;li&gt;I had to change my LINQ .Single() statements to .First()&lt;/li&gt;
&lt;li&gt;Change any string.FromJson&lt;someclass&gt;() to JsonSerializer.DeserializeFromString&lt;someclass&gt;(theString)&lt;/someclass&gt;&lt;/someclass&gt;&lt;/li&gt;
&lt;li&gt;Change any object.ToJson() to JsonSerializer.SerializeToString(theObject)&lt;/li&gt;
&lt;li&gt;In global.asax&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;The AppHost must inherit from AppHostHttpListenerBase (not&amp;nbsp;ServiceStack.WebHost.Endpoints.AppHostBase)&lt;/li&gt;
&lt;li&gt;&lt;someclass&gt;container.Register&amp;lt;someclassinterface&amp;gt;(someObject) to container.Register(someObject)&lt;/someclass&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;In Web.config&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Rename&amp;nbsp;ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory to ServiceStack.HttpHandlerFactory&lt;/li&gt;
&lt;/ol&gt;
&lt;/ol&gt;
</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/5739986536609567939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/5739986536609567939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5739986536609567939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5739986536609567939'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2014/01/updating-to-servicestack-v40.html' title='Updating to ServiceStack v4.0'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-3188480287804235314</id><published>2013-12-23T14:24:00.000-08:00</published><updated>2013-12-24T09:35:00.779-08:00</updated><title type='text'>Managing .NET MVC Complexity with ServiceStack in a Higher Ed Project</title><content type='html'>&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
This post is for intermediate programmers who are growing their first .NET MVC large-scale project and want a high-level understanding of where&amp;nbsp;&lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki&quot;&gt;ServiceStack&lt;/a&gt;&amp;nbsp;can help them manage complexity. I found lots of resources to help me understand the nitty-gritty, but when it came to questions like &quot;where should I put the business logic?&quot; and &quot;how many models of the data should I have?&quot; the answers were &quot;it depends,&quot; and while I could find different code examples, I couldn&#39;t get a high-level feel for the system. I&#39;ll show you the design I ended up with for my higher ed project.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I started with&amp;nbsp;&lt;a href=&quot;http://www.amazon.com/Microsoft%C2%AE-NET-Architecting-Applications-PRO-Developer/dp/073562609X&quot;&gt;Microsoft .NET: Architecting Applications for the Enterprise&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670&quot;&gt;Code Complete Second Edition&lt;/a&gt;. I also got a lot of help from &lt;a href=&quot;http://www.pluralsight.com/&quot;&gt;Pluralsight&lt;/a&gt;&amp;nbsp;videos - a subscription is well worth the money when you are starting out.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I am assuming a working knowledge of dependency injection, separation of concern, cohesion, coupling, and several design patterns: repositories, interfaces, factories, data transfer objects (DTOs), object-to-relational mappers, and unit testing.&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;System Layers&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
Small-scale projects often don&#39;t need layers. For example, a single-page website could be just one PHP file. ServiceStack&amp;nbsp;has lots of &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/Single-page-apps&quot;&gt;examples&lt;/a&gt;&amp;nbsp;for this and their &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/Testing&quot;&gt;tests&lt;/a&gt;&amp;nbsp;show how compact the library can be.&amp;nbsp;But as complexity grows you&#39;ll find this approach won&#39;t work for lots of reasons that I won&#39;t get into. In the PHP world this is where you&#39;ll probably adopt a MVC framework, or grow your own. In the .NET world layers are strongly encourage; an MVC project gives you three layers (taken from the &lt;a href=&quot;http://www.asp.net/mvc/tutorials/mvc-5/introduction/getting-started&quot;&gt;official tutorial&lt;/a&gt;):&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Models&lt;/b&gt;: Classes that represent the data of the application  and that use validation logic to enforce business rules for that data.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Views&lt;/b&gt;: Template files that your application uses to dynamically  generate HTML responses.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Controllers&lt;/b&gt;: Classes that handle incoming browser requests,  retrieve model data, and then specify view templates that return a response  to the browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Notice that each layer has a particular area of concern: Models know about data and its structure; Views know about HTML and how the users want to see data; Controllers know something about both and handle the communication between them.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So far so good. Nothing new here. Medium-scale projects can live comfortably. But large-scale projects need even more separation of concern. My higher ed project has these four general layers: Presentation (the normal MVC setup), Services, Data Access, and Business Logic with ServiceStack wiring it up. It looks like this:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPAwqIdLFIHNRkfF2UlB5kNQxWVo77YXI7Q2KrIgRYeV5D29MT0P68fIBg8q66ED4Dm59eFzOAV2V6iTxZsCOOuMGQyb4fW8t3ytP-I2ZKITSlFXlzbQNWjtu2xi2dVbBTlKuUlA/s1600/image+(3).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;534&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPAwqIdLFIHNRkfF2UlB5kNQxWVo77YXI7Q2KrIgRYeV5D29MT0P68fIBg8q66ED4Dm59eFzOAV2V6iTxZsCOOuMGQyb4fW8t3ytP-I2ZKITSlFXlzbQNWjtu2xi2dVbBTlKuUlA/s640/image+(3).png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Presentation Layer&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
This layer only cares about how to present data to the users/reports/scripts, and validate and collect it from them. It doesn&#39;t care how the data is processed and stored. This keeps things as simple as possible. Here is how this shakes out in .NET MVC for a student viewing their recent registrations (I&#39;ve separated out the student sidebar will be used in all sorts of ways in the project ):&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPvpx5QMFpLTSo7O7V37SqVUKF7zyj-UXPWwIN40ZkpdAfUsnelaQ7JA2pxZL5Z-N5YhDqS5uu5PfjpUGoH7vQU2DmzOmEygHJy1sx4EejGFseVcRU1Q3a6AXFNbOOpF5e9NE6Qw/s1600/image+(1).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPvpx5QMFpLTSo7O7V37SqVUKF7zyj-UXPWwIN40ZkpdAfUsnelaQ7JA2pxZL5Z-N5YhDqS5uu5PfjpUGoH7vQU2DmzOmEygHJy1sx4EejGFseVcRU1Q3a6AXFNbOOpF5e9NE6Qw/s640/image+(1).png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
But wait! Where&#39;s the data? The Service Layer&#39;s DTOs can do double-duty for us for simple stuff like this. Just strongly-type your view to the request object.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXqNrkQhKFTnIHkWJ4pjJPfTpE_zzoXvLmppr85-dW5EBDAdkXEcdgOiXAXgkgS_neuhcojZt_PfpfseG2-Iv84Erle3th62rkNNhmzT1sZ34BzaIm78NKWZI71CCfHS_f4aG2DA/s1600/image+(2).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;177&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXqNrkQhKFTnIHkWJ4pjJPfTpE_zzoXvLmppr85-dW5EBDAdkXEcdgOiXAXgkgS_neuhcojZt_PfpfseG2-Iv84Erle3th62rkNNhmzT1sZ34BzaIm78NKWZI71CCfHS_f4aG2DA/s640/image+(2).png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
(When you need more complexity or an aggregate of DTOs you&#39;ll want to create a model for each view. However, it may be simpler to just farm out different pieces of the view using Html.RenderPartial in your view)&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3&gt;
Where should my business logic go?&lt;/h3&gt;
&lt;div&gt;
This layer should avoid all business logic. Whatever the student wants to see should be contained in the DTOs, already pre-calculated. However in some cases you&#39;ll still do some on-the-fly totals, especially if you&#39;re using an AJAX approach to the client-side.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
How many models of the data should I have?&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
This layer talks with the ServiceStack DTOs. If you need to decouple the data further in this layer you&#39;d end up with two models of the data.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The DataClientFactory needs to be injected into your controllers so they can ask for data. There are several ways to do ServiceStack &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/The-IoC-container&quot;&gt;dependency injection&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Service Layer&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
This layer is... well, it&#39;s hard to get a good general definition. I&#39;d rather just give you an example. In my project it does this:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Gets a request object from the &lt;b&gt;Presentation Layer&lt;/b&gt; (and sometimes other services in its own layer)&lt;/li&gt;
&lt;li&gt;Gets the necessary data from the &lt;b&gt;Data Access Layer&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Performs some operation on the data (complex operations should be moved onto the &lt;b&gt;Business Logic Layer&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;Returns a response object&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
In my example the Presentation Layer submits a RecentRegistrationRequest to the Service Layer. It contains a unique identifier for the student. The Service Layer grabs all the data it needs about the student, massages and returns it as a RecentRegistrationResponse. It contains the unique identifier for the student, some student details, some registrations details (something like a List&lt;registrationresponse&gt;), etc.&lt;/registrationresponse&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div&gt;
The services are grouped by similar functions.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;
Where should my business logic go?&lt;/h3&gt;
&lt;div&gt;
If the&amp;nbsp;&lt;b&gt;Service Layer&lt;/b&gt;&amp;nbsp;needs to iterate the registrations for a student and produce a total attempted credits result for the&amp;nbsp;&lt;b&gt;Presentation Layer&lt;/b&gt;&amp;nbsp;there really isn&#39;t any need to segregate this off into the&amp;nbsp;&lt;b&gt;Business Logic Layer&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
How many models of the data should I have?&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
This layer doesn&#39;t have it&#39;s own models of the data. It simply talks with the ServiceStack DTOs and the plain C# objects you define in the &lt;b&gt;Data Access Layer.&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1Mt39elQp7irW8lthTdru1h-0Te9O01WFpO6_N3Wahn6nk_I4OZZGelHKiWXYIJDJ73M2_V2UY3Q47vyV8RE0Y9lJnO_IKv78rRTy6ezAP2VkcfzwAMcVeWbYCsLJWKWE9aIejA/s1600/image+(5).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1Mt39elQp7irW8lthTdru1h-0Te9O01WFpO6_N3Wahn6nk_I4OZZGelHKiWXYIJDJ73M2_V2UY3Q47vyV8RE0Y9lJnO_IKv78rRTy6ezAP2VkcfzwAMcVeWbYCsLJWKWE9aIejA/s400/image+(5).png&quot; width=&quot;323&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Data Access Layer&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
Here we do CRUD. Try to keep everything else out of this layer. You&#39;ll want to understand how persistence shouldn&#39;t happen here. Each repository generally takes care of one table (but they are functionally cohesive so they may span several tables). Thanks to ServiceStack&#39;s &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite&quot;&gt;OrmLite&lt;/a&gt;&amp;nbsp;this is quite simple. Each table just needs a plain C# class. Note the repository factory and the repository interfaces so we can mock and unit test our &lt;b&gt;Service Layer&lt;/b&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
Where should my business logic go?&lt;/h3&gt;
&lt;div&gt;
Sometimes you&#39;ll have simple business logic that can live here, like calculating someone&#39;s age from date of birth.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
How many models of the data should I have?&lt;/h3&gt;
&lt;div&gt;
This layer has a one-class-to-one-table model. You&#39;ll also have the ServiceStack DTOs. If you created view-specific models in the &lt;b&gt;Presentation Layer&lt;/b&gt;&amp;nbsp;you&#39;ll now have three models of the data.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBvHWvpj1slu-0yDNAPyjmoC_uEVb-VFrQGC3GagUR1L9_g9G4GiLjgL6YVZsrJB8ReP4EO7NoSKFDPfjefqg5rqDR6wXgGBrnD_w3yvDwzLyw8WlGZ2Op2tqjMCeTXcMgYhLM6g/s1600/image+(7).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBvHWvpj1slu-0yDNAPyjmoC_uEVb-VFrQGC3GagUR1L9_g9G4GiLjgL6YVZsrJB8ReP4EO7NoSKFDPfjefqg5rqDR6wXgGBrnD_w3yvDwzLyw8WlGZ2Op2tqjMCeTXcMgYhLM6g/s640/image+(7).png&quot; width=&quot;369&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Business Logic Layer&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Here is where we do the heavy-lifting. It&#39;s a judgement call as to when something needs to live here. F&lt;/span&gt;or things that are complex such as calculating an invoice for registrations (see below), or perhaps need to be done across multiple services, these are good candidates for this layer.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
For my project I want my invoice business logic to be agnostic to how the data is stored and how it is presented to the user, and because I absolutely need unit testing here, everything must be injected. There is a universal factory for building the components and (almost) everything has an interface, depending on whether it has methods I need to mock later.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3&gt;
Where should my business logic go?&lt;/h3&gt;
&lt;div&gt;
When you have complex routines put them here!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
How many models of the data should I have?&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
This layer should talk in business models in my opinion, not in any other model you&#39;ve created. This brings our total to three models of the data (or four if you created view-specific models in the&amp;nbsp;&lt;b&gt;Presentation Layer).&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUCMFgwRa-7BSThtKk87RnnE1hYBasQoX7bWGQ4X8eCwVHcMNrwjGNTUrye_7ObKfwmxepO3QYPovrFggz8lDUH1-8fkZMoS898lQViZ5epRdUVYIFNBbfHZoNi6mjhBp7uhX6AQ/s1600/image+(9).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUCMFgwRa-7BSThtKk87RnnE1hYBasQoX7bWGQ4X8eCwVHcMNrwjGNTUrye_7ObKfwmxepO3QYPovrFggz8lDUH1-8fkZMoS898lQViZ5epRdUVYIFNBbfHZoNi6mjhBp7uhX6AQ/s640/image+(9).png&quot; width=&quot;568&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Summary&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
Large-scale projects have enough complexity to warrant a Presentation Layer, Service Layer, Data Access Layer, and Business Logic Layer. Each layer has a particular area of concern. ServiceStack wires the layers together.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
Where should my business logic go?&lt;/h3&gt;
&lt;div&gt;
I&#39;ve suggested that there should be some in the Service Layer, some in the Data Access Layer, but the most complex routines should be offloaded to the Business Logic Layer. At the end of the day it still &quot;depends&quot; on the particular project.&lt;/div&gt;
&lt;h3&gt;
How many models of the data should I have?&lt;/h3&gt;
&lt;div&gt;
I&#39;ve suggested that you&#39;ll probably have three models of the data: The ServiceStack DTOs, plain C# objects representing the tables in your database, and business domain models. You could use a fourth data model in the Presentation Layer if you really wanted to, but I&#39;ve found that binding ServiceStack DTOs directly to the views works well.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To reduce the overhead in translating data between models you could have a look at &lt;a href=&quot;http://stackoverflow.com/questions/15368257/servicestack-new-service-side-by-side-asp-net-mvc-website&quot;&gt;this post&lt;/a&gt; on StackOverflow.&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
Happy to incorporate different points of view/corrections/suggestions from your comments.&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Here is the complete diagram:&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieEjwg3lBD-QdvZHLmaqBG-B6bulYFvZ4KfSk2Bl-ls7_Yy4e1biOZNeM_CKoRGVba4JD4eewZ905kvQk4fkrkOgd5m4uCWbU7Ta6YDjOdFFB7g82YyXjZwVjsfG8252_qIrdgeQ/s1600/image+(10).png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieEjwg3lBD-QdvZHLmaqBG-B6bulYFvZ4KfSk2Bl-ls7_Yy4e1biOZNeM_CKoRGVba4JD4eewZ905kvQk4fkrkOgd5m4uCWbU7Ta6YDjOdFFB7g82YyXjZwVjsfG8252_qIrdgeQ/s640/image+(10).png&quot; width=&quot;440&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/3188480287804235314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/3188480287804235314' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3188480287804235314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3188480287804235314'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2013/12/managing-net-mvc-complexity-with.html' title='Managing .NET MVC Complexity with ServiceStack in a Higher Ed Project'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPAwqIdLFIHNRkfF2UlB5kNQxWVo77YXI7Q2KrIgRYeV5D29MT0P68fIBg8q66ED4Dm59eFzOAV2V6iTxZsCOOuMGQyb4fW8t3ytP-I2ZKITSlFXlzbQNWjtu2xi2dVbBTlKuUlA/s72-c/image+(3).png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1434686650144091632</id><published>2013-01-27T12:19:00.002-08:00</published><updated>2013-01-27T12:19:08.999-08:00</updated><title type='text'>Life Doesn&#39;t Grow on Trees</title><content type='html'>It is one of the oddities of Judeo-Christianity that its followers believe they are created in dependence on their creator, but their creator does not depend on them.&lt;br /&gt;
&lt;br /&gt;
In the ancient world many religions believed that people were created to serve the gods, but that the gods in turn depended on their creatures - a great symbiosis that served both parties. The people provided food, worship, and a temple for their gods, and the gods returned the favour in protection and blessing.&lt;br /&gt;
&lt;br /&gt;
But it is a system of great uncertainty. Why do the gods get angry with us? What can we do to appease them?&lt;br /&gt;
&lt;br /&gt;
It is also a system of great weariness and futility. The point of existence is to maintain the status quo, eke out what you can, and offers no solution to the problem of death.&lt;br /&gt;
&lt;br /&gt;
Sermon audio -&amp;nbsp;&lt;a href=&quot;http://winnipegcentrevineyard.com/audio/2013.January.6.CamTucker.mp3&quot;&gt;http://winnipegcentrevineyard.com/audio/2013.January.6.CamTucker.mp3&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1434686650144091632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1434686650144091632' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1434686650144091632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1434686650144091632'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2013/01/life-doesnt-grow-on-trees.html' title='Life Doesn&#39;t Grow on Trees'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-4742089996224594162</id><published>2012-07-30T09:47:00.002-07:00</published><updated>2013-01-27T11:36:56.386-08:00</updated><title type='text'>God Saves the King</title><content type='html'>1 Samuel 24-26&lt;br /&gt;
&lt;br /&gt;
As near as I can tell bitterness comes from a place of unfulfilled promises. They aren&#39;t dreams, for we have those aplenty unfulfilled and it doesn&#39;t hurt very much.&lt;br /&gt;
&lt;br /&gt;
A dream come true is an expression of utter joy. A dream left in dreamland is an expression of hope, but not despair.&lt;br /&gt;
&lt;br /&gt;
A promise is an entirely different thing.&lt;br /&gt;
&lt;br /&gt;
Sermon audio:&amp;nbsp;&lt;a href=&quot;http://winnipegcentrevineyard.com/audio/2012.July.29.CamTucker.mp3&quot;&gt;http://winnipegcentrevineyard.com/audio/2012.July.29.CamTucker.mp3&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/4742089996224594162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/4742089996224594162' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4742089996224594162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4742089996224594162'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2012/07/god-saves-king.html' title='God Saves the King'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-6957066692613594294</id><published>2012-03-30T05:08:00.000-07:00</published><updated>2012-03-30T05:08:15.310-07:00</updated><title type='text'>Ecclesiastes 1-6 Translation and Study Guide</title><content type='html'>My study guide and translation for Ecclesiastes 1-6 is all done!&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://docs.google.com/open?id=0B0mGcUwFcgmpRi0taDVuaUZRYjJPU3ZNTFQ0NWdzdw&quot;&gt;https://docs.google.com/open?id=0B0mGcUwFcgmpRi0taDVuaUZRYjJPU3ZNTFQ0NWdzdw&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you like it, consider donating a few dollars to the&amp;nbsp;&lt;a href=&quot;http://www.houseofhesed.ca/&quot;&gt;House of Hesed&lt;/a&gt;, a transition house for people living with HIV/AIDS in Winnipeg.</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/6957066692613594294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/6957066692613594294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/6957066692613594294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/6957066692613594294'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2012/03/ecclesiastes-1-6-translation-and-study.html' title='Ecclesiastes 1-6 Translation and Study Guide'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-7608611788045177871</id><published>2012-02-04T13:21:00.001-08:00</published><updated>2012-02-04T13:21:20.897-08:00</updated><title type='text'>February Poem</title><content type='html'>&lt;div style=&quot;text-align: justify;&quot;&gt;
Disheveled is an odd word&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; to explain a home for even in its most&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; haphazard ways it speaks of you&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; in happy stacks of books,&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; whispers of strewn paper letters from friends,&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; photographs propped like verbs on your windowsill,&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; and clothing that punctuates here and there&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; what you looked like yesterday.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
And the noun of course is you,&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; described in a space that isn&#39;t really confused&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; as it is a poem&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - that is beautiful -&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
with no rules.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/7608611788045177871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/7608611788045177871' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7608611788045177871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7608611788045177871'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2012/02/disheveled-is-odd-word-to-explain-home.html' title='February Poem'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-7713227279763231703</id><published>2011-11-30T11:25:00.001-08:00</published><updated>2017-04-30T15:46:44.331-07:00</updated><title type='text'></title><content type='html'>&lt;div&gt;&lt;p&gt;Surely there is a moment&lt;br&gt;
&amp;nbsp; when clarity sings a note&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; of welcome, harbinger&lt;/p&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/7713227279763231703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/7713227279763231703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7713227279763231703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7713227279763231703'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/11/surely-there-is-moment-when-clarity.html' title=''/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-990398180087554350</id><published>2011-11-13T08:55:00.001-08:00</published><updated>2011-11-13T08:56:25.515-08:00</updated><title type='text'></title><content type='html'>&lt;div&gt;&lt;p&gt;The beauty and blessing&lt;br&gt;
&amp;#160; of the plant mysterious seeded.&lt;br&gt;
&amp;#160;&amp;#160;&amp;#160; A soil that grows it&lt;br&gt;
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; and finds it flowering. &lt;/p&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/990398180087554350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/990398180087554350' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/990398180087554350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/990398180087554350'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/11/beauty-and-blessing-of-plant-mysterious.html' title=''/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-4203248683082869004</id><published>2011-11-05T16:03:00.000-07:00</published><updated>2011-11-05T16:20:03.996-07:00</updated><title type='text'>Critical Realism</title><content type='html'>A modern person (a positivist or naive realist) formulates a hypothesis to solve a problem and tests it in order to obtain objective truth about reality. For her this is a sign of immediate unbiased access to the world. But Wright argues she is doing so according to her story, both in the data she accepts to corroborate her hypothesis, and in the hypothesis itself.&lt;br /&gt;
&lt;br /&gt;
For example, if the story she is telling herself about the world includes a god, then this will affect in some way the solutions she proposes to a problem, the questions she asks to prove or disprove whether her solution works, why she asks the questions she does, and what data she accepts. If her story does not include a god, everything looks different. 

Both versions of the story cannot claim the objective high-ground, according to Wright. But surprisingly neither does he say that both of these stories are equally true - just subjective perspectives (a postmodern or phenomenalist response). Instead he argues that both stories can be tested for how well they explain the world.

&lt;br /&gt;
&lt;blockquote&gt;
&quot;There is no such thing as ‘neutral’ or ‘objective’ proof; only the claim that the story we are now telling about the world as a whole makes more sense, in its outline and detail, than other potential or actual stories that may be on offer. Simplicity of outline, elegance in handling the details within it, the inclusion of all the parts of the story, and the ability of the story to make sense beyond its immediate subject-matter: these are what count.&quot; - N.T. Wright, NTPG, p.42&lt;/blockquote&gt;
My question is: How does N.T. Wright&#39;s prior commitment to history as the primary category for understanding the world affect his approach to epistemology? And what are the other categories available?&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
It seems to me that Merold Westphal, at least in the first two chapters of &lt;i&gt;Whose Community? Which Interpretation?&lt;/i&gt;, is beginning somewhere else. His discussion of hermeneutics up to this point seems to be more about the communication of ideas rather than events.</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/4203248683082869004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/4203248683082869004' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4203248683082869004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4203248683082869004'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/11/critical-realism.html' title='Critical Realism'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-8131447229072452167</id><published>2011-10-22T14:41:00.000-07:00</published><updated>2011-10-22T14:42:12.859-07:00</updated><title type='text'></title><content type='html'>Moving far away and&lt;br /&gt; transporting more than&lt;br /&gt;just boxes.&lt;br /&gt;&lt;br /&gt;subtly waiting for advent</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/8131447229072452167/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/8131447229072452167' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/8131447229072452167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/8131447229072452167'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/10/moving-far-away-and-transporting-more.html' title=''/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-7651189686648938551</id><published>2011-09-03T14:43:00.003-07:00</published><updated>2011-09-03T14:52:18.549-07:00</updated><title type='text'>On Ecclesiastes</title><content type='html'>&lt;p style=&quot;text-align:center&quot;&gt;The measure of a person is not&lt;br /&gt;in the control they exercise upon creation,&lt;br /&gt;but in their response to the influence of God upon them&lt;br /&gt;for creation.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/7651189686648938551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/7651189686648938551' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7651189686648938551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/7651189686648938551'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/09/on-ecclesiastes.html' title='On Ecclesiastes'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-3706881797615066444</id><published>2011-02-13T15:16:00.002-08:00</published><updated>2011-02-13T15:20:13.743-08:00</updated><title type='text'>December Poem</title><content type='html'>&lt;blockquote&gt;Shall we hear with our hearts&lt;br /&gt;and come to love the Maker?&lt;br /&gt;Or will we remain unturned,&lt;br /&gt;and furrowed, and bowed,&lt;br /&gt;straining against the bit and the dust.&lt;br /&gt;Is it a choice or a plan that&lt;br /&gt;drives history to its centre&lt;br /&gt;once more?&lt;br /&gt;Welcome the King, everything crows.&lt;br /&gt;Bow low and kiss the shackles free.&lt;/blockquote&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/3706881797615066444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/3706881797615066444' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3706881797615066444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3706881797615066444'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2011/02/december-poem.html' title='December Poem'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-6794702473712546521</id><published>2010-04-29T18:45:00.004-07:00</published><updated>2010-04-29T18:58:36.139-07:00</updated><title type='text'>Two men sit facing...</title><content type='html'>&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;Two men sit facing the street, joined by blood, separated by choice, both trying to ignore the latter, and bewildered by the former.&lt;br /&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;&quot;What happened,&quot; he asks himself as his teenage son gets phone call after phone call from people he doesn&#39;t know and his son won&#39;t tell him about. He asks out of fear for his son, and love, for he has heard about what young people get into these days and can hardly forget what he got himself into so many years ago.&lt;br /&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;He is afraid his son doesn&#39;t love him and never will.&lt;br /&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;He gets up to leave, earlier than he needs to, and calls him &quot;bud&quot; as he awkwardly hugs him from the side. His mother will be along shortly, and he&#39;s not interested in seeing her.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;The son retrieves his laptop - he always gets what he wants - and reminds himself that no matter what he or the other man feels, he is the one who decides who he is, no one else, and he &lt;i&gt;is&lt;/i&gt; the island he desperately needs to be, amidst a sea of other islands; strong, independent, scared.&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;He isn&#39;t sure what it means to be grown up, to really love, because it seems like the whole world doesn&#39;t have a clue either.&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;The woman on the screen reaches out with everything that she is, wishing he was strong, willing to give him everything so he&#39;ll love her.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;And God sees the man who hasn&#39;t grown up and the son who wishes he had and the woman who is beautiful and both so lonely and the mother who spends weekdays and nights with the boy so powerless to help him become something she isn&#39;t even sure exists.&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space:pre&quot;&gt; &lt;/span&gt;And the church across the street is empty, but none of them know or care, just like none of them know or care that God sees, and burns.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/6794702473712546521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/6794702473712546521' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/6794702473712546521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/6794702473712546521'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2010/04/two-men-sit-facing.html' title='Two men sit facing...'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-5832668266865687139</id><published>2009-03-12T17:51:00.003-07:00</published><updated>2009-03-25T16:35:14.451-07:00</updated><title type='text'>Leisure or Sabbath?</title><content type='html'>&lt;blockquote&gt;&lt;span style=&quot;font-size: 12pt; font-family: &amp;quot;Times New Roman&amp;quot;;&quot; lang=&quot;EN-CA&quot;&gt;“Leisure and sabbath have much in common: They are both personally restorative, enjoyable, nonutilitarian, and playful. Though sabbath and leisure delightfully overlap, there are discernible differences. Leisure is a matter of personal choice; sabbath is divinely mandated (Exod. 20:8-11). Leisure is usually perceived as avocational – something we do alongside our vocation; sabbath is vocational – part of the response of our entire person to the call of God. Leisure is usually directed to self, while sabbath, which is also personally satisfying, is directed ultimately to the pleasure of God. Both are aesthetic, but leisure tends toward hedonism while sabbath invites contemplation. Sometimes we must admit that leisure is more of a diversion from sabbath than an experience of it.”&lt;br /&gt;- The Equipping Pastor, Stevens and Collins, 1993, p. 86&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/5832668266865687139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/5832668266865687139' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5832668266865687139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/5832668266865687139'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2009/03/leisure-or-sabbath.html' title='Leisure or Sabbath?'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1122645186206408374</id><published>2009-01-19T11:20:00.003-08:00</published><updated>2009-01-19T11:58:02.246-08:00</updated><title type='text'>Eugene on the Song of Songs</title><content type='html'>&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size:130%;&quot;&gt;&lt;br /&gt;&quot;All pastoral conversation is a conversation between lovers&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:85%;&quot;&gt;(Five Smooth Stones..., Eugene Peterson, p. 44)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1122645186206408374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1122645186206408374' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1122645186206408374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1122645186206408374'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2009/01/eugene-on-being-pastor.html' title='Eugene on the Song of Songs'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1898962802915340414</id><published>2008-12-03T12:05:00.002-08:00</published><updated>2008-12-03T12:09:15.250-08:00</updated><title type='text'>Ecclesiastes</title><content type='html'>&lt;blockquote&gt;&quot;So I commend the enjoyment of life, because nothing is better for a man under the sun than to eat and drink and be glad. Then joy will accompany him in his work all the days of the life God has given him under the sun&quot; (Eccl. 8:15).&lt;/blockquote&gt;Accepting our human limitations as the gift of God is a difficult task. Look at all the injustice around us! How the poor are mistreated, and millions die in war and famine and meaninglessness. We are bent on fixing the world, and rightly so, for that is God’s work that he is already doing.&lt;br /&gt;&lt;br /&gt;But we are tempted to leave behind what it means to be human – finding simple and good pleasure in life – and instead trade it for &quot;super&quot;-human existence, in the guise of self-sacrifice and cross-bearing, that leaves us acting and behaving like gods who &quot;really&quot; know how the world should work, and do our damnedest to see our vision become a reality. Broken families and burned out pastors and church congregations are our legacy. Few know what it means to be human. And then the justice and peace-making we so passionately pursue, though a crucial and central part of being human, not only does not deliver the oppressed, but we are, in fact, never delivered ourselves.</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1898962802915340414/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1898962802915340414' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1898962802915340414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1898962802915340414'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2008/12/ecclesiastes.html' title='Ecclesiastes'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-4078082939011749188</id><published>2008-11-05T12:17:00.002-08:00</published><updated>2008-11-05T12:21:42.580-08:00</updated><title type='text'>My reaction to Job 38-42</title><content type='html'>What can really be said in the face of meaningless suffering? We question life and God, and he and life turn and question us instead. “Who are you,” he asks. “You act like, and strive for knowledge, as if you were god, but you are not. Consider my unfathomable ways.” Is it impossible to ask the meaning of life, for it is beyond our reach? Is our control of our lives only an illusion, just as we fool ourselves into thinking we can control the world? If you believe yourself to be suffering unjustly and meaninglessly, perhaps you have bought into the illusion that you can determine and order your life by righteousness, instead of living by trust. Perhaps you are being tempted to indict God.&lt;br /&gt;&lt;br /&gt;Ultimate salvation does not rest with us, for we cannot even save ourselves day to day. Science advances, we live like kings, but cancer laughs at our impotence, and there is no peace in the land, no matter how much we believe in the power of the United Nations. The more we appear to succeed, according to the voices we prefer to listen to, the more believable the illusion becomes, and the more tempted we are to sit in judgement upon God when he won’t get with our program and vindicate our godhood.&lt;br /&gt;&lt;br /&gt;What is Job&#39;s journey? He admits he has spoken of things he did not understand. Does he include his first and best reaction in 1:21 and 2:10? Perhaps he did not speak wrongly in those cases, but did not &lt;span style=&quot;font-style: italic;&quot;&gt;really &lt;/span&gt;understand. A change of knowledge has happened in his encounter with God. Perhaps his suffering was not ultimately meaningless, as his inner being has been brought into congruity with his words – “The Lord gave and the Lord has taken away; may the name of the Lord be praised.” (1:21)</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/4078082939011749188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/4078082939011749188' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4078082939011749188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/4078082939011749188'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2008/11/my-reaction-to-job-38-42.html' title='My reaction to Job 38-42'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-3310441533540390754</id><published>2008-10-13T12:44:00.004-07:00</published><updated>2008-10-13T12:46:52.324-07:00</updated><title type='text'>Wanting to Read Dune</title><content type='html'>The more I do Hebrew, and consequently the more I read, &quot;in my father&#39;s house,&quot; which tends to crop up often, I remember all the talk about &quot;houses&quot; in Frank Herbert&#39;s &lt;a href=&quot;http://en.wikipedia.org/wiki/Dune_(novel)&quot;&gt;Dune&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Not for the last time do I wonder whether I should have done English as my undergrad and then wrote/read/taught fiction.</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/3310441533540390754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/3310441533540390754' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3310441533540390754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/3310441533540390754'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2008/10/wanting-to-read-dune.html' title='Wanting to Read Dune'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9665608.post-1416141287872425691</id><published>2008-09-27T11:01:00.003-07:00</published><updated>2008-09-27T11:22:31.974-07:00</updated><title type='text'>Hebrew&#39;ness</title><content type='html'>For those who may still be following this blog... perhaps I&#39;ll write more when I&#39;m not writing so much for school, which will be done in June/July!&lt;br /&gt;&lt;br /&gt;I spent the summer learning Hebrew, and am now in &quot;intermediate&quot; Hebrew.&lt;br /&gt;&lt;br /&gt;&quot;Intermediate&quot; may lead you, as it did me, to the conclusion that I would be attaining some sort of proficiency in the language. Sorry to disappoint, but I just spent 45 minutes &quot;translating&quot; Exodus 34:11-13. I must place &quot;translating&quot; in quotations because I purchased &lt;a href=&quot;http://www.bibleworks.com&quot;&gt;BibleWorks&lt;/a&gt; and rely heavily upon it. Hopefully Hebrew won&#39;t disappear as quickly from my brain as Greek did.&lt;br /&gt;&lt;br /&gt;I&#39;m also doing a class on Proverbs/Job/Ecclesiastes. The most interesting thing I&#39;ve read lately consequently has been Eccl. 3:9-14. The burden of being pulled into the beauty of present and also carrying eternity in our hearts - being a limited and yet unlimited people - can be quite paralyzing, confusing, disheartening. I want to know the meaning of existence, and yet I can often do no better than enjoy a sunset and a cup of coffee; so beautiful in itself, and yet barely scratches the surface.&lt;br /&gt;&lt;br /&gt;Consider that the teacher then comes to 3:12-14 where he finally for the first time says, &quot;I know.&quot; All his questions, and now he says, &quot;I know&quot; something. What does he know? Give us some answers already! The focus shifts decisively towards God, the one who authors all of this.&lt;br /&gt;&lt;br /&gt;Is he good? Can he be trusted? Can we find some sort of rest, caught between limited&#39;ness and unlimited&#39;ness given that this good and trustworthy God has made us this way &quot;so that men will revere him?&quot;&lt;br /&gt;&lt;br /&gt;Hmmmm...</content><link rel='replies' type='application/atom+xml' href='http://camtucker.blogspot.com/feeds/1416141287872425691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/9665608/1416141287872425691' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1416141287872425691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9665608/posts/default/1416141287872425691'/><link rel='alternate' type='text/html' href='http://camtucker.blogspot.com/2008/09/hebrewness.html' title='Hebrew&#39;ness'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/11002884537810217238</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>