Pages - Menu

Should MVC ViewModel have methods?

Introduction

By design principle, ViewModel only contain properties, it certainly wouldn't have methods. However, there is always this temptation of convenience over convention, and to do this, we need excuses.

I have been working on MVC for a few years, but what is the big deal about it? In the old web form days, we always have this sort of ideas where front end developer will do layouts or javascripts, and then back end developers doing all these database calls and other logics. MVC and HTML5 bringing all these to the next level, View is predominantly used by front end developer, and they do not need to know anything about Controller or Repository, where a backend developer only responsible to populate the model and pass to the view then their jobs are done.

This is all achievable by having this separation of concern. With all these in minds, should MVC ViewModel have methods?

See it in sample

As usual, I pick example from first page result from google search.

Ref: http://forums.asp.net/t/1787772.aspx

The following method is inside a viewmodel. It claims that the method is for presentation only, therefore it is kinda legit.
   1: public IHtmlString Checked(double lower, double upper) {
   2:     return this.AverageRating > lower && this.AverageRating <= upper 
   3:         ? new HtmlString(" checked=\"checked\"") : null;
   4: }

But for real?

In my opinion, it is a no for me. If back end developer are to write this view model class, should I be concerned if the front end is using HTML5, JQuery or Flash?

My Alternative Solution

ViewModel

   1: public bool isAverage { get; set; }

Controller

   1: model.isAverage = (model.AverageRating > lower && model.AverageRating <= upper);

View

Checks isAverage and renders different HTML.


And you got the idea?

Verdict

I still have not encountered a situation where I have to put methods in a model, if you got one, you are welcome to fire to me. :)

The closest one that I've got is this one. Taken from an example that I have written half a year ago.


   1: public int? Age {
   2:     get 
   3:     { 
   4:         return (DateTime.Today.DayOfYear >= DOB.DayOfYear) 
   5:             ? (DateTime.Today.Year - DOB.Year) : (DateTime.Today.Year - DOB.Year - 1); 
   6:     }
   7: }

It is not a method, but a property that loaded with some logic to calculate the age base on other properties of the model. And it has no concern of presentation layer!!

Contribute to Mercurial in Simple Steps

Mercurial

Mercurial is a distributed source control management tool / system that allow developers around the globe to collaborate source codes. Recently engaged in an ecommerce project that utilize nopCommerce framework, which is an open source project. I found a little bug in there and made my very first contribution in open source. The nopCommerce is hosted on www.codeplex.com

Simple Steps

  1. Logon to CodePlex.
  2. Create a fork for the project
  3. Clone the fork
  4. Make local modifications.
  5. Check-in and commit to the local repository.
  6. Push changes from local fork to the remote server. In my case, it is https://hg.codeplex.com/forks/swon/austposthandlingchargefix
  7. After all changes are completed and checked. Create a pull request to merge the fork back to the project.

SVN Convention

Disregard the differences of the underlying technology how distributed source repository and non-distributed (classic?) repository works, a fork is basically a branch in SVN. Once you branch off, you can make local modifications that you do not conflict with the trunk (default). When you are done with the fork, you are then required to merge back and is done by the "pull request". In an open source environment, there will be some core developers of the project with higher access that will merge the changes.

Bucket name does matter on AWS S3

Scope


As some of you might know I have a second career as a photographer, and it is just natural for me to write my own website (Bootstrapped indeed!!). Currently having some issues with the existing web hosting so I began to migrate my site to be hosted in Amazon S3.

Migration Began


Went through the Web UI interface on AWS portal to upload files, set permission and enabled Static Website Hosting. All nice and sweet, had some issues to apply all public read policy to the bucket so I don't have to change permission per file basis. Once again, it wasn't difficult to find out how when we are in that era of information technology, a bit of search and I got the policy example up and running.

For those who curious here the code I have used. Version date cannot be changed or it will throw error. Why 2008???

   1: {

   2: "Version":"2008-10-17",

   3: "Id":"http referer policy example",

   4: "Statement":[

   5: {

   6: "Sid":"readonly policy",

   7: "Effect":"Allow",

   8: "Principal":"*",

   9: "Action":"s3:GetObject",

  10: "Resource":"arn:aws:s3:::mybucket/*"

  11: }

  12: ]

  13: }


