Static Repositories (vs Instance)

by MikeHogg30. July 2012 10:10

I find for small projects that I always used to create static repositories.  It can be a speedy mechanism to develop with, and I probably took the lead from MS' Membership classes and their treatment of Membership classes, plus years of working against Oracle databases when no .Net ORM existed that would go against Oracle...

 

I like static because I use them to return typed collections, and they, with linq if necessary, provide easy one liners in most cases where I interface with data access, and don't need to cache or watch my calls for performance, like in Web sites. But, in web cases, note that on each POST or GET, you are recreating all objects anyway, so I would just add predicate calls to static repo.  Here's what many of my repo Getxxx methods look like:

 

 

internal static IEnumerable<Models.FileModel> GetFileModels(string username = null)
        {
            List<SqlParameter> parms = new List<SqlParameter>();
            parms.Add(new SqlParameter("@EmailAddress", username));
            IDataReader r = DatabaseHelper.GetDataReader("sp_GetFileModels", parms);
            DataTable t = new DataTable();
            t.Load(r);
            var result = from DataRow row in t.Rows
                         select new Models.FileModel
                         {
                             Id = Convert.ToInt32(row["Id"]),
                             RequestDate = DateTime.Parse(row["RequestDate"].ToString()),
                             TitleName = row["TitleName"].ToString(),
                             SomethingId = Convert.ToInt16(row["SomethingId"]),
                             SomethingTypeId = Convert.ToInt16(row["SomethingTypeId"]),
                             FileName = row["Name"].ToString(),
                             FileTypeId =  Convert.ToInt32(row["FileTypeId"]),
                              FileTypeName = row["FileTypeName"].ToString(),
                              ContentEncoding = row["ContentEncoding"].ToString(),
                              ContentLength = Convert.ToInt32(row["ContentLength"]),
                             ActiveFlag = Convert.ToBoolean(row["ActiveFlag"]),
                             EnabledFlag = Convert.ToBoolean(row["EnabledFlag"]),
                             SomeCompanyName = row["SomeCompanyName"].ToString(),
                             SomeNumber = row["SomeNumber"].ToString(),
                             EstimatedNumberOfSomethings = MH.lib.DatabaseHelper.ConvertDBNullsToNInt(row["EstimatedNumberOfSomethings"]),
                             EstimatedDeadline = Convert.IsDBNull(row["EstimatedDeadline"]) ? new DateTime() : DateTime.Parse(row["EstimatedDeadline"].ToString()),
                             FinalDeadline = DateTime.Parse(row["FinalDeadline"].ToString()),
                             ActivityDate = DateTime.Parse(row["ActivityDate"].ToString()), 
                         };
 return result;
        }

 

 

And then in my business layer I simply Getxxx and if I need a single or a filtered set I simply add a linq predicate.

 

 

