Referenced Projects in a solution disappear on compile time Visual Studio 2010

Take the following example of a solution:

  • MyProject
  • MyProject.Core
  • MyProject.Infrasturcture

I decide to add a new console application called MyProject.Console. I reference the MyProject.Core into this console application. Everything is working fine until I try to build and run the console application. I get a error saying “Could not find the referenced Assembly.”.

I figured out that when I created the new console application in Visual Studio 2010 it defaulted to .NET 4 Client Profile as the framework and not .NET 4. This is because all my other projects were using the .NET 4 framework.

References

http://stackoverflow.com/questions/4286599/referenced-project-gets-lost-at-compile-time

The property could not be set to a ‘Int16′ value. You must set this property to a non-null value of type ‘Int32′.

I came across the following error when trying to map a stored procedure to a complex type in EF 4+.

The property could not be set to a ‘Int16′ value. You must set this property to a non-null value of type ‘Int32′.

The problem was the Entity Framework was thinking the return data type was Int16 but in fact the complex type value was specified as Int32.

The solution was to make sure that your results from the stored procedure are explicitly set and cast to the right data types. You can do this by using the convert function in T-SQL. Example below.

1
 select convert(int,myValue) from foo

Once you do implement this function into your stored procedures, you can update your mappings in EF and the problem will be fixed (well for me anyway).

Giving Enum’s descriptions

Enums are very good for giving descriptions to values especially numeric values. By default though a enum value name cannot have spaces. Look at the following which shows an example of no spaces between the first enum value name.

1
2
3
4
enum myEnum
{
   Icannothaveaspace = 1
}

A good way to solve this problem is to use the Description attribute from the System.ComponentModel namespace. We can use this attribute to give each enum value a descriptive meaning.

1
2
3
4
5
6
7
8
9
using System.ComponentModel;

 public enum MyEnum
    {
        [Description("Authorised Contact")]
        AuthorisedContact = 1,
        [Description("Assigned Contact")]
        CoauthorisedContact = 2
    }

From here we can create a extension method that uses reflection to get the description attribute for the selected enum value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class Extensions
    {
        /// <summary>
        /// Get the description of a enum value using the description attribute
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetEnumDescription(this Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(
                typeof(DescriptionAttribute),
                false);

            if (attributes != null && attributes.Length > 0)
                return attributes[0].Description;
           
            return value.ToString();
        }
    }

To use this, just call GetEnumDescription to get the description of a enum value.

1
2
3
4
5
6
7
8
9
  using Extensions; //Make sure that you use this to get the extension method
 
  public myClass
  {
      public void foo()
      {
          var myDescription = MyEnumValue.GetEnumDescription();
      }
  }

Setting the screen position when visiting previous page with jQuery

The scenario

This problem came while I was working on a client’s website. Take this scenario. I have a listing page (similar to a listing page from www.trademe.co.nz or www.ascent.co.nz ). They have a lot of listings. Each listing has a hyperlink going to a detail page which describes more about each listing.
If you have quite a large page of listings, the user has to scroll down the page to find the listing they want to click on. The problem is, when they click on the link and go to the details page, what happens when the click back and they end up back at the top of the page again? This can be quite annoying. It would be “nice” to get the browser to go back to the previous position on the listing page.

The Approach

We achieve this but attach a click event handler to the specific a tags that we want to record the last clicked position from the user before we redirect to the next page. This means when the user goes back to the previous page they will be taken to the exact position on the page. When the click event happens we always override what was in the cookie so that we don’t end up with several pages and positions. We don’t want a user to go to another page they have been to before and set the scroll position, it should only happen to the previous page.

Here is the code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var COOKIE_NAME = "scroll";
 
$(document).ready(function () {
    // Check to see if the user already has the cookie set to scroll
    var scrollPosition = getCookiePosition(COOKIE_NAME);
    if (scrollPosition.length > 0) {
        // Scroll to the position of the last link clicked
        window.scrollTo(0, parseInt(scrollPosition, 10));
    }
 
    // Attach an overriding click event for each link
    // saveScrollPosition function can be called before the user is redirected.
    $("a.savel").each(function () {
        $(this).click(function () {
            saveScrollPosition($(this));
        });
    });
});
 