What really got me and wasn't so obvious to me is that the bucket name is directly related to the domain I am going to use for CNAME redirect. What does it mean? Let's have a look.

At first I setup the bucket as "swphotography" as below.

swphotography

After I change my CNAME record in my DNS register, I get the error below.

error

With my given endpoint from s3, the site works fine without 404 error.

The error message was actually quite useful, especially the 2nd and 3rd lines helped me to resolve the problem.

The 2nd line is basically saying "bucket not found", and the 3rd line is saying BucketName: www.sunnyw.net.

Bucket Name Matters!!


Hello S3, I didn't setup the bucket name as www.sunnyw.net, it was swphotography! It had me thinking maybe the bucket name is related to the url, and maybe it uses it as a way to do bucket look up.

sunnyw.net

Copy the files over from the swphotography bucket to the www.sunnyw.net bucket, my site is up and running again!

Conclusion


It was a bit of effort but pretty happy with the result. As my usual whinging thing, perhaps they can provide a wizard tool for people who want to use CDN for static website hosting? It is not an uncommon strategy to host simple content sites and you may find articles about it on the internet easily. Extra bedside reading anyone?

CyberSource - The Magic Line(s) for Authorize and Capture

Scope


Working on integration with the CyberSource payment gateway that does direct payment (non-hosted option) on our online shop. Using the latest API version 1.93 as of today the article is written, and I followed their instructions and created a SOAP based WCF to connect to their Test Business Center (sandbox).

Problem


I followed copied their sample code, fiddle around to get it compile and submit the transaction with no error.

However, when I login to the test center, I only get authorize but not capture.

auth

(Authorize and Capture is explained in the CyberSource KB.)

Try to search around and finally find this pdf file. On page 176, it clearly explained on how to "request a sale". However, don't get so excited yet, it doesn't work if you just copy and paste the code. That's because they mix the documentations between their old Simple Order API Call and the SOAP API Call.

The 2 magic lines you need to get capture working (and authorize at the same time, essentially we are doing sale) as follow.

magic

Run the code and back to our sandbox, it is now trying to authorize and capture in the same transaction. Notice the green means successful, the red authorization in 2nd row stopped the transaction to proceed for capture, therefore it is neither red nor green color.

sale

Conclusion


It was a no brainer to work out the magic line(s) for the problem if you read the sample code carefully. I only wishful thinking that they would have put these 2 magic lines in the given sample code but I had my fun on a bit of code hunting and reading documentations today. Surely a better understanding of the CyberSource Payment Gateway.

nopCommerce Customization with Controller Inheritance

Scope


The following article is based on the latest platform of nopCommerce as of today, version 3.1.

Scenario


Trying to customize the view by adding some extra custom text, the goal is ideally we will not change the existing controller in the nopCommerce platform to allow easier future upgrade.

Normally, we will create a widget plugin to do that, nice and neat that plugin is isolated from the page controller and allow easy plug and play. This would work fine for most people.

However, in certain scenarios, if the works required to be done in the controller is more complicated like introducing new services, security control etc, then we will need to modify the controller.

The gotcha is we don't want to change the existing controller for future upgrade, with IoC and OO architecture, one would think it is easy just to create a new controller to inherit from the existing controller for custom works, but life is not that easy.

Customization


CoreController

In CustomerController, we need to set the Register() as virtual. And that is only change required in our core controller, so that you can override its functionalities.

VirtualRegister


CustomController

Let's take CustomerController as an example and I want to override the Register().

I created my own CustomerController file inside a folder called Custom.

 CustomerController in Explorer

I gave it a namespace of Nop.Web.Controllers.Custom, and inherit from the Nop.Web.Controllers.CustomController.

controller

You will need to set your constructor same as the parent controller, and use the keyword base to inherit from the base controller, pretty standard C# inheritance syntax.

constructor

You may override the Register() like the following. In this example, we changed the value of the ViewBag.Title only, but as you can imagine, you can initialize new objects or call other services, all be achieved.

OverrideRegister

RouteProvider

RouteProvider

In the Nop.Web project, Infrastructure\RouteProvider.cs, we need to map the controller to a different namespace, so our custom controller will be used in place of the parent one.

RouteProvider Custom

View

For demonstration purpose, I updated the view as follow, but a more professional way by using theme can be found here.

view

