Rusty Divine

Live, Love, Learn, Teach

Try, then Ask Rule

Matt’s “You Must Try, and then You Must Ask” rule reminds me of why I love working on a team.

I have worked remotely before, for a stretch of 4 years, and not only was I working from my house, but I was the only developer on a project most of the time. I also have good friends who are doing well as sole proprietors in software consultancy and have considered whether I should follow their lead.

I know I could do it again, but I would prefer not to. I enjoy talking with my team and having someone around to jolly me out of a funk. Learning is accelerated by seeing how my other team mates are structuring their code and talking about what frameworks they are interested in. Not being the only person on the hook if a deadline is missed or a customer is unhappy is an incredible relief.

At work, we’ve often repeated the mantra that if you get stuck, make sure to ask someone. We have a stand up each day that should bring anything seriously blocking to light – it forces us to be accountable to each other. What I really like about Matt’s post though is that he has dug deeper than the surface to give some solid advice:

  1. If you’ve hit a point of giving up, give it another 15 minutes.
  2. During those 15 minutes, you must document everything you’re doing so that you can tell someone else.
  3. After that, you must ask someone for help.

I like that the second step is to document what you need to do to explain it clearly. Often, I get wrapped up in something I didn’t expect and just confuse myself. When I call someone over to explain it, I end up figuring out the problem myself just by talking through it logically – so, this step can often save my team’s time because often it will be enough to solve the problem.

VS2012 Multi-Project Solution References Error

While working on a plugin for nopCommerce (a payment plugin for Sage Payments, the USA portal, HTTPS), I was getting build errors for references to Nop.Core and Nop.Services.

Error	5	The type or namespace name 'Core' does not exist in the namespace 'Nop' (are you missing an assembly reference?)	C:\Code\SgtNop\Main\Plugins\Nop.Plugin.Payments.SageHttps\SageHttpsPaymentProcessor.cs	8	11	Nop.Plugin.Payments.SageHttps

 

The intellisense was working on Nop's assemblies, but the project wouldn't compile. The problem was my new plugin project was compiling to .Net 4 when the Nop.Core compiles to .Net 4.5. After changing the plugin's target framework to 4.5, everything compiled.

 

CakePHP with IIS, SQL Server, SRS, LDAP, and TFS

Our development team recently completed a project with a requirement that PHP be used and hosted on IIS 7. We are a C#.Net team and have some experience with PHP and we had to work hard to get all of our tools to work together, so hopefully this will help you, too. The project turned out fantastically, thanks in part to a really good product owner on the client side. Thanks goes to Adam Costenbader for some of the notes for these steps.

  1. Install IIS 7 (add/remove programs > configure windows features)
  2. From the IIS Manager, enable FastCGI support.
  3. Download and install PHP 5.3 or later
  4. Install the URL Rewrite extension
  5. Install the PHP Manager
    1. Enable php_pdo_sqlsrv_54_nts.dll for sql server. If missing, you can download it from Microsoft, then install and enable it by adding an extension in the PHP manager.
    2. Enable php_LDAP.dll for LDAP access
  6. In IIS Manager, check that there is a handler mapping for PHP to the FastCGIModule. With the server node selected, enter the Handler Mappings and if *.php is not setup for PHP53_via_FastCGI, then add the mapping. The path is *.php, module is FastCgiModule, executable is C:\[php path]\php-cgi.exe, name is PHP53_via_FastCGI. Select the Request Restrictions and then check Invoke and select File or Folder.
  7. Create a website in IIS Manager and set index.php as the default document for the website.
    1. The security for the root folder needs to permit USERS at the basic level, and IIS_IUSRS needs full permissions to the app\tmp folder.
  8. Install the TFS Power Tools for VS2012 to get a Windows Explorer add-in for TFS
  9. For reporting, install the SQL Server Data Tools
  10. For PHP Intelli-sense, we purchased PHP Tools for Visual Studio
  11. We were never able to get debugging to work with CakePHP, although we tried PHP Tools and Xdebug.
  12. To make the solution work with TFS, we had to make sure the app\tmp directory wasn’t read-only, but the folder structure had to be there. We had trouble with the project trying to add the files back in each time, so we ended up just excluding the app\tmp from source control and then creating the folder structure manually on new checkouts.
  13. To get some SQL Server Reporting Services to serve reports as HTML into a CakePHP view:
    1. We created a local user on the report server and permissions (db_datareader) to the database.
    2. If SRS is on a different server, you need to allow basic authentication by editing the rsreportserver.config found in the Program Files:
      1. <Authentication>
           <AuthenticationTypes>
            <RSWindowsNTLM/>
            <RSWindowsBasic/>
           </AuthenticationTypes>
           <EnableAuthPersistence>true</EnableAuthPersistence>
        </Authentication>
    3. On the report server, run IE as an administrator and browse to the server/reports folder. Navigate to your reports folder and to the Properties tab > Security page. Add the local user here and make sure Browser is checked.
    4. For more examples, see this MSDN post.

