Server side resource rendering with Angular and ASP .NET MVC: pass data from html to controller.

I wanted a ASP .NET MVC view (*.cshtml) to render on the server and pass the static text contained in the the ASP .NET project resource file to an Angular controller, so on the initial page request, all static data is returned to the client. Dynamic data, like grid content would then be requested by a separate JSON call.

The resource data is passed to the Angular controller by using ng-init.

 

For the sake of this blog post I stuffed everything in one *.cshtml page, including CSS en JavaScript.

Of course this would be separate files in an real application.

 

Project resource file

image

 

Layout.cshtml

 

<!DOCTYPE html> <html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="app"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Send initialization data from HTML to Angular controller</title> <style> /* Add some initial styling. */ html, body { background-color: #F1F1F1; font-family: "Open Sans", sans-serif; font-size: 13px; height: 100%; } body { margin: 20px; } [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; } </style> <!-- Library scripts --> <script src="https://code.angularjs.org/1.4.1/angular.min.js"></script> </head> <body ng-cloak> @RenderBody() <div> <div ng-controller="main" ng-init="resources={ textFromResource: '@WebApplication1.Properties.Resources.TextFromResource'}"> <h1>{{ title }}</h1> {{ resources.textFromResource }} </div> </div> <script> // Angular module. (function () { "use strict"; angular .module("app", []); }()); // Angular controller. (function () { "use strict"; function controller($scope) { $scope.title = "Show ASP .NET MVC resoure (*.resx) data to user on initial load."; } angular .module("app") .controller("main", ["$scope", controller]); }()); </script> </body> </html>

 

 

Result

image

Post arbitrary JSON data to a MVC controller action method

If you want to sent arbitrary JSON data to a MVC controller action method, you can use the following code:

[HttpPost]
public ActionResult HandleRequest()
{
// Read the RAW JSON from the request, 
// because MVC does not support a dynamic input parameter or an input parameter of type JObject.
// ASP .NET Web API does support a dynamic input parameter or an input parameter of type JObject.
string json = new StreamReader(this.Request.InputStream).ReadToEnd();
dynamic clientData = JObject.Parse(json);

Using ValidateAntiForgeryToken with AngularJS and MVC

I was getting a HTTP 500 error, when posting data from AngularJS to a MVC controller, which had the attribute ValidateAntiForgeryToken applied to it. This attribute expects a form to be posted to the MVC controller action method, but I wanted to sent JSON. If you want to do that then you can follow the blogpost from julian jelfs.

 

Now I just add: @Html.AntiForgeryToken()

Then I apply the custom attribute ValidateJsonAntiForgeryToken to the MVC controller action method, like

[HttpPost]
[Authorize] [ValidateJsonAntiForgeryToken]
public ActionResult GetDashboardData(DashboardViewModel model) {

Now I can Post data in AngularJS like:

// When using the @Html.AntiForgeryToken() and [ValidateJsonAntiForgeryToken], a http POST, 
// should contain a AntiForgeryToken in the header. // The @Html.AntiForgeryToken() will write add a hidden HTML input element with the name
//"__RequestVerificationToken". // This input element will contain to AntiForgeryToken. // The MVC AuthorizeAttribute [ValidateJsonAntiForgeryToken],
//will check the http request header for the "AntiForgeryToken".
function getHttpConfig() { var token = angular.element("input[name='__RequestVerificationToken']").val(); var config = { headers: { '__RequestVerificationToken': token } }; return config; }

var config = getHttpConfig();
var url = “http://localhost”;
var postData = {}; return $http.post(url, postData, config).then(function (response) {
// Do something terrible
});

 

Solution

 

https://julianjelfs.wordpress.com/category/mvc/

Manually create the SQL Server tables used by de SimpleMembershipProvider

 

Below you will find the t-sql code needed to generate de SQL Server SimpleMembershipProvider tables.

 

 

set ansi_nulls on
go
set quoted_identifier on
go
if object_id('dbo.webpages_Membership') is null
begin
create table dbo.webpages_Membership
(
UserId                                    int not null constraint PK_Membership_UserId primary key,
CreateDate                                datetime null,
ConfirmationToken                        nvarchar(128) null,
IsConfirmed                                bit null constraint DF_Membership_IsConfirmed default (0),
LastPasswordFailureDate                    datetime null,
PasswordFailuresSinceLastSuccess        int not null constraint DF_Membership_PasswordFailuresSinceLastSuccess default (0),
[Password]                                nvarchar(128) not null,
PasswordChangedDate                        datetime null,
PasswordSalt                            nvarchar(128) not null,
PasswordVerificationToken                nvarchar(128) null,
PasswordVerificationTokenExpirationDate    datetime null
)
end
go
set ansi_nulls on
go
set quoted_identifier on
go
if object_id('dbo.webpages_Roles') is null
begin
create table dbo.webpages_Roles
(
RoleId int identity(1,1) not null,
RoleName nvarchar(256) not null,
constraint PK_Roles_RoleId primary key clustered 
(
RoleId ASC
) with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY],
constraint UQ_Roles_RoleName unique nonclustered 
(
RoleName asc
) with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY]
)
end
go
set ansi_nulls on
go
set quoted_identifier on
go
if object_id('dbo.webpages_OAuthMembership') is null
begin
create table dbo.webpages_OAuthMembership
(
Provider        nvarchar(30) not null,
ProviderUserId    nvarchar(100) not null,
UserId            int not null,
constraint PK_OAuthMembership_Provider_ProviderUserId primary key clustered 
(
Provider asc,
ProviderUserId asc
) with    (    pad_index = off, 
statistics_norecompute = off,
ignore_dup_key = off,
allow_row_locks = on,
allow_page_locks = on) on [PRIMARY]
)
end
go
set ansi_nulls on
go
set quoted_identifier on
go
if object_id('dbo.webpages_UserProfile') is null
begin
create table dbo.webpages_UserProfile
(
UserId int identity(1,1) not null,
UserName nvarchar(56) not null,
constraint PK_UserProfile_UserId primary key clustered 
(
UserId asc
) with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY],
constraint UQ_UserProfile_UserName unique nonclustered 
(
UserName asc
) with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [PRIMARY]
)
end
go
set ansi_nulls on
go
set quoted_identifier on
go
if object_id('dbo.webpages_UsersInRoles') is null
begin
create table dbo.webpages_UsersInRoles
(
UserId int not null constraint FK_UsersInRoles_UserId foreign key(UserId) references dbo.webpages_UserProfile (UserId),
RoleId int not null constraint FK_UsersInRoles_RoleId foreign key(RoleId) references dbo.webpages_Roles (RoleId),
primary key clustered 
(
UserId asc,
RoleId asc
) with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on PRIMARY]
)
end
go