Result

Compile the solution and head to register page, it should look like this.

result


IoC

What about IoC? How will the CustomController get instantiated? That is already handled by the Nop.Web.Framework.DependencyRegistrar that will register all controllers in the assembly.

Conclusion


As demonstrated, it is not horribly difficult to do customizations in nopCommerce, I only wish it could be easier with the RouteProvider part where I can instead just use the IoC to change the constructor only, then the Visual Studio would be smart enough to pick up the custom controller during compilation.

nopCommerce Plugin - The partial view was not found

In an attempt of creating a new plugin, we have the following standard nopCommerce views and a "shared" view.

solutionexplorer

When dealing with view, I ran into an error like below where it cannot find the view for the plugin.

viewerror

In visual studio, view created by default with Build Action set to Content. This needs to be changed to Embedded Resource.

viewresource

The partial view also needed to be referenced by giving the fully qualified name space in the calling view.

FQ

How to write a nopCommerce plugin - Hello World

Scope

This is an article to discuss and may resolve the "does not implement IController" error. It gives a walk thru on how to write a NopCommerce plugin with the NopCommerce 3.1 framework.

Introduction

If you are new to NopCommerce and about to write a NopCommerce plugin, the likelihood is that you might have read this article already.

If you are then lucky enough to get the infamous "does not implement IController" error, with a bit of google, you would have landed and read this article.

1

If you are then still having the same error, unable to resolve the problem even followed all the suggested solutions by others like "pad a space to global.asax" etc, then the rest of the article is for you.

Project Setup

In this example, it is a quick walk thru on how to build  a not so simple Hello World widget plugin.

If you read the above how-to article, they suggested you to start a new class library project, but I found copying an existing project will save you from the mysterious "does not implement IController" error.

To begin, I made a copy of the Nop.Plugin.Widgets.NivoSlider plugin that is part of the framework and gave it a new name.

Things that I then need to modify are as follow:-

Project Properties

Change the output path and file name.

2

3

Properties\AssemblyInfo.cs

Fairly straight forward. And for the sake of completeness, I generated a new guid and replaced the one I copied (although I am not going to expose the dll to COM)

References folder

The good thing about copying an existing project is I can leave this untouched.

Description.txt

Fairly straight forward.

HelloWorldPlugin.cs

We will return "hello_world" in the GetWidgetZones(). This name will match the widget in the view that use this plugin later on, so the widget will be rendered in the desired place. The rest is pretty self explained from the comments.

widgetzone

HelloWorldSettings.cs


We will have 2 fields, to store greeting and name.

code

Controller

Following the same naming convention, we will call it WidgetsHelloWorldController.

In the constructor, we only need ISettingService, IStoreContext, IStoreService, IWorkContext.

controller constructor


The 3 main actions in this controller are Configure(), Configure(configurationModel) and PublicInfo(widgetZone).

Configure is for the configuration page in the admin; PublicInfo is the actual rendering of the plugin in the front end.

Fairly similar to the NivoSlider code so I won't go into details.

Configure

Configure(Model)

PublicInfo

Models

Similarly,

ConfModel

PubModel


Make sure the namespace is correct or it will throw runtime error.

Views

The Configure.cshtml view is quite a straight forward exercise of find and replace / code chopping.

The PublicInfo.cshtml view will display the message.

publicview

Hello World

We are now nearly there to greet the world.

Admin

Go thru the process and install the plugin, then enable it.

install

Click on the configure button and depends on your multi-store setup, you might get this.

admin conf


Values can be stored site-wide or store specific, I will leave the fun parts for you to explore.

Front End

For demonstration purpose, we need to inject this widget / plugin into an view so it can be rendered in the front end. In this example, I picked the cart.cshtml view, you can find this in the Views\ShoppingCart as part of the NopCommerce platform.

We will add a line to indicate where we want the widget to go in the view.

cart


And it should look like this.

hi

Conclusion

The way how plugins are created were not so developer-friendly. It takes a lot of copy and paste works and some of the error messages are not so friendly neither. Nevertheless, it is a nice platform that allow you plug and play different plugins, install / uninstall / enable / disable. The multi-store ability with the plugin greatly helped reduce development times where in some platforms you maybe hacking your way until you can't hack further. I am looking forward to see what NopCommerce will evolve into in the future.