We were able to get this CakePHP website running on an Azure website, too, which was a fun challenge (we did not activate the LDAP or SRS portions for this). This was our web.config for Azure:

<!-- Configuration for Windows Azure -->
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <clear/>
        <rule name="Imported Rule 0" stopProcessing="true">
          <match url="^(img|css|files|js)(.*)$"/>
          <action type="Rewrite" url="app/webroot/{R:1}{R:2}" appendQueryString="false"/>
        </rule>
        <rule name="Imported Rule 1" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false"/>
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
          </conditions>
          <action type="Rewrite" url="index.php?url={R:1}" appendQueryString="true"/>
        </rule>
        <rule name="Imported Rule 2" stopProcessing="true">
          <match url="^$" ignoreCase="false"/>
          <action type="Rewrite" url="/"/>
        </rule>
        <rule name="Imported Rule 3" stopProcessing="true">
          <match url="(.*)" ignoreCase="false"/>
          <action type="Rewrite" url="/{R:1}"/>
        </rule>
        <rule name="Imported Rule 4" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false"/>
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
          </conditions>
          <action type="Rewrite" url="index.php/{R:1}" appendQueryString="true"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
<!-- Configuration for Windows Server 2008 --> 
<?xml version="1.0"?>
<configuration> 
  <system.webServer>
    <rewrite>
      <rules>
        <clear/>
        <rule name="Imported Rule 0" stopProcessing="true">
          <match url="^(img|css|files|js)(.*)$"/>
          <action type="Rewrite" url="app/webroot/{R:1}{R:2}" appendQueryString="false"/>
        </rule>
        <rule name="Imported Rule 1" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false"/>
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
          </conditions>
          <action type="Rewrite" url="index.php?url={R:1}" appendQueryString="true"/>
        </rule>
        <rule name="Imported Rule 2" stopProcessing="true">
          <match url="^$" ignoreCase="false"/>
          <action type="Rewrite" url="app/webroot/"/>
        </rule>
        <rule name="Imported Rule 3" stopProcessing="true">
          <match url="(.*)" ignoreCase="false"/>
          <action type="Rewrite" url="app/webroot/{R:1}"/>
        </rule>
        <rule name="Imported Rule 4" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false"/>
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
          </conditions>
          <action type="Rewrite" url="index.php?url={R:1}" appendQueryString="true"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
  <system.web>
    <compilation targetFramework="4.5"/>
    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
  </system.web>

   <system.net >
    <mailSettings>
        <smtp deliveryMethod="SpecifiedPickupDirectory" from="from@me.com">
        <specifiedPickupDirectory pickupDirectoryLocation="c:\Temp\MailDrop" />
      </smtp>-
    </mailSettings>
  </system.net>
  
</configuration>

Breaking the 1000ms Time-To-Glass mobile barrier

Ilya Grigorik is one of Google's engineers who is on the Make the Web Fast team. His talk on Breaking the 1000ms Time to Glass Mobile Barrier has some great tips:

Optimize Images:

 

This may mean making jpgs instead of pngs.

Mobile Latency:

 

The time it takes to send that first packet can eat up half-a-second!

Async scripts:

 

  Performance Rules

 

 

 

 Implications

 

 

 Bottom Line

One Request. Inline. Defer the rest.

Nebraska Code Camp 3

This year at Nebraska Code Camp for the first time there was an extra day of hands-on labs. I signed up for Advanced Machine Learning and a Single Page App (SPA) lab that was cancelled at the last minute, so instead I crashed Adam Grocholski's Windows 8 lab where we had a lot of fun just playing around.

I'm really proud to work for Five Nines Technology Group who was a Platinum Sponsor this year - we are really excited about the programming community here in Nebraska and enjoy contributing to its vibrancy.

 

Machine Learning Notes

My favorite part of labs are always the great resources for continued learning I get from the instructors. I took the machine learning lab because it was out of my comfort zone, but it is something that I find could be quite useful. For instance, a potential customer recently asked us to write an algorithm that compared athletes across the nation and rank them by some test results.

So, here are the resources Luke Amdor shared with us:

Some of the topics he covered were:

 

Windows 8 Notes

This was a full-day lab, but I only caught the last half. The first half they did some hands-on labs at MSDN; here are some example labs you can do.

We took a look at many of the Azure offerings and talked about differences between mobile services and cloud services and networking and active directory. I totally encourage you to sign up for a free 90-day trial!

