European ASP.NET MVC 4 and MVC 5 Hosting

BLOG about ASP.NET MVC 3, ASP.NET MVC 4, and ASP.NET MVC 5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET MVC 4 Hosting - Amsterdam :: Using MVC 4 to Run Sitecore

clock July 17, 2013 11:19 by author Scott

ASP.NET MVC is everywhere, and if you judge from the the job adverts ASP.NET Web Forms is rapidly being consigned to the “legacy” category. So where does that leave developers like me, currently using Sitecore?
Although it seems to me that Sitecore is Web Forms to it’s back teeth, there are nevertheless some signs that it too is going MVC.

From Sitecore version 6.6 there is support for MVC straight out of the tin (or the installer, at least). This is great but how do you get from that install to not just an MVC project, but MVC 4 (.NET 4.5) using Visual Studio 2012?
The steps are available around and about, but this post seeks to update the process to the latest version of Sitecore and pull it all together.

If you are really new to Sitecore MVC, probably the best place to start is here.

1. Create a Visual Studio Project from the install

a.) Using the latest version of Sitecore (Sitecore 6.6.0 rev. 130214), create a new instance
b.) Create a new Empty Website with Visual Studio 2012 using .NET 4 (but NOT MVC)
c.) Copy the Sitecore instance into your Empty Project Directory, and include the files (and Assembly References) into it. I found that the best way to get the Assemblies in was to copy them to a third directory (not the bin directory), then browse to that and Visual Studio will add them, nice and cleanly.
d.) Take the global.asax from the Sitecore Instance, pull out the inline code and paste it into a new global.asax.cs
e.) Publish the Site

2. Make the project MVC3

a.) Unload the project, then right-mouse click to edit the project file. In the project file you will find an element

<ProjectTypeGuids>

add this GUID to it at the front of the list, separated by a semi-colon:
{E53F8FEA-EAE0-44A6-8774-FFD645390401};

b.) Reload the project you now have an MVC3 Sitecore project.

c.) You should now confirm that MVC3 is working:

(this taken from John West’s blog)

Create a the subdirectory /Views containing a nested subdirectory named Sitecore.
Create the /Views/Sitecore/index.cshtml file containing some Razor code (note that the @inherits directive is not necessary if the /Views subdirectory contains the proper web.config file, such as that installed when you create an MVC project in Visual Studio):

@inherits System.Web.Mvc.WebViewPage
@{
  Layout = null;
}
<html>
  <body>
  <h4>Index.cshtml</h4>
  <div>Model:
@{
  if (Model == null)
  {
<text>null</text>
  }
  else
  {
@Model.GetType();
  }
}
    </div>
  </body>
</html>

In the Sitecore Content Editor user interface, navigate to the /sitecore/layout/Layouts/Sample Layout item that defines the default layout.
Click the Content Tab.

In the Path field, enter /Views/Sitecore/index.cshtml, and save.

Either publish the layout definition item and view the home page of the published site (http://playground – not http://playground/default.aspx), or use the Page Editor. You should see content such as the following:

Index.cshtml

Model: Sitecore.Mvc.Presentation.RenderingModel

3. Make the Project MVC 4

a.) In the project Website Properties change the Target Framework to .NET 4.5

b.) Update the references from MVC3 to MVC4

all references in .config files need to reflect these versions:
System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″
System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
System.Web.Helpers, Version=2.0.0.0, Culture=neutral, publicKeyToken=”31bf3856ad364e35″
System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35

these appSettings need to be added to the web.config:

<appSettings>
  <add key="webpages:Version" value="2.0.0.0" />
  <add key="PreserveLoginUrl" value="true" />
</appSettings>

further details here:

c.) In Solution Explorer, right-click on the References and select Manage NuGet Packages. In the left pane, select Online\NuGet official package source, then update the following:

ASP.NET MVC 4
(Optional) jQuery, jQuery Validation and jQuery UI
(Optional) Entity Framework
(Optonal) Modernizr

d.) Update ProjectTypeGuids element and replace {E53F8FEA-EAE0-44A6-8774-FFD645390401} with {E3E379DF-F4C6-4180-9B81-6769533ABE47}.

e.) If the project references any third-party libraries that are compiled using previous versions of ASP.NET MVC, open the root Web.config file and add the following three bindingRedirect elements under the configuration section:

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers"
             publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc"
             publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="4.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages"
             publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

f.) Finally, there is an incompatability with the Sitecore login page under .NET 4.5, to do with the use of an iframe. The file sitecore/login/default.aspx contains an iframe to the SDN that .NET 4.5 doesn’t like. Replace it with a div:

<iframe id="SDN">
  <div id="StartPage" runat="server" allowtransparency="true"
 frameborder="0" scrolling="auto" marginheight="0" marginwidth="0"
 style="display: none"></div>
</iframe> 

<div id="SDN">
  <div id="StartPage" runat="server" allowtransparency="true"
 frameborder="0" scrolling="auto" marginheight="0" marginwidth="0"
 style="display: none"></div>
</div>

and you’re off to the races. More detail here.

I should point out, of course that .NET 4.5 is not supported as yet by Sitecore, but with this you can play with 4.5… until Sitecore 7 arrives.

Good luck.

 



European ASP.NET MVC 4 Hosting - Amsterdam :: Enhanced Default Template MVC 4

clock July 5, 2013 07:10 by author Scott

MVC 4 introduces many new features. In today post, I will talk about MVC 4 and enhanced default templates.

When you create a new project, the default website looks quite different from previous versions.  Besides the improved aesthetics, the default website has improved functionality thanks to a technique called adaptive rendering.

Adaptive rendering is where the page is rendered specific to the browser without any customization.  This is an absolute must for most developers today; write once, run everywhere.  The adaptive rendering is made possible thanks to a new CSS type -> media queries