// Get the offset (height from top of page and the current page url) of the link element
// and save it in a cookie.
function saveScrollPosition(link) {
    var linkTop = link.offset().top;
    setCookie(COOKIE_NAME, "pos=" + linkTop + "&link=" + escape(window.location.pathname), 1);
}
 
//Trim whitespaces
function trim(str) {
   return str.replace(/^\s+|\s+$/g, '');
}
// Get cookie helper function
function getCookiePosition(name) {
    if (document.cookie.length > 0) {
        //Get the right cookie
        var cookies = document.cookie.split(";");
        for (var i = 0; i < cookies.length; i++) {
           
            if (trim(cookies[i].split("=")[0]) == COOKIE_NAME) {
                //Get the position of the & sign
                var pos = cookies[i].indexOf("&");
                if (pos > -1) {
                    //Get value
                    var scrollValue = cookies[i].split("=")[2].substring(0, cookies[i].split("=")[2].indexOf("&"));
                    var link = unescape(trim(cookies[i].split("=")[3]));
                    //if the link is the same as the current page, then assume user wants to go back to the same position
                    if (link == window.location.pathname) return scrollValue;
                }
                return "";
            }
        }
        return "";
    }
    return "";
}
 
//Method to create a new cookie
function createCookie(name, value, days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        var expires = "; expires=" + date.toGMTString();
    }
    else var expires = "";
    document.cookie = name + "=" + value + expires + "; path=/";
}
 
// Set cookie
function setCookie(name, value, expiredays) {
    //Remove all other cookies
    var cookies = document.cookie.split(";");
    for (var i = 0; i < cookies.length; i++) {
        if (trim(cookies[i].split("=")[0]) == COOKIE_NAME) {
            createCookie(trim(cookies[i].split("=")[0]), "", -1);
        }
    }
    //Set the new cookie
    createCookie(name, value, expiredays);
}

Magento Fontis Paymate Module Fix for Not Passing Data Issue

I upgraded my magento from 1.4.0.1 to 1.6.1 and yes a lot of things broke especially the ability to pay using Paymate.
I did some research and found a lot of people having the issue of non data being passed from Magento to Paymate to populate the fields like address, amount etc. Since there has been no support at all? I decided to take matters into my own hands.

Here is the code from the file: /app/code/community/Fontis/Paymate/Model/Paymate.php
replace this file with your original file Paymate.php. As always I suggest that you back up everything!
This worked for me. Technically the issue was the code didn’t like getting the getQuote() from the checkout/session object. Instead getting the getLastRealOrderId from checkout/session and loading a new model with the order id worked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<?php
/**
 * Fontis Paymate Extension
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so one can be sent to you a copy immediately.
 *
 * @category   Fontis
 * @package    Fontis_Paymate
 * @author     Lloyd Hazlett
 * @author     Chris Norton
 * @copyright  Copyright (c) 2010 Fontis Pty. Ltd. (http://www.fontis.com.au)
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */

/**
 * Paymate payment model
 *
 * @category   Fontis
 * @package    Fontis_Australia
 */
class Fontis_Paymate_Model_Paymate extends Mage_Payment_Model_Method_Abstract
{
    const CGI_URL = 'https://www.paymate.com/PayMate/ExpressPayment';
    const CGI_URL_TEST = 'https://www.paymate.com/PayMate/TestExpressPayment';
    const REQUEST_AMOUNT_EDITABLE = 'N';

    protected $_code  = 'paymate';
    protected $_formBlockType = 'fontis_paymate_block_form';
    protected $_allowCurrencyCode = array('AUD', 'EUR', 'GBP', 'NZD', 'USD');
   
    protected $_isGateway               = false;
    protected $_canAuthorize            = false;
    protected $_canCapture              = true;
    protected $_canCapturePartial       = false;
    protected $_canRefund               = false;
    protected $_canVoid                 = false;
    protected $_canUseInternal          = false;
    protected $_canUseCheckout          = true;
    protected $_canUseForMultishipping  = false;
   