model = lib.Repository.GetFileModels(UserNameOrNullIfAdmin()).FirstOrDefault(f =>
                    f.SomethingId == somethingid &&
                    (f.FileTypeId == filetypeid || ( filetypeid == 0 && f.SomethingTypeId == lib.CONST.ACONSTANTID &&// in case of xyz filetypeid arg is zero
                                                        (f.FileTypeId == lib.CONST.SOMECONSTANTID || f.FileTypeId == lib.CONST.ANOTHERCONSTANTID)))
                    && f.ActiveFlag == true);
 if (model == null)
                {
                    Models.Something s = lib.Repository.GetSomethings(UserNameOrNullIfAdmin(), somethingid).FirstOrDefault();
                    model = new Models.FileModel
                  {
                      SomethingId = somethingid,
etc., etc.

 

I know this isn't pushing my predicate all the way to the database like Entity Framework so it is something to watch for.  In my experience, most small cases can get to the order of several hundreds of business objects and only require light attention to performance.  And this handles most non-business related applications.

 

But most of the MVC framework codebases and examples I have seen out there that is shared or posted on blogs, happen to use the instance repository pattern... And I hate not knowing Why something is the way it is, so … what are the reasons NOT to use static Repos?

 

I searched for a while, but I only found maybe one informative post, with a couple good answers.

http://stackoverflow.com/questions/5622592/why-arent-data-repositories-static

 

It makes a lot of sense.  Instantiated Repositories can be mocked, and your front layers tested.  That is the number one reason.  I'm not sure that instantiation is a requirement for caching, but it makes sense if you are getting into a site that large.  Second reason – for me this is a big one – is that you can use the inheritance and interfaces to practice DRY and the Repository pattern and not have to write all of your boilerplate data access code in each repository. 

 

The first point speaks to testing, and I haven't seen or heard of any local developers who work at a place that actually promote unit tests.  But I do on occasion in some of my projects, and it helps my writing style.  Food for thought...

JQuery

by MikeHogg5. July 2012 09:49

 

On my third web project, I got the opportunity to dive into jquery a little more.   We were rewriting an existing site in MVC3.  One of my tasks was to reproduce a standard sort of “Locations” page.  I had some javascript to work from, but we were adding a new “Location Features” feature, and a GPS feature, and the existing javascript wasn’t in any shape to be extended.  I only needed a small form, and my only server side code, my Location Features MVC3 call, was a three liner LINQ query against an Entity Framework db, so most of the task was my opportunity to rewrite some old mess of long cryptic javascript function into jquery, and figuring out a good readable, maintainable code flow.    In the first page here you will see my standard style of using the MVC model binding, even for two properties, and a very short clean page of html, with my CSS file referenced in the beginning, and my JS file referenced at the end.

 

 

 

@model SomeBase.Models.LocatorModel
<link rel="stylesheet" href="@Url.Content("~/Content/css/locator.css")">
<div class="locatordiv">
    @using (Html.BeginForm("Locator", "Home", FormMethod.Post, new { id = "locator" }))
    { 
        <p>  Enter a city and state, or zip code below.</p>
 
        @Html.HiddenFor(m => m.Gps)
        @Html.TextBoxFor(m => m.Zip, new { title = "Enter ZIP", @Class = "textbox" })
        <input type="submit" value="" class="findbutton" data-category="Find" data-event="Homepage Zipcode" />
        <a href="#" class="gpsbutton" onclick="do_geo();" title="GPS" data-category="Find" data-event="GPS"></a>
        <div id="gpsloading">Working ...<br /><img src="@Url.Content("~/Content/images/load-bar.gif")" /></div>
    } 
</div>
<div id="maploading">
    <img src="@Url.Content("~/Content/images/load-circle.gif")" />
</div>
<div id="results"></div>
<div id="mapDiv"></div>
<script type="text/javascript">
 var configMQAPIKey = '@Html.Raw(System.Configuration.ConfigurationManager.AppSettings["MQ:APIKey"].ToString())';
 var configMQOLOKey = '@Html.Raw(System.Configuration.ConfigurationManager.AppSettings["MQ:OLOKey"].ToString())';
 var configMQHostedDataTable = '@Html.Raw(System.Configuration.ConfigurationManager.AppSettings["MQ:HostedDatatable"].ToString())';
        </script>
        <script src='//www.mapquestapi.com/sdk/js/v7.0.s/mqa.toolkit.js?key=@Html.Raw(System.Configuration.ConfigurationManager.AppSettings["MQ:APIKey"].ToString())'></script>
        <script src="@Url.SomeContent("~/Content/js/locator.js")"></script>
        <script type="text/javascript" src="@Url.Content("~/Content/js/jquery.cookie.js")" ></script>

The javascript wasn’t terribly complicated, but it was fun to get deeper into jquery and learn to use standard jquery element creation and manipulation methods, and the unobstrusive javascript pattern, as opposed to the old verbose javascript getelementById calls.   Here you will see my style of paying special attention to method and variable names, in place of comments.  I was taught that comments should be for WHY not WHAT, and if you name your objects clearly enough, you don’t need to comment WHAT you are doing.

 

 

function searchByGps() {
    $("#Zip").val(""); $("#Key").val("")
    search("https://www.mapquestapi.com/search/v1/radius" +
                    "?key=" + mq_key + "&radius=" + $("#inradius").val() + "&callback=processPOIs&maxMatches=" + inmatch +
           "&origin=" + encodeURIComponent($("#Gps").val()) + "&hostedData=" + intable);
} 
function searchByZip() {
    $("#Gps").val(""); $("#Key").val("")
    search("https://www.mapquestapi.com/search/v1/radius" +
                    "?key=" + mq_key + "&radius=" + $("#inradius").val() + "&callback=processPOIs&maxMatches=" + inmatch +
           "&origin=" + encodeURIComponent($("#Zip").val()) + "&hostedData=" + intable);
} 
 
function search(url){
    MQA.IO.doJSONP(url);
}
function processPOIs(results) {
if (results.searchResults != null && results.searchResults.length > 0) {
        drawPOIsOnMap(results.searchResults);
        drawResultTable(results.searchResults);
if ($("#Key").val()) {
            $("#Zip").val(results.searchResults[0].fields.address + " " + results.searchResults[0].fields.city + ", " +
                          results.searchResults[0].fields.state + " " + results.searchResults[0].fields.postal);
        }
        $.ajax('/Home/GetLocationFeatures', {
            data: JSON.stringify(parseToEntityObjects(results.searchResults)),
            dataType: "json",
            type: "post",
            contentType: "application/json",
            success: function (featuredata) { processFeatures(featuredata, results.searchResults); }
        });
    } 
else if (results.info && results.info.statuscode == 610) {
// ambiguities
// results.collections[1] is To, 0 is From
        $("#Gps").val(results.collections[0][0].latLng.lat + ',' + results.collections[0][0].latLng.lng);
        searchByGps();
    }
else {
        $('#results .frame').html("<h1>Oops! We couldn’t find any results. Please try your search again.</h1>");
    }
}
function processOLOs(oloData, searchResults) {
    $.each(searchResults, function (i, result) { 
if (oloData != null) {
            $.each(oloData.restaurants, function () {
 if (this.telephone == result.fields.Phone) {
 var oloid = '#olo' + result.fields.RecordId;
                    $(oloid).append($("<a></a>", { href: this.url, "class": "ololinks", target: "_blank", text: "Place an Order" }));
                } 
            });  }   }); } 
function processFeatures(featureData, searchResults) {
    $.each(searchResults, function (i, result) { 
if (featureData != null) {
            $.each(featureData, function () {
 if (this.StoreNumber == result.fields.RecordId) {
 var fid = '#feature' + result.fields.RecordId;
                    $(fid).append($("<a></a>", {href:this.Url, "class":"featurelinks", target:"_blank", text:this.Label}));
                }   });         }  });  } 
function drawResultTable(results) {
    $("#maploading").hide();
    $('#results').html("<h1>Search Results</h1>");
    $.each(results, function (i, result) {
 
        $("<div></div>", { "class": 'resultrow' })
                     .append("<p class='addressrowresult'>" +
                result.fields.address + "<br/ >" +
                result.fields.city + ", " + result.fields.state + "<br/ >" +
                result.fields.Phone + "<br /></p>")
            .append("<div>" + getRoundedDistance(result) + " Mi." + getMapItLink(result) + "</div>") 
            .append("<div class='olos' id='olo" + result.fields.RecordId + "'></div>") // olo can find this span$(#olo#storenumber#) later
            .append("<div class='features' id='feature" + result.fields.RecordId + "'></div>") // feature can find this span$(#feature#storenumber#) later
            .appendTo("#results");
    }); } 
function parseToEntityObjects(data) {
var locations = [];
    $.each(data, function () {
        locations.push({
            StoreNumber: this.fields.RecordId,
            Address1: this.fields.address,
            City: this.fields.city,
            ZipCode: this.fields.postal,
            Phone: this.fields.Phone
        });
    });
return locations;
}
function getRoundedDistance(result) { 
if (result.distance > 10) {
return Math.round(result.distance);
    } else if (result.distance > 1) {
return Math.round(result.distance * 10) / 10;
    } 
return Math.round(result.distance * 100) / 100;
}
function getMapItLink(result){ 
var destination = result.fields.address + ',' + result.fields.city + ',' + result.fields.state + ',' + result.fields.postal;
var link = $("<a>", { href: "#", "class": "mapitlink", title: "Map It!", onclick: "mapIt(getOrigin(), '" + destination + "');return false;", text: "Map It!" })
                .attr({ "data-category": "Find", "data-event": "Map Quest" });
return $('<div>').append(link.clone()).html();// hack to get string not js object for mqa
} 
function getPOIRollover(result) {
var rollover = $("<div></div>", { "class": "poirollover" }).append($('<h4></h4>').text(result.fields.N))
        .append($('<span></span>').text(result.fields.address))
        .append($('<br />')).append($('<span></span').text(result.fields.city + ", " + result.fields.state))
        .append($('<br />')).append($('<span></span>').text(result.fields.Phone))
        .append($('<br />')).append($('<span></span>').text(getRoundedDistance(result) + " Mi."))
        .append(getMapItLink(result));
return $('<div>').append(rollover.clone()).html(); // hack to get string not js object for mqa
}
function mapIt(origin, destination) {
// build a url and send to the Directions Web Service
    MQA.IO.doJSONP("http://www.mapquestapi.com/directions/v1/route?" +
              "key=" + mq_key + "&" +
              "from=" + origin + "&" +
              "to=" + destination + "&" +
              "shapeFormat=raw&generalize=0.1&" +
              "callback=drawDirections");
}

 

My CSS file was mostly empty at the start.  But I wanted to make it easy to add style later, by applying classes and these empty placeholders for just about every element that we might want.

 

 

.locatordiv
{
text-align:center;
margin:20px;
} 
.gpsbutton {
background:url(../images/icon-gps.png) no-repeat;
background-size:42px auto;
padding:21px; 
} 
#gpsloading
{ display:none;
}
#maploading
{ display: none;
}
#mapDiv
{ float:left;
}
#results
{ float:left;
}
.resultrow
{ margin:20px;
}
.mapitlink
{ 
}
.poirollover
{ width:150px;
}
.olos
{
}
.ololinks
{
}
.features
{ display:block;
}
.featurelinks
{ display:block;
}
.addressrowresult
{ width: 210px;
}
#directionsdiv
{ 
}
.directionrow
{ 
}

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more.amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here