@media all and (min-width: 640px) { #media-queries-1 { background-color: #0f0; } }
@media screen and (max-width: 2000px) { #media-queries-2 { background-color: #0f0; } }

After you creating a new website, you’ll be able to see the improvements in the default layout.

To see the adaptive rendering in action, open the page using a mobile device.

Here’s how the page looks on an iPhone.

And here’s how the page look on an iPad.

The page renders differently depending on the size of the screen.  Making the page do that without media queries is tricky.  Using media queries makes it simple.

One final thing to note is the default template takes advantage of jQuery UI also.  The Login and Register links show you how to use this JavaScript library to  provide a richer UI.  You can read all about the other features that are available to you with jQuery UI here.

Testing with Emulators

The best way I found to test what the site will look like is with FireFox and User Agent Switcher add-on,
which changes the user agent that’s sent to the browser.  That can be downloaded
here.



European ASP.NET MVC 4 Hosting - Amsterdam :: How to Fix CSS Problem in ASP.NET MVC 4

clock July 1, 2013 12:10 by author Scott

Some of you will face this problem when using Bundling and Minification in ASP.NET MVC 4. This post cover about how to fix relative CSS Path in ASP.NET MVC 4.

Recently, I hit a known problem with deploying to IIS virtual directories. It’s not a problem for ASP.NET which understands virtual directories and so if you ask for “~” or “/” will return “yoursite/virtualfolder”. However, JavaScript is run under IIS, which doesn’t understand this idea so well. Do “/” in JS and you’ll get back “yoursite” NOT “yoursite/virtualfolder”.

So what’s the fix? Well, for JavaScript there are a couple of answers. So I check my javascript. Based on this answer, I came up with this:

1. Add a hidden field to a masterpage:

  @Html.Hidden("HiddenCurrentUrl",  Url.Content("~"))

2. As one of the first things you do, ensure this JS runs. All it does it take the value in the field and store it:

var baseUrl = "";
baseUrl = $("#HiddenCurrentUrl").val();


3. Use baseUrl wherever you need to call things in JavaScript:

Silverlight.createObject(
            baseUrl + "ClientBin/SilverlightBridge.xap",  // source
            ...
        );

Hmm… But you know, that is not the problem. The problem is with the CSS file

    .link-button.cancel {
        background-image: url('../Images/appbar.cancel.darkgrey.png');
    }

A similar problem occures where these URLs start with a slash(/) because IIS interprets that incorrectly. You can’t invoke ASP.NET, which does know the right root into a CSS file

After some messing around, I came to this compromise, which works well. It also exposes some of the hidden power of using Bundling:

  1. For any images you reference in CSS, move them into a relative folder, such as /images near the CSS. I know, this may be an unacceptable compromise for you. But actually, it makes a lot of sense. The image is probably only used by the CSS so it makes sense to have it nearby, not in some global /images folder.  
  2. In order to make this work, you need to get your CSS (which you’ve bundled up) into the same place relative to the images you’re reference, or at least, you need to cheat the browser into thinking this happening.
  3. So, for instance:

      bundles.Add(new StyleBundle("~/Content/DataTableStyle").Include(


    comes out the other end as:

Don’t forget that all your “static” content, such as images won’t be affected by Bundling and can be referenced using the folder structure you expect, i.e. the one you’ve set up in Visual Studio.

So… when you create the Style Bundle you give it a name which reflects where you have it in your Visual Studio folder structure, then it will pop out the other end in the same place, relatively, to the image files which it’s references! And because the Bundling is happening via ASP.NET, the URL of the CSS file, and wherever it is referenced, works fine in virtual directories!



European ASP.NET MVC 4 Hosting - Amsterdam :: What is PRG and Using PRG on MVC 4

clock June 18, 2013 06:55 by author Scott

Hellooo….. Today post I will talk about what is PRG in ASP.NET MVC 4 and how to use it. Before we start, the question is what is PRG?

What is the PRG Pattern ?

  • PRG stands for "Post/Redirect/Get"
  • Instead of returning an HTML page directly,
  • The POST operation returns a redirection command (using the HTTP 303 response code (sometimes 302)
  • together with the HTTP "Location" response header),
  • Instructing the browser to load a different page using an HTTP GET request
  • The result page can then safely be bookmarked or reloaded without unexpected side effects

How to Explain this by using an Example ?

  • Here I'm going to use User Registration function as an example
  • If the Registration attempt is Successful, the user should be redirected to his Home page
  • Otherwise they should be redirected back to the Registration page

Image 1 : Shows Action Methods Interaction When do Registration

AccountController.cs        

Code for the Register Action Method (Get) for Displaying Register View is as below

        /// <summary>
        /// for displaying Register View
        /// </summary>
        [HttpGet]
        [AllowAnonymous]
        public ActionResult Register()
        {
            return View();
              }

Image 2 : Register View

Code for the Register Action Method (Post) for Processing the Registration and Shows Register Page again is as below

        /// <summary>
        /// for Registering the User
        /// </summary>
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
          if (ModelState.IsValid)
          {
            // Attempt to register the user
            try
             {
               WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
               WebSecurity.Login(model.UserName, model.Password);
               ViewBag.Message = "Successfully Registered!";
               return View(model);//PRG has not been Maintained
             }
              catch (MembershipCreateUserException e)
             {
                 ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
             }
          }
            // If we got this far, something failed, redisplay form
            return View(model);
        }

Image 3 : New user has been Created



- At this point If you try to Refresh (F5) or Reload the Page,You'll see below mentioned kind of Security Alert Message Box

Image 4 : Register form with "Confirm Form Resubmission" Message Box



- After that If you try to press "Continue" Button, You'll see below mentioned Run time exception
- But for another situation this may be Data Duplication issue etc.

Image 5 : Run time Exception

How to Get Rid Of this Issue ?

  • You have to maintain PRG Pattern with your Return type, After finishing the Successful Registration
  • To properly perform PRG you must return a redirecting ViewResult from your Action
  • Such as RedirectToAction, otherwise you'll get the dialog box pictured above (i.e. Image 4)

Code for the Properly Perform PRG Pattern is Maintaining Register Action Method is as below

    /// <summary>
    /// for Registering the User
    /// </summary>
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
     {
      if (ModelState.IsValid)
      {
       // Attempt to register the user
       try
       {
         WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
         WebSecurity.Login(model.UserName, model.Password);
         ViewBag.Message = "Successfully Registered!";
         return RedirectToAction("Index""Home");//PRG has been Maintained
        }
        catch (MembershipCreateUserException e)
        {
          ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
      }
       // If we got this far, something failed, redisplay form
       return View(model);
     }

Image 6 : Home page

DONE!!



European ASP.NET MVC 4 Hosting - Amsterdam :: Revisiting IBundleTransform ASP.NET 4.5 and MVC 4

clock May 13, 2013 10:26 by author Scott

Web optimization frameworks include two defaults transform type JsMinify and CssMinify which is used by ScriptBundle and StyleBundle respectively. However we can create our own custom transform type to processe references as per our need. To create custom transform type, we need to create class which implements IBundleTransform interface.

IBundleTransform interface define a method named Process which process bundle response. In developer preview version, Process method had only one parameter of type BundleResponse, however onwards RC release, Process method introduced one more parameter of type BundleContext. In this post, we will see how we can utilize this additional parameter while creating our custom transform type.

BundleContext

As name suggest, with BundleContext, we can get information about bundles which could include existing bundle information, bundle url, HTTP context for bundle, etc. Following is the list of all property of BundleContext.

- BundleContext.BundleCollection : We can get collection of all bundles including default and custom bundle in application through this property.

- BundleContext.BundleVirtualPath : This property expose virtual bundle url i.e. ~/bundles/MyBundle.

- BundleContext.HttpContext : This property is type of HttpContextBase, and we can have access of HTTP context through this property. This is very much useful property when we are creating transform type which generate dynamic response. For e.g. we can access query string parameter passed to bundle url (~/bundles/MyBundle?id=123) through this property (context.HttpContext.Request.QueryString["id"]) and we can use it to create dynamic bundle response.

- BundleContext.UseServerCache : Default value of this property is true. It means only first request to bundle url will be intercepted by transform types and once response is generated it will be stored in server cache and further request to bundle url will be served from server cache without processing it. This will help to reduce bundle processing time and to increase performance. If we set BundleContext.UseServerCache to false then all request will be processed by transform type this is only necessary when bundle url are generating dynamic response. See detailed walkthrough later in this post showing how to use this property in accordance with BundleResponse.Cacheability.

- BundleContext.EnableInstrumentation : Default value of this property is false. This is used for tracing and analysis purpose. We can check value of this property and can write tracing code accordingly. We can also set true to this property to enable instrumentation for further lifecycle of Web optimization frameworks for current bundle request.

BundleResponse

BundleResponse is used to retrieve list of files included in bundle so we can process it and generate response for bundle. As BundleResponse is used to generate response of bundles, it needs to take care of two primary properties of generated response. One is response content type and another one is HTTP Cache-Control header. So BundleResponse also expose properties for the same. Following is the list of all properties in BundleResponse class.

- BundleResponse.Files : This is IEnumerable collection of files which is included in bundle. We can iterate through this collection and process file content to generate bundle response.

- BundleResponse.ContentType : Through this property, we can set content type for bundle so that browser can render it appropriately. Default content type "text/html".

- BundleResponse.Cacheability : We can use this property to set Cache-Control HTTP header of bundled response. Default value of this property is Public.

- BundleResponse.Content : Anything which we set as a value of this property, that content will be sent back to browser as a response of bundle.

Following is the complete code which shows how to create custom transform type and how we can use it with bundling.

public class CustomTransformType : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        string strBundleResponse = string.Empty;
        foreach (FileInfo file in response.Files)
        {
            // PROCESS FILE CONTENT
        }
        response.Content = strBundleResponse;
    }


Bundle myBundle = new Bundle("~/bundles/MyBundle", new CustomTransformType());
myBundle.Include("~/path/to/file");
bundles.Add(myBundle);

Bundle and truly dynamic response

As we noted earlier, we can set BundleContext.UseServerCache to false in order to process all bundle request and generate dynamic response. Let try to simulate this by small walkthrough and see it works or we need to take care any additional parameter.

public void Process(BundleContext context, BundleResponse response)

{
    context.UseServerCache = false;
    response.Content = DateTime.Now.ToString();
}


We are returning current date time with UseServerCache set to false. Now try to hit bundle url multiple times by pressing F5. Oops… it seems it has processed bundle response only first time. Let dig more into this, open another browser and hit same url… ahmm it seems it has processed bundle response one more time… again press F5 multiple times…bad luck

As we can see, it seems (read again it seems) it is processing bundle response only first time for separate client (is it really? nop). Nop this is not the case. In fact this is how client deals with it due to HTTP cache control header. Confused? See response header of bundle url to get more information.

As we noted earlier default value of BundleResponse.Cacheability is Public. So even if we have set BundleContext.UseServerCache to false then also due to Expires response header and Public Cache-Control header client is not sending request back to server. So in this case we need to also set BundleResponse.Cacheability to NoCache. We can also set it to Private but in some client we need to press Ctrl + F5 to refresh bundle response.

public void Process(BundleContext context, BundleResponse response)
{
    context.UseServerCache = false;
    response.Cacheability = HttpCacheability.NoCache;
    response.Content = DateTime.Now.ToString();
}

After setting BundleResponse.Cacheability to NoCache try to refresh bundle url again now it is re generating bundle response on each request.



European ASP.NET MVC 4 Hosting - Amsterdam :: How to Upload a file in MVC4 C#5 .NET 4.5

clock May 3, 2013 06:33 by author Scott

One of the features of this so called killer app will be to upload pictures (nothing special I agree). But how would I do this for all the clients I hope to support (WinRT/WP7/Html5/IOS).

Let me first present the server that will be used for all these clients, I’ll then follow up with what I consider to be the simplest client a html5 browser!

Server

So I fired up VS11 and created a new MVC4 application using .net 4.5 / C#  and the WebApi template.

I then added a controller called FileUploadController.cs

   1:  using System.Collections.Generic;
   2:  using System.Linq;
   3:  using System.Net;
   4:  using System.Net.Http;
   5:  using System.Threading.Tasks;
   6:  using System.Web.Http;
   7:   
   8:  namespace MvcApplication16.Controllers
   9:  {
  10:      public class FileUploadController : ApiController
  11:      {
  12:          public async Task<IEnumerable<string>> PostMultipartStream()
  13:          {
  14:              // Check we're uploading a file
  15:              if (!Request.Content.IsMimeMultipartContent("form-data"))           
  16:                  throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
  17:                 
  18:              // Create the stream provider, and tell it sort files in my c:\temp\uploads folder
  19:              var provider = new MultipartFormDataStreamProvider("c:\\temp\\uploads");
  20:   
  21:              // Read using the stream
  22:              var bodyparts = await Request.Content.ReadAsMultipartAsync(provider);           
  23:          
  24:              // Create response.
  25:              return provider.BodyPartFileNames.Select(kv => kv.Value);           
  26:          }
  27:      }
  28:      
  29:  }

You can see from line 12 that I’ve made this operation async, you’ve really got to admire the simplicity of async/await construct in .net 4.5! In line 22 you can see that the compiler and some state machine magic allow the freeing up of the asp worker thread…..

HTML5 Client

The client couldn’t have been easier, fist a look at it in the browser

   7:      <meta name="viewport" content="width=device-width" />
   8:  </head>
   9:  <body>
  10:      @using (Html.BeginForm("FileUpload", "api", FormMethod.Post, new { enctype = "multipart/form-data" }))
  11:      {
  12:          <div>Please select some files</div>
  13:          <input name="data" type="file" multiple>
  14:          <input type="submit" />           
  15:      }
  16:  </body>
  17:  </html>

The important part above is using the enctype attribute, in fact line 10 loosely translates to

<form action="~/api/FileUpload" enctype="multipart/form-data" method="POST">

Don’t believe me? Then try VS11’s awesome new feature – page inspector

Right click on the html and choose view in page inspector

and we’re done! Of course in the real world we’ll use ajax with a few trick re sandbox, but here’s the response in the browser with xml.

I’ll hopefully follow up with the samples for the client list below when I get to the respective development machines.

 



European ASP.NET MVC 4 Hosting - Amsterdam :: Using MySql 5 as Membership Backend for ASP.NET 4.5 MVC 4 Application

clock May 1, 2013 09:18 by author Scott

Oracle provide MySql ASP.Net web providers through NuGet – search for MySql.Web. Using the MySql providers you can easily use MySql as your membership/profile/role backend for an ASP.Net application.

I am going to demonstrate using it as the membership provider for an MVC 4 application using Razor but the steps are almost identical for the ASPX views.

Web.config for MySql Membership Provider

Use the following configuration in your web.config file:

1              <membership defaultProvider="MySqlMembershipProvider">
2              <providers>
3              <clear />
4              <add name="MySqlMembershipProvider" type="MySql.Web.Security.MySQLMembershipProvider, MySql.Web, Version=6.5.4.0, PublicKeyToken=c5687fc88969c44d"
5              autogenerateschema="true"
6              connectionStringName="*NAME_OF_YOUR_CONN_STRING*"
7              enablePasswordRetrieval="false"
8              enablePasswordReset="true"
9              requiresQuestionAndAnswer="false"
10           requiresUniqueEmail="false"
11           passwordFormat="Hashed"
12           maxInvalidPasswordAttempts="5"
13           minRequiredPasswordLength="6"
14           minRequiredNonalphanumericCharacters="0"
15           passwordAttemptWindow="10"
16           passwordStrengthRegularExpression=""
17           applicationName="/" />
18           </providers>
19           </membership>

Obviously you also need to configure a valid MySql connection string, create a MySql schema, change any other security settings you want and put the name of your connection string into the provider configuration but your on your own doing that.

Create Tables

Once you have the initial setup done open the ASP.Net configuration website (VS2012: Project menu -> ASP.NET Configuration – at the bottom of the menu) and create a user in the security tab. Doing this will create the database structure and a new user providing you have set the configuration up correctly.

Unbreak the MVC 4 AccountController

This is a simple fix for getting MySql up and running. Basically we just rollback the AccountController to use the old style MembershipProvider which is supported by the MySql MembershipProvider.

  1. Delete the MVC 4 AccountController, AccountModels, Account view folder and _LoginPartial shared view
  2. Create a new MVC 3 web application
  3. Copy the MVC 3 AccountController, AccountModels, Account view folder and _LogOnPartial shared view into your MVC 4 application
  4. Replace @Html.Partial(“_LoginPartial”) in the shared _Layout view with @Html.Partial(“_LogOnPartial”)

This won’t support OAuth authentication but will get your MySql Membership provider up and running with ASP.Net 4.5 and MVC 4.

Raw SQL for MySql Membership Tables

If you want to directly create the membership data structure without using the ASP.NET configuration tools use the following SQL as a base and modify SCHEMA_NAME and collation to be what you require.

1              -- -----------------------------------------------------
2              -- Table `SCHEMA_NAME`.`my_aspnet_applications`
3              -- -----------------------------------------------------
4              CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_applications` (
5                `id` INT(11) NOT NULL AUTO_INCREMENT ,
6                `name` VARCHAR(256) NULL DEFAULT NULL ,
7                `description` VARCHAR(256) NULL DEFAULT NULL ,
8                PRIMARY KEY (`id`) )
9              ENGINE = InnoDB
10           AUTO_INCREMENT = 2
11           DEFAULT CHARACTER SET = latin1
12           COLLATE = latin1_swedish_ci;
13           -- -----------------------------------------------------
14           -- Table `SCHEMA_NAME`.`my_aspnet_membership`
15           -- -----------------------------------------------------
16           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_membership` (
17             `userId` INT(11) NOT NULL DEFAULT '0' ,
18             `Email` VARCHAR(128) NULL DEFAULT NULL ,
19             `Comment` VARCHAR(255) NULL DEFAULT NULL ,
20             `Password` VARCHAR(128) NOT NULL ,
21             `PasswordKey` CHAR(32) NULL DEFAULT NULL ,
22             `PasswordFormat` TINYINT(4) NULL DEFAULT NULL ,
23             `PasswordQuestion` VARCHAR(255) NULL DEFAULT NULL ,
24             `PasswordAnswer` VARCHAR(255) NULL DEFAULT NULL ,
25             `IsApproved` TINYINT(1) NULL DEFAULT NULL ,
26             `LastActivityDate` DATETIME NULL DEFAULT NULL ,
27             `LastLoginDate` DATETIME NULL DEFAULT NULL ,
28             `LastPasswordChangedDate` DATETIME NULL DEFAULT NULL ,
29             `CreationDate` DATETIME NULL DEFAULT NULL ,
30             `IsLockedOut` TINYINT(1) NULL DEFAULT NULL ,
31             `LastLockedOutDate` DATETIME NULL DEFAULT NULL ,
32             `FailedPasswordAttemptCount` INT(10) UNSIGNED NULL DEFAULT NULL ,
33             `FailedPasswordAttemptWindowStart` DATETIME NULL DEFAULT NULL ,
34             `FailedPasswordAnswerAttemptCount` INT(10) UNSIGNED NULL DEFAULT NULL ,
35             `FailedPasswordAnswerAttemptWindowStart` DATETIME NULL DEFAULT NULL ,
36             PRIMARY KEY (`userId`) )
37           ENGINE = InnoDB
38           DEFAULT CHARACTER SET = latin1
39           COLLATE = latin1_swedish_ci
40           COMMENT = '2';
41           -- -----------------------------------------------------
42           -- Table `SCHEMA_NAME`.`my_aspnet_profiles`
43           -- -----------------------------------------------------
44           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_profiles` (
45             `userId` INT(11) NOT NULL ,
46             `valueindex` LONGTEXT NULL DEFAULT NULL ,
47             `stringdata` LONGTEXT NULL DEFAULT NULL ,
48             `binarydata` LONGBLOB NULL DEFAULT NULL ,
49             `lastUpdatedDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,
50             PRIMARY KEY (`userId`) )
51           ENGINE = InnoDB
52           DEFAULT CHARACTER SET = latin1
53           COLLATE = latin1_swedish_ci;
54           -- -----------------------------------------------------
55           -- Table `SCHEMA_NAME`.`my_aspnet_roles`
56           -- -----------------------------------------------------
57           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_roles` (
58             `id` INT(11) NOT NULL AUTO_INCREMENT ,
59             `applicationId` INT(11) NOT NULL ,
60            `name` VARCHAR(255) NOT NULL ,
61             PRIMARY KEY (`id`) )
62           ENGINE = InnoDB
63           DEFAULT CHARACTER SET = latin1
64           COLLATE = latin1_swedish_ci
65           ROW_FORMAT = DYNAMIC;
66           -- -----------------------------------------------------
67           -- Table `SCHEMA_NAME`.`my_aspnet_schemaversion`
68           -- -----------------------------------------------------
69           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_schemaversion` (
70             `version` INT(11) NULL DEFAULT NULL )
71           ENGINE = InnoDB
72           DEFAULT CHARACTER SET = latin1
73           COLLATE = latin1_swedish_ci;
74           -- -----------------------------------------------------
75           -- Table `SCHEMA_NAME`.`my_aspnet_sessioncleanup`
76           -- -----------------------------------------------------
77           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_sessioncleanup` (
78             `LastRun` DATETIME NOT NULL ,
79             `IntervalMinutes` INT(11) NOT NULL )
80           ENGINE = InnoDB
81           DEFAULT CHARACTER SET = latin1
82           COLLATE = latin1_swedish_ci;
83           -- -----------------------------------------------------
84           -- Table `SCHEMA_NAME`.`my_aspnet_sessions`
85           -- -----------------------------------------------------
86           CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_sessions` (
87             `SessionId` VARCHAR(255) NOT NULL ,
88             `ApplicationId` INT(11) NOT NULL ,
89             `Created` DATETIME NOT NULL ,
90             `Expires` DATETIME NOT NULL ,
91            `LockDate` DATETIME NOT NULL ,
92             `LockId` INT(11) NOT NULL ,
93             `Timeout` INT(11) NOT NULL ,
94             `Locked` TINYINT(1) NOT NULL ,
95             `SessionItems` LONGBLOB NULL DEFAULT NULL ,
96             `Flags` INT(11) NOT NULL ,
97             PRIMARY KEY (`SessionId`, `ApplicationId`) )
98           ENGINE = InnoDB
99           DEFAULT CHARACTER SET = latin1
100         COLLATE = latin1_swedish_ci;
101         -- -----------------------------------------------------
102         -- Table `SCHEMA_NAME`.`my_aspnet_users`
103         -- -----------------------------------------------------
104         CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_users` (
105           `id` INT(11) NOT NULL AUTO_INCREMENT ,
106           `applicationId` INT(11) NOT NULL ,
107          `name` VARCHAR(256) NOT NULL ,
108           `isAnonymous` TINYINT(1) NOT NULL DEFAULT '1' ,
109           `lastActivityDate` DATETIME NULL DEFAULT NULL ,
110           PRIMARY KEY (`id`) )
111         ENGINE = InnoDB
112         AUTO_INCREMENT = 2
113         DEFAULT CHARACTER SET = latin1
114         COLLATE = latin1_swedish_ci;
115         -- -----------------------------------------------------
116         -- Table `SCHEMA_NAME`.`my_aspnet_usersinroles`
117         -- -----------------------------------------------------
118         CREATE  TABLE IF NOT EXISTS `SCHEMA_NAME`.`my_aspnet_usersinroles` (
119           `userId` INT(11) NOT NULL DEFAULT '0' ,
120           `roleId` INT(11) NOT NULL DEFAULT '0' ,
121           PRIMARY KEY (`userId`, `roleId`) )
122         ENGINE = InnoDB
123         DEFAULT CHARACTER SET = latin1
124         COLLATE = latin1_swedish_ci
125         ROW_FORMAT = DYNAMIC;

 



European ASP.NET MVC 4 Hosting - Amsterdam :: Error Handling and Logging in MVC4

clock April 2, 2013 10:19 by author Scott

Error handing is the main concern in any application, whether it is web application or desktop application. Usually, we catch the exception and log its details to database or text,xml file and also display a user friendly message to end user in-place of error.

Asp.Net MVC has some bulit-in exception filters. HandleError is the default bulit-in exception filter. Let's see how to use this filter with in your application.

HandleError Attribute

The HandleErrorAttribute filter works only when custom errors are enabled in the Web.config file of your application. You can enable custom errors by adding a customErrors attribute inside the <system.web> node, as shown below:

</system.web>
...
<customErrors mode="On" defaultRedirect="Error.htm"/>
</system.web>

HandleError Attribute can be used to handle error at Action Method level, Controller level and Global level.

HandleError Attribute at Action Method Level

Simply put this attribute to the action method to tell MVC how to react when an exception occurs.

[HandleError(ExceptionType = typeof(System.Data.DataException), View = "DatabaseError")]
public ActionResult Index(int id)
{
 var db = new MyDataContext();
 return View("Index", db.Categories.Single(x => x.Id == id));
}

In the above example when a database exception (System.Data.DataException) occurs during the execution of the Index action, MVC will display the DatabaseError view.

HandleError Attribute at Controller Level

Simply put this attribute to the controller to tell MVC how to react when an exception occurs with in the action methods of this controller.

[HandleError(ExceptionType = typeof(System.Data.DataException), View = "DatabaseError")]
public class HomeController : Controller
{
 /* Controller Actions with HandleError applied to them */
}

In the above example when a database exception (System.Data.DataException) occurs during the execution of the controler's any action methos, MVC will display the DatabaseError view.

HandleError Attribute at Global Level

You can also apply the HandleError Attribute for the entire application by registering it as a global error handler. For registering a global error handler, open the FilterConfig.cs file with in App_Start folder to find the RegisterGlobalFilters method.

By default, ASP.NET MVC template has already registered a global HandleErrorAttribute to the GlobalFilterCollection for your application. Here you can also add your own custom filter to the global filter collection as well.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
 filters.Add(new HandleErrorAttribute
 {
 ExceptionType = typeof(System.Data.DataException),
 View = "DatabaseError"
 }); 

 filters.Add(new HandleErrorAttribute()); //by default added
}

Note

Always remember, by default, global filters are executed in their registered order. so register error filters for specific exception types before any other.

You can also set the order of execution of these filter by giving number values to each. The above registered filters can be re-written as:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
 filters.Add(new HandleErrorAttribute(),2); //by default added
 filters.Add(new HandleErrorAttribute
 {
 ExceptionType = typeof(System.Data.DataException),
 View = "DatabaseError"
 },1);
}

Now, all the filters will be executed in the number sequence.

Limitation of HandleError Attribute

1. It has no support to log the exceptions since it suppressed the error once it is handled.
2. It only catch 500 Http error and doesn't catch other HTTP errors like 404,401 etc.
3. It doesn't catch the errors that occur outside the controllers like errors occurs in model.
4. It returns error view even if error occurred in AJAX calls that is not useful in the client-side. It would be great to return a piece of JSON in AJAX exceptions .

Extending HandleError Filter for logging and handling more errors

You can also make your custom error filter by extending HandleError filter. Let's see how to extend this filter to log the exceptions and return a JSON object for AJAX calls.

public class CustomHandleErrorAttribute : HandleErrorAttribute
{
 public override void OnException(ExceptionContext filterContext)
 {
 if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
 {
 return;
 }

 if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
 {
 return;
 }

 if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
 {
 return;
 }

 // if the request is AJAX return JSON else view.
 if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
 {
 filterContext.Result = new JsonResult
 {
 JsonRequestBehavior = JsonRequestBehavior.AllowGet,
 Data = new
 {
 error = true,
 message = filterContext.Exception.Message
 }
 };
 }
 else
 {
 var controllerName = (string)filterContext.RouteData.Values["controller"];
 var actionName = (string)filterContext.RouteData.Values["action"];
 var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

 filterContext.Result = new ViewResult
 {
 ViewName = View,
 MasterName = Master,
 ViewData = new ViewDataDictionary(model),
 TempData = filterContext.Controller.TempData
 };
 }

 // log the error by using your own method
 LogError(filterContext.Exception.Message, filterContext.Exception);

 filterContext.ExceptionHandled = true;
 filterContext.HttpContext.Response.Clear();
 filterContext.HttpContext.Response.StatusCode = 500;

 filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
 }
}

 



European ASP.NET MVC 4 Hosting - Amsterdam :: Using Dependency Injection with AutoFac in the ASP.NET Web API

clock March 26, 2013 11:07 by author Scott

This post is going to tell you exactly how to use the same in DI container in your MVC Controllers and your Web Api controllers, so you can share the same set of services. Of course after you have seen this, it will be immediately clear how to use different containers in both, if you like to do so. The example will be implemented using the Repository pattern, AutoFac, Entity Framework 5 and the EF powertools.

Setting things up

Fire up Visual Studio 2012 RC and start a new MVC 4 empty project:

Call it anything you like. After Visual Studio is done creating your project layout, we’re going to implement the Repository pattern. In a production application you’ll probably want to split your solution into multiple projects, but for now we’re going to do everything in one. First, make sure you have installed the Entity Framework powertools using the Visual Studio extension manager:

After this, use NuGet to add EF 5.0 support to your MVC project:

If you don’t see the PreRelease version, make sure to set the combobox in the top of the screen to “Include Prerelease”. There is one last thing left to do to complete the setup and that’s adding a DI container to our project. You can of course use anything you like, but I’m going with AutoFac. If you want to find out why you should use AutoFac too you can read this. In short, AutoFac combines a full feature set with great performance, is easy to configure and has great support. You can use NuGet to add AutoFac to your project:

Make sure you get the “MVC 4 RC Integration” package. This will provide you with easy integration and will also install the basic AutoFac DLL’s. That’s it, now we’ve got everything we need (assuming you already have a database).

Creating the repository

Create the following interface:

01           using System;
02           using System.Collections.Generic;
03           using System.Linq;
04           using System.Text;
05           using System.Threading.Tasks;
06          
07           namespace Adventureworks.DAL.Repository
08           {
09               public interface IRepository<in TKey,TEntity>    {
10                   void Add(TEntity entity);
11                   void Delete(TEntity entity);
12                   void Update(TEntity entity);
13                   IEnumerable<TEntity> GetAll();
14                   TEntity GetById(TKey id);
15               }
16           }

Now let’s implement it using EF 5.0 and the powertools. I really like the Code only feature of the new Entity Framework release, totally removing the .edmx file. But until recently you couldn’t reverse engineer code only from an existing database. Luckily the EF powertools fix this for us. Right click your Web Project and go the “Entity Framework” menu and select “Reverse engineer Code first”:

Select the database of your choosing and let the tooling do it’s magical stuff. After all is said and done, you will have Entity classes, a DBContext and a file containing the code for configuring the DbContext. Create a class which implements the IRepository interface like this:

1              using System;
2              using System.Collections.Generic;
3              using System.Data.Entity.Infrastructure;
4              using System.Linq;
5              using System.Text;
6              using System.Threading.Tasks;
7              using Adventureworks.Domain;
8             
9              namespace Adventureworks.DAL.Repository.EntityFramework
10           {
11               public class EntityFrameworkProductRepository : IRepository<int,Product>
12               {
13          
14                   public void Add(Product entity)
15                   {
16                       PerformAction((context) =>
17                           {
18                               context.Product.Add(entity);
19                               context.SaveChanges();
20                           });
21          
22                   }
23          
24                   public void Delete(Product entity)
25                   {
26                       PerformAction((context) =>
27                           {
28                               context.Product.Attach(entity);
29                               context.Product.Remove(entity);
30                               context.SaveChanges();
31                           });
32                   }
33          
34                   public void Update(Product entity)
35                   {
36                       PerformAction((context) =>
37                           {
38                              context.Product.Attach(entity);
39                              context.Entry(entity).State = System.Data.EntityState.Modified;
40                              context.SaveChanges();
41                           });
42                   }
43          
44                   public IEnumerable<Product> GetAll()
45                   {
46                       return Read((context) =>
47                           {
48                               return context.Product.AsNoTracking().ToArray();
49                           });
50          
51                   }
52          
53                   public Product GetById(int id)
54                   {
55                       return Read((context) =>
56                           {
57                               Product p = context.Product.AsNoTracking().SingleOrDefault((pr) => pr.ProductID ==
id);
58                               if (p == null)
59                               {
60                                   throw new ArgumentException("Invalid id: " + id);
61                               }
62                               return p;
63                           });
64                   }
65          
66                   private void PerformAction(Action<AdventureWorks2012Entities> toPerform)
67                   {
68                       using (AdventureWorks2012Entities ents = new AdventureWorks2012Entities())
69                       {
70                           ConfigureDbContext(ents);
71                           toPerform(ents);
72                       }
73                   }
74          
75                   private T Read<T>(Func<AdventureWorks2012Entities, T> toPerform)
76                   {
77                       using (AdventureWorks2012Entities ents = new AdventureWorks2012Entities())
78                       {
79                           ConfigureDbContext(ents);
80                           return toPerform(ents);
81                       }
82                   }
83          
84                   private void ConfigureDbContext(AdventureWorks2012Entities ents)
85                   {
86                       ents.Configuration.AutoDetectChangesEnabled = false;
87                       ents.Configuration.LazyLoadingEnabled = false;
88                       ents.Configuration.ProxyCreationEnabled = false;
89                       ents.Configuration.ValidateOnSaveEnabled = true;
90          
91                   }
92          
93               }
94           }

There are a couple of things going on here. Starting on line 66 I’ve created three helper methods which set up the DbContext correctly and dispose it. These methods are used by calling them and supplying a Lambda which uses the DbContext. Let’s take a look at the GetAll method on line 44. You can see that I don’t use change tracking. Change tracking is something you get as a bonus when using the EF, I like to abstract this away with my Repository implementation. It’s also completely useless in a web application since all state is gone after each request and it has a lot of overhead. “But how do you update if you don’t have any change tracking?” you ask?, well take a look at the Update method on line 34. Just set the whole entity as “Modified” and the EF will perform an update for you.

Creating a Web API Controller

Now let’s create a Web API controller to perform some CRUD functionality:

Implement it like this:

1              using System;
2              using System.Collections.Generic;
3              using System.Linq;
4              using System.Net;
5              using System.Net.Http;
6              using System.Web.Http;
7              using Adventureworks.DAL.Repository;
8              using Adventureworks.Domain;
9             
10           namespace Adventureworks.Web.Controllers
11           {
12               public class ProductController : ApiController
13               {
14          
15                   private IRepository<int, Product> _productRepository;
16          
17                   public ProductController(IRepository<int,Product> repository)
18                   {
19                       _productRepository = repository;
20                   }
21          
22                   public IEnumerable<Product> Get()
23                   {
24                       return _productRepository.GetAll();
25                   }
26          
27                   public Product Get(int id)
28                   {
29                       try
30                       {
31                           return _productRepository.GetById(id);
32                       }
33                       catch (ArgumentException ex)
34                       {
35                           throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound){ Content = new StringContent(ex.Message)});
36                       }
37                   }
38          
39                   // POST api/product
40                   public HttpResponseMessage Post(Product product)
41                   {
42                      ValidateProduct();
43                      _productRepository.Add(product);
44                      return new HttpResponseMessage(HttpStatusCode.Created) { Content = new StringContent(Url.Route("DefaultApi",
45                          new{controller="Product",id=product.ProductID}))};
46                   }
47          
48                   // PUT api/product/5
49                   public HttpResponseMessage Put(Product product)
50                   {
51                       ValidateProduct();
52                       _productRepository.Update(product);
53                       return new HttpResponseMessage(HttpStatusCode.NoContent);
54                   }
55          
56                   // DELETE api/product/5
57                   public HttpResponseMessage Delete(Product product)
58                   {
59                       _productRepository.Delete(product);
60                       return new HttpResponseMessage(HttpStatusCode.NoContent);
61                   }
62          
63                   private void ValidateProduct()
64                   {
65                       if (!ModelState.IsValid)
66                       {
67                           throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
68                       }
69                   }
70               }
71           }

Let’s go to line 65 first. This is a helper method which uses the built in Model Binding feature to validate the incoming product. This means that you can just decorate your Product class with attributes or implement IValidatable object to implement data validation. Keeping up with the spirit of rest, this method will generate a BadRequest statuscode when the incoming product is invalid. Now jump up to the constructor. The controller only works with an IRepository interface to perform the crud functionality, it never knows anything about the Entity Framework. This is the key advantage of DI, as we can now mock the repository and unittest our controller. Now jump to line 42; The Post method. A post in REST means an insert. It’s also in the spirit of REST that you use the HTTP statuscodes to signal what’s going on. When you create new content, you should provide the caller with an url to the new content. Similar to the Post method, you can see that the other methods also use statuscodes to indicate if everything went well or not.

Wiring everything up

Last thing left to do is to configure our container and integrate it with MVC. Here’s my Global.asax:

1              using System;
2              using System.Collections.Generic;
3              using System.Linq;
4              using System.Web;
5              using System.Web.Http;
6              using System.Web.Mvc;
7              using System.Web.Routing;
8              using Adventureworks.DAL.Repository.EntityFramework;
9              using Adventureworks.Web.Services;
10           using Autofac;
11           using Autofac.Integration.Mvc;
12           using Autofac.Integration.WebApi;
13          
14           namespace Adventureworks.Web
15           {
16               // Note: For instructions on enabling IIS6 or IIS7 classic mode,
17               // visit http://go.microsoft.com/?LinkId=9394801
18               public class MvcApplication : System.Web.HttpApplication
19               {
20                   protected void Application_Start()
21                   {
22                       AreaRegistration.RegisterAllAreas();
23          
24                       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
25                       RouteConfig.RegisterRoutes(RouteTable.Routes);
26          
27                       var builder = new ContainerBuilder();
28                       builder.RegisterControllers(typeof(MvcApplication).Assembly);
29                       builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
30                       builder.RegisterType<EntityFrameworkProductRepository>().AsImplementedInterfaces().InstancePerApiRequest().InstancePerHttpRequest();
31                       var container = builder.Build();
32          
33                       DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
34                       GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
35                   }
36               }
37           }

First up are lines 28-32. This is the configuring of the AutoFac container. You register all the controllers for MVC and the Web API with two lines of code. This is done on lines 29 and 30. On line 31 I am registering the EntityFrameworkProductRepository in a per request scope, for MVC controllers and Web API controllers. On line 32 the container is built. On line 35 the container is registered for MVC controllers. On line 36 it’s registered for API controllers. This is what bites people the most. To use DI with MVC, you need a class which implements IDependencyResolver. To use DI with the ASP.NET Web API, you also need a class which implements IDependencyResolver. But these interfaces aren’t the same and they live in different namespaces. The dependency resolvers are also registered differently as you can see on lines 35 and 36. Luckily, AutoFac’s MVC integration package which we installed earlier, contains dependency resolvers for use to use, otherwise we had to implement these ourselves. That’s all! Now go out and test your REST service with your favorite tool.



European ASP.NET MVC 4 Hosting - Amsterdam :: How to Integrate Facebook Login button in ASP.NET MVC 4 application

clock March 15, 2013 07:06 by author Scott

This article demonstrates how to integrate login button on the web page in order to obtain access token that we'll need for further tutorials.

Visual Studio project setup

Firstly, let's get started by opening visual studio and creating new ASP.NET Mvc 4 Web Application. Name it FacebookLoginButton and make sure .NET Framework 4 is selected. Click on OK. Another window should now pop up asking for a type of tempalte you'd like to install in your app. Select An Empty ASP.NET MVC Project.

Once you've got your project created, right click on Controllers folder and Add Controller. Make sure controller name is set to HomeController.

What we need now is a view associated with home controller index method. To add a view, open newly created HomeController and look for a line where it returns View() ActionResult. View() should be highligted in red. Right click on it and select Add View.

Make sure you compile your project before editing anything. There is some problem with VS 2010 and MVC 4 Razor engine. When you try to edit .cshtml file without rebuilding your solution first, VisualStudio will crash.

Import and configure facebook javascript framework

Time for a little bit of javascript-ing. Buuu. Right click on Scripts and create new Javascript file. Name it Facebook.js. Paste in following content:

function InitialiseFacebook(appId) { 

    window.fbAsyncInit = function () {
        FB.init({
            appId: appId,
            status: true,
            cookie: true,
            xfbml: true
        }); 

        FB.Event.subscribe('auth.login', function (response) {
            var credentials = { uid: response.authResponse.userID, accessToken: response.authResponse.accessToken };
            SubmitLogin(credentials);
        }); 

        FB.getLoginStatus(function (response) {
            if (response.status === 'connected') {
                alert("user is logged into fb");
            }
            else if (response.status === 'not_authorized') { alert("user is not authorised"); }
            else { alert("user is not conntected to facebook");  } 

        }); 

        function SubmitLogin(credentials) {
            $.ajax({
                url: "/account/facebooklogin",
                type: "POST",
                data: credentials,
                error: function () {
                    alert("error logging in to your facebook account.");
                },
                success: function () {
                    window.location.reload();
                }
            });
        } 

    }; 

    (function (d) {
        var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
        if (d.getElementById(id)) {
            return;
        }
        js = d.createElement('script');
        js.id = id;
        js.async = true;
        js.src = "//connect.facebook.net/en_US/all.js";
        ref.parentNode.insertBefore(js, ref);
    } (document)); 

}

This javascript will ensure that we're subscribed to login event on which we'll submit fb access token to our controller and save it in session. Also, on each window load, we'll check for fb login status and alert user accordingly.

Sign up for an app

Now, go to developers.facebook.com and create a new app. Make sure all app's urls point to the actual address of the app. If you're running the app from Visual Studio, its address will be http://localhost:[PORT NUMBER].

Login model and controller

Next, we need to add account controller that will save facebook response in session. Before we add it, let's create a model for an object that we'll pass to account controller. Right click on Models folder and add FacebookLoginModel.cs (Class).

namespace FacebookLoginButton.Models
{
    public class FacebookLoginModel
    {
        public string uid { get; set; }
        public string accessToken { get; set; }
    }
}

Once we've got our model, we can add AccountController.cs.

using System.Web.Mvc;
using FacebookLoginButton.Models; 

namespace FacebookLoginButton.Controllers
{
    public class AccountController : Controller
    {
        [HttpPost]
        public JsonResult FacebookLogin(FacebookLoginModel model)
        {
            Session["uid"] = model.uid;
            Session["accessToken"] = model.accessToken; 

            return Json(new {success = true});
        } 

    }
}

Login button configuration

To enable facebook framework, make sure you've got following lines added to your Views -> Shared -> Layout.cshtml file. Following lines should be added just before body closing tag.

<div id="fb-root"></div>
    <script src="@Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/Facebook.js")"
type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            InitialiseFacebook(@System.Configuration.ConfigurationManager.AppSettings["FacebookAppId"]);
        });
    </script>

Finally, modify Views -> Home -> Index.cshtml by pasting in following code:

@{
    ViewBag.Title = "Part 1 - Facebook Login Button";    Layout = "~/Views/Shared/_Layout.cshtml";


<h2>Part 1 - Facebook Login Button</h2> 

<fb:login-button autologoutlink="true" perms="read_friendlists, create_event, email, publish_stream"></fb:login-button> 

<p>Facebook Access Token: @Session["accessToken"]</p>
<p>Facebook User Id: @Session["uid"]</p> 

<p>If you're not getting javascript prompts on each window load, make sure facebook app id in web config is correct.</p>

That's it. Feel free to download comleted solution attached to this post.

Done. Great job

 



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in