ASP .NET MVC4 / Web Api – Create users and roles for SimpleMembershipProvider with PowerShell and C#

If you are using the SimpleMembershipProvider for FormAuthentication in a ASP .NET MVC4 / Web Api project, the following PowerShell / C# code can be used to create and delete users and roles.

  • Create an empty XML App.config file "C:\Temp\App.config".
  • Paste the XML below in the file and save it.
  • Create an empty PowerShell file "C:\Temp\Manage_MVC_users_and_roles.ps1".
  • Paste the PowerShell code below in the file and save it.
  • Create an empty C# file "C:\Temp\Manage_MVC_users_and_roles.cs".
  • Paste the C# code below in the file and save it.
  • Execute the file "C:\Temp\Manage_MVC_users_and_roles.ps1" with PowerShell.

 

This will create an user "test2" with password "test2" and a role "Administrators".

 

Note

  • Database tables will automatically be created if they don’t exist.
  • De folder "C:\Temp" should contain the assemblies "System.Web.WebPages.dll" and "WebMatrix.Data.dll" and "WebMatrix.WebData.dll", this assembly can be downloaded using NuGet.

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<profile defaultProvider="SimpleProfileProvider">
<providers>
<add name="SimpleProfileProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"
connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
</assemblyBinding>
</runtime>
</configuration>