    /**
     * Assign data to info model instance
     *
     * @param   mixed $data
     * @return  Fontis_Australia_Model_Payment_Paymate
     */
    public function assignData($data)
    {
        $details = array();
        if ($this->getUsername())
        {
            $details['username'] = $this->getUsername();
        }
        if (!empty($details))
        {
            $this->getInfoInstance()->setAdditionalData(serialize($details));
        }
        return $this;
    }

    public function getUsername()
    {
        return $this->getConfigData('username');
    }
   
    public function getUrl()
    {
        $url = $this->getConfigData('cgi_url');
       
        if(!$url)
        {
            $url = self::CGI_URL_TEST;
        }
       
        return $url;
    }
   
    /**
     * Get session namespace
     *
     * @return Fontis_Australia_Model_Payment_Paymate_Session
     */
    public function getSession()
    {
        return Mage::getSingleton('paymate/paymate_session');
    }

    /**
     * Get checkout session namespace
     *
     * @return Mage_Checkout_Model_Session
     */
    public function getCheckout()
    {
        return Mage::getSingleton('checkout/session');
    }

    /**
     * Get current quote
     *
     * @return Mage_Sales_Model_Quote
     */
    public function getQuote()
    {
        return $this->getCheckout()->getQuote();
    }

    public function getCheckoutFormFields()
    {
        $orderIncrementId = $this->getCheckout()->getLastRealOrderId();
            $order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrementId);
           
        $a = $order->getShippingAddress();
        $b = $order->getBillingAddress();
        $currency_code = $order->getCurrencyCode();
        //$cost = $order->getSubtotal() - $order->getDiscountAmount();
        $cost = $order->getGrandTotal();
        //$shipping = $order->getShippingAmount();

        //$_shippingTax = $order->getShippingAddress()->getTaxAmount();
        //$_billingTax = $order->getBillingAddress()->getTaxAmount();
       
        //$tax = sprintf('%.2f', $_shippingTax + $_billingTax);
        //$cost = sprintf('%.2f', $cost + $tax);
       
        $fields = array(
            'mid'                   => $this->getUsername(),
            'amt'                   => sprintf('%.2f', $cost + $shipping),
            'amt_editable'          => self::REQUEST_AMOUNT_EDITABLE,
            'currency'              => $currency_code,
            'ref'                   => $orderIncrementId,
            'pmt_sender_email'      => $b->getEmail(),
            'pmt_contact_firstname' => $b->getFirstname(),
            'pmt_contact_surname'   => $b->getLastname(),
            'pmt_contact_phone'     => $b->getTelephone(),
            'pmt_country'           => $b->getCountry(),
            'regindi_address1'      => $b->getStreet(1),
            'regindi_address2'      => $b->getStreet(2),
            'regindi_sub'           => $b->getCity(),
            'regindi_state'         => $b->getRegion(),     // Returns full state name
            'regindi_pcode'         => $b->getPostcode(),
            'return'                => Mage::getUrl('paymate/paymate/complete'),
            'popup'                 => 'N',
        );

        // Run through fields and replace any occurrences of & with the word
        // 'and', as having an ampersand present will conflict with the HTTP
        // request.
        $filtered_fields = array();
        foreach ($fields as $k=>$v) {
            $value = str_replace("&","and",$v);
            $filtered_fields[$k] =  $value;
        }
       
        return $filtered_fields;
    }

    public function createFormBlock($name)
    {
        $block = $this->getLayout()->createBlock('paymate/paymate_form', $name)
            ->setMethod('paymate')
            ->setPayment($this->getPayment())
            ->setTemplate('fontis/paymate/form.phtml');

        return $block;
    }

    /*validate the currency code is avaialable to use for paypal or not*/
    public function validate()
    {
        parent::validate();
        $currency_code = $this->getQuote()->getBaseCurrencyCode();
        if (!in_array($currency_code,$this->_allowCurrencyCode)) {
            Mage::throwException(Mage::helper('paymate')->__('Selected currency code ('.$currency_code.') is not compatabile with Paymate'));
        }
        return $this;
    }

    public function onOrderValidate(Mage_Sales_Model_Order_Payment $payment)
    {
       return $this;
    }

    public function onInvoiceCreate(Mage_Sales_Model_Invoice_Payment $payment)
    {

    }

    public function canCapture()
    {
        return true;
    }

    public function getOrderPlaceRedirectUrl()
    {
          return Mage::getUrl('paymate/paymate/redirect');
    }
}