We also had a lot of fun with game programming. Adam's favorite is TouchDevelop - a really cool game and app programming tool that is free and lets you share code snips. 

How To Set a Modified Date in Entity Framework

Just saw Julie Lerman show a slick way to set the modified date on an entity by overriding the SaveChanges() on a DbContext:

 

 

Other good tips:

  • Download the EF PowerTools for a nice plug in that lets you rt-click on the class that implements DbContext and select EF\View Entity Data Model (read only) => to get a relational diagram of how EF is going to create your database before you do a migration.

 

  • In your models, use both the navigation property and the foreign key to increase performance (w/out FK, EF has to go figure out what ID to put in there), to make some object graph management more intuitive for adding/updating or just figuring out if that navigation property exists without invoking lazy loading.

 

  • For lookup-lists, you can improve performance by not tracking the entity state:

_context.MyEntity.AsNoTracking().OrderBy(a=>a.Name).ToList();

When you do the AsNoTracking, combine it with the previous tip so that EF doesn't add a new MyEntity when you save the parent each time:

parent.MyEntityId = Id; // instead of parent.MyEntity = myEntity <- that might cause EF to save a new (duplicate) myEntity to the database

 

Subtle MVC, Razor, Membership Bug

I encountered a really frustrating bug yesterday when I tried to hit my MVC project on the QA server, and it turns out it manifested itself in several telltale ways:

  1. The QA server (IIS 7) reported 403 Forbidden for my login page.  Could not load anything.
  2. My local machine (IIS 7 Express) would not show the ValidationSummary on the login page
  3. My local machine would not let me navigate to the forgot password page, or any [Allow Anonymous] pages other than the login page, unless I logged in;
  4. after logging into my local machine, and then everything worked fine, and I could get to the [Allow Anonymous] pages
  5. My local machine would not show the company name text in the banner until I logged in

The last sign was finally what clicked – I didn’t realize these all were related until I noticed that one.  On my _layout.cshtml page, I have the following:

@{ Html.RenderAction("_CompanyName", "CompanyName"); }

The site is a multi-tenant application that has a url like: {company}.domain.com.  The above calls into an action that takes that {company} and looks up the company name in the repository, then displays that name in the banner.

The problem was the _CompanyName action on the CompanyNameController was not marked as [Allow Anonymous].  IIS Express was able to let this slide, but IIS did not!

Entity Framework (EF) Migrations–Quick Cheat

This isn’t quite a cheat sheet, but it is a quick reference to the workflow for using EF Database Migrations.

I’m also using MvcScaffolding on my controllers with the –Repository flag to stub out my controller, views, and repository.

 

EF_Migrations

Calculating Running Totals using TSQL in SQL Server

The fastest method I’ve found of calculating running totals in tsql is to use a temporary table.

--Create a temporary variable
declare @RunningTotal money
set @RunningTotal = 0

--Setup a results table
declare @results table(RowId int, CustomerId int, TransactionDate date, TransactionType varchar(50), 
    Reference varchar(50), Amount money, DaysSince int, RemainingAmount money, RunningTotal money default 0)

--Insert everything except the running total
insert into @results(RowId, CustomerId, TransactionDate, TransactionType, Reference, Amount, DaysSince, RemainingAmount)
SELECT  
    row_number() over (order by art.Date, ardt.Name),
    art.CustomerId,   
    art.Date, 
    ardt.Name AS [Transaction Type], 
    case IsNull(art.Reference,'') when '' then art.DocumentNumber else art.Reference end as Reference, 
    art.Amount,
    datediff(d, art.date, getdate()) daysDiff,
    art.RemainingAmount
FROM         
    dbo.ARTransactions AS art INNER JOIN
    dbo.ARDocumentTypes AS ardt ON art.ARDocumentTypeId = ardt.ARDocumentTypeId
WHERE     
    (art.CustomerId = @CustomerId)
    and (art.RemainingAmount <> 0 or datediff(d, art.date, @StatementDate) < 30)
order by
    art.Date, 
    ardt.Name

--Now, update the running total using our temp variable
update @results
set @RunningTotal = RunningTotal = @RunningTotal + RemainingAmount

--Return the results
select * from @results order by RowId

Defeated BrowserQuest

 

Just spent a fun hour playing Mozilla's new HTML 5 retro MMO game, BrowserQuest (more info).  They are using node.js to do web sockets, which allow them to communicate bi-directionally between the browser and the server.  Since it's just HTML5 and Javascript, you should be able to play it on your mobile phone, too.

After playing for about 30 minutes, my browser (Chrome) crashed and I had to restart my computer to get my mouse pointer back.  I was relieved that BrowserQuest also uses HTML 5's local storage, so I was able to pick up the game right where I left off!  

And, of course I had to play until I won (that's me below after defeating the final boss)!