PowerShell code (Manage_MVC_users_and_roles.ps1)

 
# Get folder containing this script.
$scriptFolder = split-path $SCRIPT:MyInvocation.MyCommand.Path -parent
# Load App.config file from scriptfolder.
$appConfigPath = "$scriptFolder\App.config"
[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $appConfigPath)
# Compile C# code to dll.
$Assem = ( 
"WebMatrix.Data", 
"WebMatrix.WebData",
"System.Security",
"System.Web",
"System.Web.WebPages",
'System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
)
$codePath = "$scriptFolder\Manage_MVC_users_and_roles.cs"
$codeAssemblyName = "Manage_MVC_users_and_roles.dll"
Add-Type -OutputType Library –ReferencedAssemblies $Assem -OutputAssembly $codeAssemblyName -Path $codePath
# Load dll.
$codeAssemblyPath = "$scriptFolder\$codeAssemblyName"
Add-Type -Path $codeAssemblyPath
# Execute C# code.
$wrapper = New-Object Research.Rli.WebSecurityExecuter
$wrapper.Execute($scriptFolder);
pause

C# code

namespace Research.Rli
{
using System.Linq;
public interface IWebSecurityExecuter
{
void Execute(string scriptFolder);
}
public class WebSecurityExecuter : IWebSecurityExecuter
{
/// <summary>
/// Executes code found in the [WebSecurityWrapper] class in a new .net AppDomain, 
/// To prevent assembly load errors, a new AppDomain is created, with the ApplicationBase set to the folder containing this *.cs file.
/// </summary>
/// <param name="scriptFolder">
/// Folder containing this *.cs file.
/// </param>
public void Execute(string scriptFolder)
{
System.AppDomain childDomain = null;
try
{
var domainSetup = new System.AppDomainSetup()
{
ApplicationBase = scriptFolder,
ConfigurationFile = System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = System.AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = System.LoaderOptimization.MultiDomainHost
};
childDomain = System.AppDomain.CreateDomain("NewAppDomain", null, domainSetup);
IWebSecurityWrapper wrapper = (IWebSecurityWrapper)childDomain.CreateInstanceAndUnwrap(typeof(WebSecurityWrapper).Assembly.FullName, typeof(WebSecurityWrapper).FullName);
wrapper.Initialize();
wrapper.CreateUsers();
wrapper.CreateRoles();
}
finally
{
if (childDomain != null) System.AppDomain.Unload(childDomain);
}
}
}
public class WebSecurityWrapper : System.MarshalByRefObject, IWebSecurityWrapper
{
/// <summary>
/// Initializes the database connection and creates tabels, when these tables do not exist.
/// </summary>
public void Initialize()
{
DatabaseConnectionString = @"Data Source=(LocalDb)\v11.0;Initial Catalog=MyDatabase;Integrated Security=SSPI;";
Roles = new System.Collections.Generic.List<string>
{
"Administrators"
};
Users = new System.Collections.Generic.List<User> 
{ 
new User { Name = "test2", Password = "test2"}
};
UserIdColumnName = "UserId";
UserNameColumnName = "UserName";
UserTableName = "UserProfile";
// Creates user and role tables if they don't exist.
WebMatrix.WebData.WebSecurity.InitializeDatabaseConnection(DatabaseConnectionString, "System.Data.SqlClient", UserTableName, UserIdColumnName, UserNameColumnName, true);
}
public string DatabaseConnectionString { get; set; }
public System.Collections.Generic.List<string> Roles { get; set; }
public System.Collections.Generic.List<User> Users { get; set; }
public string UserIdColumnName { get; set; }
public string UserNameColumnName { get; set; }
public string UserTableName { get; set; }
public void CreateRoles()
{
Roles.ForEach(x =>
{
var roles = (WebMatrix.WebData.SimpleRoleProvider)System.Web.Security.Roles.Provider;
if (roles.RoleExists(x))
{
System.Console.WriteLine("Role [{0}] already exists.", x);
}
else
{
roles.CreateRole(x);
}
});
}
public void CreateUsers()
{
Users.ForEach(x =>
{
if (WebMatrix.WebData.WebSecurity.UserExists(x.Name))
{
System.Console.WriteLine("User [{0}] already exists.", x.Name);
}
else
{
WebMatrix.WebData.WebSecurity.CreateUserAndAccount(x.Name, x.Password);
}
});
}
public void DeleteRoles()
{
Roles.ForEach(x =>
{
var roles = (WebMatrix.WebData.SimpleRoleProvider)System.Web.Security.Roles.Provider;
if (roles.RoleExists(x))
{
System.Collections.Generic.List<string> usersInRole = roles.GetUsersInRole(x).ToList();
Users = new System.Collections.Generic.List<User>();
usersInRole.ForEach(u =>
{
Users.Add(new User { Name = u });
});
DeleteUsers();
roles.DeleteRole(x, true);
}
else
{
System.Console.WriteLine("Role [{0}] already exists.", x);
}
});
}
public void DeleteUsers()
{
var membership = (WebMatrix.WebData.SimpleMembershipProvider)System.Web.Security.Membership.Provider;
Users.ForEach(x =>
{
if (WebMatrix.WebData.WebSecurity.UserExists(x.Name))
{
membership.DeleteAccount(x.Name);
membership.DeleteUser(x.Name, true);
}
else
{
System.Console.WriteLine("User [{0}] does not exist.", x.Name);
}
});
}
public void ResetPasswordUsers()
{
Users.ForEach(x =>
{
if (WebMatrix.WebData.WebSecurity.UserExists(x.Name))
{
WebMatrix.WebData.WebSecurity.ResetPassword(null, x.Name);
}
else
{
System.Console.WriteLine("User [{0}] does not exist.", x.Name);
}
});
}
}
public interface IWebSecurityWrapper
{
void CreateRoles();
void CreateUsers();
string DatabaseConnectionString { get; set; }
void DeleteRoles();
void DeleteUsers();
void Initialize();
void ResetPasswordUsers();
System.Collections.Generic.List<string> Roles { get; set; }
string UserIdColumnName { get; set; }
string UserNameColumnName { get; set; }
System.Collections.Generic.List<Research.Rli.User> Users { get; set; }
string UserTableName { get; set; }
}
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
}