When to use TempData in ASP.NET MVC

The problem

I came across this problem where I had a basic list of items and for each if these items I wanted to add a delete action on my controller. I wanted to add some error checking around the delete action so if someone attempted to modify the URL and enter a incorrect parameter into my controller, I wanted to redirect back to the previous controller and notify the user of this mistake. Take this example. I have a Product Controller and the Index Action and View lists all of the products. I have a delete action on the Product Controller which checks if the productId parameter is valid and then removes the product and redirects to the Index action using the RedirectToAction method.

The solution

When validating the productId, if its NOT valid simply add a new key/value to the TempData object and call the RedirectToAction method. TempData stores data for short periods of time for the current and next HTTP request. In your index view, you can check to see if your key/value pair exists and display a error message. If you refresh the page, you will notice the data from the TempData will be gone. Remember to use RedirectToAction where possible as this is more friendly with Unit Testing rather than using the HttpResponse redirect method.

This reference illustrates a good understanding between TempData, ViewBag and ViewData.

http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications

Perform ASP.NET Postback using JQuery

Came across a situation where I needed to disable the ASP.NET button using JQuery when the user clicks on the button, this is so we stop the user from clicking the button twice while the page is doing a postback.

Firstly, add the following JQuery/Javascript code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>

 function autoSubmit()
 {
 <%= ClientScript.GetPostBackEventReference(btnSaveChanges, "") %>;
 }
     $(function () {

           $("#<%= btnSaveChanges.ClientID %>").click(function () {
             $(this).attr("disabled", "true");
             autoSubmit();
           
             return false;
         });
     });
</script>

You can inject the correct generated javascript code from using the function GetPostBackEventReference between the server tags. Bind a click event to the ASP.NET server control which disables the button and call the autosubmit javascript function to perform a post back on the ASP.NET button control.

References

http://msdn.microsoft.com/en-us/library/ms153112.aspx

Some useful Extension Methods

Back to basics…

A couple of useful Extension methods if you are checking if a string is a valid integer or decimal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static bool IsInteger(this string value)
{
if (String.IsNullOrEmpty(value)) return false;
Int32 tmpNo;
return Int32.TryParse(value, out tmpNo);
}


public static bool IsDecimal(this string value)
{
if (String.IsNullOrEmpty(value)) return false;
Decimal tmpNo;
return Decimal.TryParse(value, out tmpNo);
}

To implement the Extension Methods:

1
2
3
4
5
6
7
8
9
var myString = "10.5";

if (myString.IsDecimal())

{

//Do something

}

Areas and ASP.NET MVC Routes Tip

Lets say that you have several areas define in your ASP.NET MVC solution. You find that when you run your application you get the following error “Multiple types were found that match the controller named ‘Home’.”. This is because ASP.NET MVC finds all the routing definitions (i.e in each area.cs file or the global.asax) and sees conflicts with duplicate controller names.
Simple solution is to associate the namespace with the routes you are registering. Take the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// global.asax route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "MyApplication.Controllers" } // Controller Namespace
);
?
// Area Registration Route
context.MapRoute(
MyArea_default,
"MyArea/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new string[] { "MyApplication.Areas.MyArea.Controllers" }
);

Credit to Steve Testa, Thanks!.

OrderBy making null records come last Entity Framework and Linq

Ever want to sort your result set but make sure the records with a null sorting column appear last?
See the example below

1
2
3
4
5
6
7
8
9
10
var results = (from x in EntityObjectContext.MyTable
              select x
              into grp
              select new
              {
            mydata = grp,
            myid = grp.id,
            mysortingentity = grp.mysortingentity
              }
             ).OrderBy(x => sortingcolumn == null).ThenBy(x => x.mysortingentity.Name).ThenBy(x => x.myid);

Watch this space, this can easily be transformed into a Extension Method. Check out this link http://tahirhassan.blogspot.com/2010/06/linq-to-sql-order-by-nulls-last.html, this Extension method works for LinqToSQL.