How to make Entity Framework 5.0 use an in-memory persistence DbContext instead of accessing the database.

If you want to switch between in-memory stub data and a database during runtime with Entity Framework 5.0, you have several options, some of them are:

– Using a second level cache mechanism like http://www.codeproject.com/Articles/435142/Entity-Framework-Second-Level-Caching-with-DbConte , filling the cache before use and setting the expiration time to infinite.

– Creating a extension method on the DbSet class that uses only the DbSet (for direct database access) or DbSet.Local for in-memory stub data, based on some parameter.

– Implement a MemoryPersistenceDbContext and MemoryPersistenceDbSet.

 

This post will focus on the last option.

Create a new MVC4 project in Microsoft Visual Studio 2010

File > New > Project

image

 

image

image

 

Add Entity Framework 5.0 NuGet package

Rightclick solution > Manage NuGet Packages for Solution…

image

image

image

image

 

I added 2 tables to a Research database on a LocalDb SQL Server 2012 instance

image

Add *.edmx model

Right click on the models folder:

image

 

image

ModelName = ResearchModel.edmx

image

DbContext name = ResearchUow

(UOW = Unit of work)

image

image

Model namespace = ResearchModel

Check all tables

image

Add code generation item

Open the ResearchUow.edmx > right click > Add Code Generation Item…

image

Code generation item name = ResearchModel.tt

image

Now the project looks like

image

 

Add an IEntity interface

This interface will be used to make the Find function work.

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Mvc4Application.Models
{
public interface IEntity
{
int Id { get; }
}
}

Add a MemoryPersistenceDbSet.cs in the Models folder

This will be used to store the data in-memory instead of in the database.

image

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace Mvc4Application.Models
{
public class MemoryPersistenceDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public MemoryPersistenceDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
if (!(typeof(T) is IEntity)) { throw new ArgumentException(string.Format("Entity [{0}] does not contain a property [Id], so it could not be converted to the IEntity interface, used in this function.", typeof(T).ToString())); }
return this.SingleOrDefault(e => (e as IEntity).Id == (int)keyValues.Single());
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
}

Change the ResearchModel.tt file, so all generated POCO entities derive from IEntity

Open the ResearchModel.tt files and change the line

<#=codeStringGenerator.EntityClassOpening(entity)#>

to

<#=codeStringGenerator.EntityClassOpening(entity)#> : IEntity

and click save, on save of the ResearchModel.tt file, the POCO entities will be regenerated and will now all implement the IEntity interface:

image

 

Change the ResearchModel.Context.tt file, so the IResearchUow interface, the ResearchUow class and the MemoryPersistenceResearchUow will be created.

 

Open the ResearchModel.Context.tt file and find the code:


<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext

{

    public <#=code.Escape(container)#>()

        : base("name=<#=container.Name#>")

    {

<#

if (!loader.IsLazyLoadingEnabled(container))

{

#>

        this.Configuration.LazyLoadingEnabled = false;

<#

}

#>

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)

    {

        throw new UnintentionalCodeFirstException();

    }

<#

    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())

    {

#>

    <#=codeStringGenerator.DbSet(entitySet)#>

<#

    }

    foreach (var edmFunction in container.FunctionImports)

    {

        WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false);

    }

#>

}

<#

if (!String.IsNullOrEmpty(codeNamespace))

{

    PopIndent();

#>

}

<#

}

#>

 

 

Replace by

 


<#=Accessibility.ForType(container)#> partial interface IResearchlUow

{

<#

    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())

    {

#>

    <#=codeStringGenerator.IDbSet(entitySet)#>

<#

    }

    foreach (var edmFunction in container.FunctionImports)

    {

        WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false);

    }

#>

    int SaveChanges();

}

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext, IResearchUow

{

<#

    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())

    {

#>

    <#=codeStringGenerator.DbSet(entitySet)#>

<#

    }

    foreach (var edmFunction in container.FunctionImports)

    {

        WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false);

    }

#>

    public <#=code.Escape(container)#>() : base("name=<#=container.Name#>")

    {

<#

if (!loader.IsLazyLoadingEnabled(container))

{

#>

        this.Configuration.LazyLoadingEnabled = false;

<#

}

#>

    }

    public <#=code.Escape(container)#>(string connection) : base(connection)

    {

<#

if (!loader.IsLazyLoadingEnabled(container))

{

#>

        this.Configuration.LazyLoadingEnabled = false;

<#

}

#>

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)

    {

        throw new UnintentionalCodeFirstException();

    }

}

<#=Accessibility.ForType(container)#> partial class MemoryPersistenceResearchUow : <#=code.Escape(container)#>

{

    public MemoryPersistenceResearchUow()

    {

        Seed();

    }

    public void ClearAll()

    {

<#

    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())

    {

#>

        <#=codeStringGenerator.DbSetInConstructor(entitySet)#>

<#

    }

#>

    }

    public override int SaveChanges()

    {

        return 0;

    }

}

<#

if (!String.IsNullOrEmpty(codeNamespace))

{

    PopIndent();

#>

}

<#

}

#>

 

and find

public string DbSet(EntitySet entitySet)

{

    return string.Format(

        CultureInfo.InvariantCulture,

        "{0} DbSet<{1}> {2} {{ get; set; }}",

        Accessibility.ForReadOnlyProperty(entitySet),

        _typeMapper.GetTypeName(entitySet.ElementType),

        _code.Escape(entitySet));

}

 

Replace by

    public string IDbSet(EntitySet entitySet)

    {

        return string.Format(

            CultureInfo.InvariantCulture,

            "IDbSet<{0}> {1} {{ get; set; }}",

            _typeMapper.GetTypeName(entitySet.ElementType),

            _code.Escape(entitySet));

    }

    public string DbSet(EntitySet entitySet)

    {

        return string.Format(

            CultureInfo.InvariantCulture,

            "{0} IDbSet<{1}> {2} {{ get; set; }}",

            Accessibility.ForReadOnlyProperty(entitySet),

            _typeMapper.GetTypeName(entitySet.ElementType),

            _code.Escape(entitySet));

    }

    public string DbSetInConstructor(EntitySet entitySet)

    {

        return string.Format(

            CultureInfo.InvariantCulture,

            "this.{0} = new MemoryPersistenceDbSet<{0}>();",

            _typeMapper.GetTypeName(entitySet.ElementType));

    }

 

Now on save of the ResearchModel.Context.tt  T4 template will generate the following code:

namespace Mvc4Application.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial interface IResearchUow
{
IDbSet<Car> Car { get; set; }
IDbSet<Person> Person { get; set; }
int SaveChanges();
}
public partial class ResearchUow : DbContext, IResearchUow
{
public IDbSet<Car> Car { get; set; }
public IDbSet<Person> Person { get; set; }
public ResearchUow() : base("name=ResearchUow")
{
}
public ResearchUow(string connection) : base(connection)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
public partial class MemoryPersistenceResearchUow : ResearchUow
{
public MemoryPersistenceResearchUow()
{
Seed();
}
public void ClearAll()
{
this.Car = new MemoryPersistenceDbSet<Car>();
this.Person = new MemoryPersistenceDbSet<Person>();
}
public override int SaveChanges()
{
return 0;
}
}
}

 

Add a partial class file for the MemoryPersistenceResearchUow

Every time you update the ReserachModel.edmx from the database or save the T4 templates ResearchModel.tt and ResearchModel.Context.tt, the T4 templates will execute and regenerate all POCO entities and the IResearchUow interface, ResearchUow class and the MemoryPersistenceResearchUow.  To prevent the code that seeds the in-memory UOW to be overwritten a partial class MemoryPersistenceResearchUow is created.

image

public partial class MemoryPersistenceResearchUow
{
public void Seed()
{
ClearAll();
// TODO Add seed logic here, like.....
this.Person.Add(new Person { Id = 1, Name = "Roel van Lisdonk" });
this.Car.Add(new Car { Id = 1, NumberPlate = "8-KJA-00", PersonId = 1 });
}
}

Add a IResearchUowFactory and ResearchUowFactory that will contain the logic to create a ResearchUow or an MemoryPersistenceResearchUow.

namespace Mvc4Application.Models
{
public interface IResearchUowFactory
{
IResearchUow GetResearchUow();
}
}
using System;
using System.Web.Configuration;
namespace Mvc4Application.Models
{
public class ResearchUowFactory : IResearchUowFactory
{
public IResearchUow GetResearchUow()
{
string key = "UseStubs";
string result = WebConfigurationManager.AppSettings[key];
if (result == null) { throw new ApplicationException(string.Format("Could not find AppSetting[{0}].", key)); }
bool useStubs = bool.Parse(result);
return useStubs ? new MemoryPersistenceResearchUow() : new ResearchUow();
}
}
}

 

The project will no look like:

image

 

Add appSetting "UseStubs" to the Web.config

  <appSettings>
<add key="UseStubs" value="true" />

Install an IoC container by using NuGet, in this case I will use ninject:

Install Ninject and Ninject.MVC3 (no MVC4 available yet, but works just fine) this will also install Ninject.Web.Common.

image

In the App_Start folder change the NinjectWebCommon.cs

Fill the RegisterService function:

/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IResearchUowFactory>().To<ResearchUowFactory>();
} 

In the Controllers folder change the HomeControler, add:

private readonly IResearchUowFactory _researchUowFactory;
private readonly IResearchUow _researchUow;
public HomeController(IResearchUowFactory researchUowFactory)
{
_researchUowFactory = researchUowFactory;
_researchUow = _researchUowFactory.GetResearchUow();
}
public ActionResult Index()
{
Person firstPerson = _researchUow.Person.First();
ViewBag.Message = string.Format("First person name [{0}].", firstPerson.Name);
return View();
}

Result

This results in:

image

 

The text "First person name [Roel van Lisdonk]" is shown. This was the data from the seed method:

this.Person.Add(new Person { Id = 1, Name = "Roel van Lisdonk" });

and not from the real database, because the database at this point is empty.

This proves we can switch using in-memory stub data or the real database by changing a appSetting in the web.config.

 

 

 

Don’t forget to fix the unit tests.

In the unit tests for the HomeController, change the lines:

HomeController controller = new HomeController();

to

HomeController controller = new HomeController(new ResearchUowFactory());

 

 

Now you are able to use the ResearchUow within your HomeController and switch between the MemoryPersistenceResearchUow and the ResearchUow by changing the appSetting UseStubs.

MVC tip: use Url.Content in script src attribute, in *.cshtml files!

In your "*.cshtml" pages, use Url.Content and don’t directly specify the locations of the JavaScript files, because specifying the JavaSript files directly will result in 404 error, when using a virtual directory/application within a IIS website.

 

So use

<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
</head>

and don’t use:

<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
</head>