Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Monday, 30 March 2015
( Async | MVC )

http://www.asp.net/mvc/overview/performance/using-asynchronous-methods-in-aspnet-mvc-4

Found a strange problem with the app hanging/blocking on the await client.GetAsync call.

private static string _address = "http://www.google.com"; public async Task<ActionResult> Index() { var t1 = CallAPI(1); await t1; ViewBag.stuff = t1.Result; return View(); } async Task<string> CallAPI(int i) { string result; using (var client = new HttpClient()) { var response = await client.GetAsync(_address); response.EnsureSuccessStatusCode(); result = await response.Content.ReadAsStringAsync(); } return result; }

http://stackoverflow.com/questions/14046471/task-waitall-keeps-in-loop

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html 

http://blog.stephencleary.com/2012/02/async-and-await.html (intro post)

public async Task<ActionResult> Index() { var time = DateTime.Now; ServicePointManager.DefaultConnectionLimit = 5; const int n = 5; var tasks = new Task<string>[n]; for (int i = 0; i < n; i++) { tasks[i] = CallAPI(i); } // at this point all tasks will be running at the same time await Task.WhenAll(tasks); Debug.WriteLine(Actual(time), "After all tasks"); // do stuff for (int i = 0; i < n; i++) { var things = JsonConvert.DeserializeObject<Thing>(tasks[0].Result); var url = things.response.biographies[6].url; Debug.WriteLine(url); } return View(); } async Task<string> CallAPI(int i) { var time = DateTime.Now; string result; using (var client = new HttpClient()) { // ConfigureAwat - do not capture the current ASP.NET request context var response = await client.GetAsync(_address).ConfigureAwait(false); // Not on the original context here, instead we're running on the thread pool response.EnsureSuccessStatusCode(); result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); } Debug.WriteLine(Actual(time), "Finished " + i); return result; }

This works as expected

| | # 
# Friday, 12 December 2014
( AngularJS | MVC )

http://www.ndcvideos.com/#/app/video/3061

http://www.melvicorp.com/downloads.html – Source code

image
Talking about a hybrid application – some Angular and some MVC

Uses routing in both frameworks, so some is MVC and some in ng.

Back button works perfectly

| | # 
# Tuesday, 18 February 2014

Making Controllers Testable

fakepersonrepository – using a fake its much easier to test the controller now

StructureMap

using constructor based DI is prone to errors, and labour intensive… so use a DI framework

public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
// x.For<IExample>().Use<Example>();
});
returnObjectFactory.Container;

Uses a similar concept to Unity, so ISomething is requested it returns Something.

PeopleController and Views

  • use ViewModels – could be exposing too much data with Model, and could be changing Model
  • Paging required
  • Sorting required

Multiple ViewModels – eg displaying a person (fewer fields) would have different data to editing a person..

He uses AutoMapper

Car car = _carRepository.FindById(id);
CarViewModel carViewModel = newCarViewModel();
carViewModel.Id = car.Id;
carViewModel.Name = car.Name;
return View(carViewModel);

Car car = _carRepository.FindById(id);
CarViewModel carViewModel = newCarViewModel();
Mapper.Map(car, carViewModel)

AutoMapper

public static class AutoMapperConfig
{
public static voidStart()
{
Mapper.CreateMap<Person, DisplayPerson>();
// Other mappings go here
Mapper.AssertConfigurationIsValid();
}
}

Can even do it automatically if have the same name and types of members

Sorting

public ActionResult Index(string sort = "Id", string sortDir = "ASC")
{
    var sortedPeopleList = _peopleRepository
    .FindAll().OrderBy(string.Format("{0} {1}", sort, sortDir));
    // Other code here
    return View();
}

eg using a grid and want to sort based on column header

Linq doesn’t allow this.. no overload of OrderBy that accepts a string.

Can use DynamicQuery library

Paging

public ActionResult Index(int page = 1, string sort = "Id", string sortDir = "ASC")
{
    var allPeople = _peopleRepository.FindAll()
    .OrderBy(string.Format("{0} {1}", sort, sortDir)).Skip((page * PageSize) - PageSize)
    .Take(PageSize);
    var model = new List<DisplayPerson>();
    Mapper.Map(allPeople, model);
    return View(model);
}

Combining paging and sorting

public ActionResult Index(int page = 1, string sort = "Id", string sortDir = "ASC")
{
    int totalRecords = _peopleRepository.FindAll().Count();
    var data = new List<DisplayPerson>();
    IQueryable<Person> allPeople = _peopleRepository.FindAll()
    .OrderBy(BuildOrderBy(sort, sortDir)).Skip((page * PageSize) - PageSize)
    .Take(PageSize);
    Mapper.Map(allPeople, data);
    var model = new PagerModel<DisplayPerson> { Data = data, PageNumber = page, PageSize = PageSize, TotalRows = totalRecords };
    return View(model);
}

Using Count() to get proper paging links.  PagerModel<T> – made it generic which means other ViewModel types that need to support paging can use it.

public class PagerModel<T> where T : class
{
    public IEnumerable<T> Data { get; set; }
    public int PageSize { get; set; }
    public int PageNumber { get; set; }
    public int TotalRows { get; set; }
}

He is using MVC WebGrid to render:

@if (Model.Data.Any())
{
    var grid = new WebGrid(null, defaultSort: "FirstName", columnNames: new[] { "Id", "FullName", "DateOfBirth", "Type" }, rowsPerPage: Model.PageSize);
    grid.Bind(Model.Data, rowCount: Model.TotalRows, autoSortAndPage: false);

    @grid.GetHtml(columns: grid.Columns(
   grid.Column("Id"),
   grid.Column(header: "Full name", columnName: "FullName", format: (item) => Html.ActionLink(((string)item.FullName), "Details", new { item.id })),
   grid.Column("DateOfBirth", header: "Date of Birth", format: (item) => item.DateOfBirth.ToString("d")),
   grid.Column("Type", canSort: false),
   grid.Column(header: "Addresses", format: item => new HtmlString(
                Html.ActionLink("Home", "Edit", "Addresses", new { personId = item.Id, contactType = (int)ContactType.Personal }, null).ToString() + " | " +
                Html.ActionLink("Work", "Edit", "Addresses", new { personId = item.Id, contactType = (int)ContactType.Business }, null).ToString())
   ),
   grid.Column(format: (item) => Html.ActionLink("E-mail addresses", "List", "EmailAddresses", new { personId = item.id }, null)),
   grid.Column(format: (item) => Html.ActionLink("Phone numbers", "List", "PhoneNumbers", new { personId = item.id }, null)),
   grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { item.id })),
   grid.Column(format: (item) => Html.ActionLink("Delete", "Delete", new { item.id }))
  )
)
}

CreateAndEditPerson has dataannotations on it.

If anything fails then am passing good EF error messages back to the user.

Validation

public class CreateAndEditPerson : IValidatableObject
{
    public int Id { get; set; }

    [Required, DisplayName("First name")]
    public string FirstName { get; set; }

    [Required, DisplayName("Last name")]
    public string LastName { get; set; }

    [DisplayName("Date of birth")]
    public DateTime DateOfBirth { get; set; }

    public PersonType Type { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Type == PersonType.None)
        {
            yield return new ValidationResult("PersonType can't be None.", new[] { "Type" });
        }
    }
}

Duplication here with annotations and Validate method.. reckons is okay as potentially different validations.  eg for legacy code on model.. and tighter on ViewModel.

DropDownList

He uses a helper… better way in EF6 and MVC5

Suggests using JQueyryUI or KendoUI to make good looking UI’s.

| | # 
# Saturday, 21 December 2013
( MVC | TDD )

Jesse Liberty PS video

  • Test only the code you wrote
    • do not test with other modules (that is integration testing)
  • Business logic should not be in the controller
    • should be in the Model or Repository layer
  • Mocking – allows us to simulate a DB
    • Stubs.. very simple.. return a single value
    • Fakes.. a bit of logic
    • Full Mocks
  • DI

Mocking

Remember you are are not testing the mocked object… but you are testing your other code

  • Interfaces
  • Methods
  • Properties
  • LINQ Queries
  • Non-public members and types

DI allows us to insert our mock

  • Constructor injection (poor mans)
  • Property injection (feed mock to specific property)

Part 2

image

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

He’s called his interface Repository (Uncle Bob recommendation no I)

public interface Repository
{
    List<Product> GetAll();
}
[TestMethod]
public void Index_Returns_All_Products_in_DB()
{
    var productRepository = Mock.Create<Repository>();
    // Accepts a lambda statement
    Mock.Arrange(() => productRepository.GetAll())
        .Returns(new List<Product>()
        {
            new Product {Genre = "Fiction", ID=1, Name="Moby Dick", Price=12.50m},
            new Product {Genre = "Fiction", ID=2, Name="War and Peace", Price=17m}
        })
        .MustBeCalled();

    // Act
    HomeController controller = new HomeController(productRepository);
    ViewResult viewResult = controller.Index();
    var model = viewResult.Model as IEnumerable<Product>;

    // Assert
    Assert.AreEqual(2, model.Count());
}

Using JustMock to mock out the Interface’d Repository.

Part 3

Determine and explore requirements throughout this process

[TestMethod]
public void FindByGenreReturnsAllInGentre()
{
    // Arrange
    // Returning a mock - concrete implementation of the Repository interface
    Repository productRepository = Mock.Create<Repository>();
    Mock.Arrange(() => productRepository.GetAll())
        .Returns(new List<Product>()
        {
            new Product {Genre = "Fiction", ID=1, Name="Moby Dick", Price=12.50m},
            new Product {Genre = "Fiction", ID=2, Name="War and Peace", Price=17m},
            new Product {Genre = "Non-Fiction", ID=3, Name="Chemistry", Price=20m}
        })
        .MustBeCalled();

    // Act
    var controller = new HomeController(productRepository);
    ViewResult viewResult = controller.FindByGenre("Fiction");
    var model = viewResult.Model as IEnumerable<Product>;

    // Assert
    Assert.AreEqual(2, model.Count());
    Assert.AreEqual("Moby Dick", model.ToList()[0].Name);
    Assert.AreEqual("War and Peace", model.ToList()[1].Name);
}

image

| | # 
( MVC | TDD )

http://pluralsight.com/training/Courses/TableOfContents/aspdotnet-mvc-testing-from-scratch
Jesse Liberty

FizzBuzzMVC

Mocking: JustMock (free)

Unit Testing

  • Test a single unit – a class or (better) a method
  • Test in isolation

SOLID Principles and Refactoring

Uncle Bob

  • Single Responsibility
    • Exactly one reason for that method / class to change
    • One responsibility per method / class
  • Open-Closed Principle
    • Open for extension, closed for modification.. so dependable API
    • Derive but don’t modify internals
  • Liskov Substitution
    • Ability to replace instances with their subtypes
  • Interface Segregation Principle
    • No client should be forced to depend on methods it doesn’t use
    • Small Interfaces
  • Dependency Inversion
    • Depend on abstractions (ie DI – just like in controller which we implement poor mans DI)

Names matter

  • Classes = Nouns (things)
  • Methods = Verbs (doing)
  • Collections are plural
  • Hungarian eg lblName… avoid.

.

  • Functions should be small
  • Do one thing
  • Avoid switch
    • Use derived types
  • Minimize parameters into a method
    • Three or more increases cognitive load

DRY – Don’t Repeat Yourself

YAGNI – You Aint Goona Need It

Use fewer and smarter comments – use to explain intent

TODO Comments – use

FizzBuzz

image

image

[TestMethod]
public void Given1Return1()
{
    var controller = new FizzBuzzController();

    var result = controller.Index(1) as ViewResult;

    string expected = "1";
    string actual = result.ViewBag.Output;

    Assert.AreEqual(expected, actual);
}
public ActionResult Index(int value)
{
    for (int i = 1; i <= value; i++)
    {
        if (i % 3 == 0)
        {
            ViewBag.Output += "Fizz ";
        }
        else
        {
            ViewBag.Output += i.ToString() + " ";
        }    
    }
    return View();
}
        [TestMethod]
        public void Given1Return1()
        {
            var controller = new FizzBuzzController();
            var result = controller.Index(1) as ViewResult;
            string expected = "1 ";
            string actual = result.ViewBag.Output;
            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Given3Return12Fizz()
        {
            var controller = new FizzBuzzController();
            var result = controller.Index(3) as ViewResult;
            string expected = "1 2 Fizz ";
            string actual = result.ViewBag.Output;
            Assert.AreEqual(expected, actual);
        }

Red Green refactor.

public ActionResult Index(int value)
{
    for (int i = 1; i <= value; i++)
    {
        if (i % 3 == 0 && i % 5 == 0)
        {
            ViewBag.Output += "FizzBuzz ";
        }
        else if (i % 3 == 0)
        {
            ViewBag.Output += "Fizz ";
        }
        else if (i % 5 == 0)
        {
            ViewBag.Output += "Buzz ";
        }
        else
        {
            ViewBag.Output += i.ToString() + " ";
        }    
    }
    return View();
}

Put in a view @ViewBag.Ouput  then /FizzBuzz/Index?value=15

| | # 
# Monday, 26 September 2011
( Entity Framework | Massive | MVC | MVC3 | T4 | VidPub )

Use wordpress theme!

What is the business model?

  • Sell videos (ie download)
  • Sell access to the videos (ie stream)
  • Have small chunks ie episodes

What is our model (starting in baby steps)?

  • A Customer buys a Production
  • A Production is one or more Episodes
  • A Customer buys a Subscription
  • A Subscription gives access to Productions

so episodes cannot be purchased individually.

Write Tests

  1. CustomerSpecs
  2. SubscriptionSpecs
  3. ProductionSpecs
  4. EpisodeSpecs

Share them with the team as a discussion around business logic.

[TestFixture]
    public class CustomerSpecs : TestBase {
        [Test]
        public void a_user_should_be_able_to_add_production_to_cart() {
            this.IsPending();
        }

        [Test]
        public void a_user_that_owns_a_production_should_be_able_to_stream() {
            this.IsPending();
        }

        [Test]
        public void a_user_that_owns_a_production_should_be_able_to_download() {
            this.IsPending();
        }

        [Test]
        public void a_user_should_have_be_able_to_purchase_sub() {
            this.IsPending();
        }

        [Test]
        public void a_user_with_monthly_should_only_be_able_to_stream() {
            this.IsPending();
        }

        [Test]
        public void a_user_with_yearly_should_be_able_to_stream_and_download() {
            this.IsPending();
        }

        [Test]
        public void a_user_with_cancelled_sub_should_not_be_able_to_stream_or_download() {
            this.IsPending();
        }

        [Test]
        public void a_user_with_a_suspended_sub_should_not_be_able_to_stream_or_download() {
            this.IsPending();
        }

        [Test]
        public void a_user_with_overdue_sub_should_be_able_to_stream_or_download() {
            this.IsPending();
        }
        [Test]
        public void a_user_should_be_able_to_cancel_sub() {
            this.IsPending();
        }
        
    }
//A Subscription grants access over time
    //there is a monthly and a yearly
    //a monthly subscription offers stream only access
    //a yearly offers stream and download
    //a Customer can buy a subscription

    [TestFixture]
    public class SubscriptionSpecs : TestBase {

        [Test]
        public void a_subscription_is_only_valid_one_per_user() {
            this.IsPending();
        }

        [Test]
        public void a_subscription_can_be_pending_current_overdue_suspended_or_cancelled() {
            this.IsPending();
        }

        [Test]
        public void a_pending_subscription_can_turn_current() {
            this.IsPending();
        }

        [Test]
        public void a_current_subscription_can_become_overdue_if_payment_late() {
            this.IsPending();
        }

        [Test]
        public void an_overdue_subscription_can_become_current_if_payment_received_in_full() {
            this.IsPending();
        }

        [Test]
        public void an_overdue_subscription_can_become_suspended_if_payment_not_received_after_3_tries() {
            this.IsPending();
        }


        [Test]
        public void a_subscription_can_be_upgraded_from_monthly_to_annual() {
            this.IsPending();
        }

        [Test]
        public void a_subscription_cannot_be_downgraded_from_annual_to_monthly() {
            this.IsPending();
        }

    }
//A Production is a collection of Episodes
    //A Customer can buy a Production
    //A Customer cannot buy an individual episode

    [TestFixture]
    public class ProductionSpecs : TestBase {

        [Test]
        public void a_production_has_one_or_more_episodes() {
            this.IsPending();
        }

        [Test]
        public void a_production_can_cost_0_or_more_dollars() {
            this.IsPending();
        }

        [Test]
        public void a_production_can_be_in_production_published_suspended_or_offline() {
            this.IsPending();
        }

        [Test]
        public void a_production_is_viewable_if_not_offline() {
            this.IsPending();
        }

        [Test]
        public void a_production_can_be_downloaded_if_flagged() {
            this.IsPending();
        }


        [Test]
        public void episodes_can_be_released_offline_in_process() {
            this.IsPending();
        }
        
        [Test]
        public void episodes_are_viewable_if_released() {
            this.IsPending();
        }


        [Test]
        public void customers_can_see_notes_per_production() {
            this.IsPending();
        }

        [Test]
        public void customers_can_see_notes_per_episode() {
            this.IsPending();
        }

        [Test]
        public void customers_can_see_when_an_episode_and_production_was_released() {
            this.IsPending();
        }

        [Test]
        public void customers_can_see_who_authored_the_production() {
            this.IsPending();
        }

        [Test]
        public void customers_can_see_how_long_an_episode_is() {
            this.IsPending();
        }

        [Test]
        public void customers_can_see_total_duration_of_production() {
            this.IsPending();
        }
        

DB

DB First, Classes (or code) First, Migrations (from Rails world)

but .NET people don’t know migrations so leave for now.

EF Code-first

git checkout –b “codefirst”

EF Code-first

NuGet install in Web project.

Make classes with some properties:

public class Production {
        [Required]
        public string Title { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }


    }

Rob isn’t a fan of DataAnnotations…messy.  More attributes than code… aren’t descriptive.. hard to refactor.

eg in AccountModel:

namespace VidPub.Web.Models {

    public class ChangePasswordModel {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Current password")]
        public string OldPassword { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "New password")]
        public string NewPassword { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm new password")]
        [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public class LogOnModel {
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }

setup DbContext:

namespace VidPub.Web.Models {
    public class VidpubDBContext : DbContext {
        public DbSet<Production> Productions { get; set; }
    }
}

Trick#1 – EF will try to use .\SQLExpress

image

No PK defined.

Guids as PK’s make DBA’s cry?

EF Conventions will use these as PK’s:

  • ProductionID
  • ID
namespace VidPub.Web.Models {
    public class Production {
        [Required]
        public int ID { get; set; }
        [Required]
        public string Title { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
    }
}

image

with no connection string defined we have our db created.

However its made the nvarchar(MAX) which isn’t good..

namespace VidPub.Web.Models {
    public class Production {
        [Required]
        public int ID { get; set; }
        [Required]
        [MaxLength(200)]
        public string Title { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
    }
}

however on recompile:

image

Tip #2: EF Code-first won’t run ALTER… its drop or recreate.

FK

    public class Production {
        [Required]
        public int ID { get; set; }
        [Required]
        public string Title { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public ICollection<Episode> Episodes { get; set; }
    }

    public class Episode {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public int ProductionID { get; set; }
    }

and it gen’s up the database fine.

var db = new VidPub.Web.Models.VidpubDBContext();
            var p = new Production { Title = "My Production" };
            var e = new Episode { Title = "My Episode" };
            p.Episodes = new List<Episode>();
            p.Episodes.Add(e);
            db.Productions.Add(p);

            db.SaveChanges();

MVC Scaffolding

git checkout –b “scaffolding”

Install-Package MvcScaffolding

only have this in the model (no dbcontext from above)

namespace VidPub.Web.Models {
    public class Production {
        public int ID { get; set; }
        [Required]
        public string Title { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public ICollection<Episode> Episodes { get; set; }
    }

    public class Episode {
        public int ID { get; set; }
        public int ProductionID { get; set; }
        public string Title { get; set; }
    }
}
Scaffold Controller production
And it worked:
image
image

hmm – fast to go.. but even changes need to make eg varchar(max), database regens.. are going to get old v.soon.

Opinion

EF and Code-first… more work…in real world.

Make model in Database:

The less abstractions the better.. he loves rails activerecord.

Scaleable and Maintainable

More dev’s will know EF in the future years than anything else.

Good/Bad opinion of EF -

Massive

Bad – future hires won’t know it

Good

  • small and simple
  • high perf
  • this has worked for Rob in the past

Controller Design

Design RESTfully

REpresentational State Trasfer

“create an experience for a user that is predictable and understandable based on a url”

image

so now have a controller that is stubbed out and ready to go.

public class ProductionsController : Controller
    {
        Productions _table;
        public ProductionsController() {
            _table = new Productions();
        }

        public ActionResult Index()
        {
            return View(_table.All());
        }

then create a view:

image

fooling the tooling a bit as we’re going to be using dynamics, so chose any object.

image

Made db in the db! From the dbscript.sql file that came with VidPub.

So now we’ve got data reading from via Massive.

public class ProductionsController : Controller
    {
        Productions _table;
        public ProductionsController() {
            _table = new Productions();
        }

        public ActionResult Index()
        {
            return View(_table.All());
        }

via Massive (vidpub connection string in web.config)

public class Productions : DynamicModel {
        public Productions()
            : base("VidPub", "Productions", "ID") {
        }
<connectionStrings>
    <add name="VidPub" connectionString="server=.\;database=VidPub_Dev;integrated security=true" />

and rendered:

image

Views

[HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            dynamic item = _table.CreateFrom(collection);
            try
            {
                _table.Insert(item);
                return RedirectToAction("Index");
            }
            catch
            {
                TempData["alert"] = "There was an error adding this item";
                return View();
            }
        }

CreateFrom creates a new Expando, white listed against the columns in the db.

Customizing the Generators - T4

As we can’t do @Html.TextBox(“title”, Model.Title)… extension methods and dynamics don’t play well together.

If we do string title = Model.Title then it all works.

image

This added a bunch of templates":

image

Then just took our ProductionsController code and put it into the template Controller.tt

<#@ template language="C#" HostSpecific="True" #>
<#
MvcTextTemplateHost mvcHost = (MvcTextTemplateHost)(Host);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using VidPub.Web.Models;

<#
var controllerName = mvcHost.ControllerName;
var nameSpace = mvcHost.Namespace;
var tableName = controllerName.Replace("Controller", "");
#>

namespace <#= nameSpace #> {
public class <#= controllerName #> : Controller
    {
        dynamic _table;
        public <#= controllerName #>() {
            _table = new <#= tableName #>();
            ViewBag.Table = _table;
        }

        public ActionResult Index()
        {
            return View(_table.All());
        }

So now it generates much more cleanly.

namespace VidPub.Web.Controllers
{
    public class ProductionsController : ApplicationController
    {
        dynamic _table;
        public ProductionsController() {
            _table = new Productions();
            ViewBag.Table = _table;
        }

        public ActionResult Index()
        {
            return View(_table.All());
        }

        public ActionResult Details(int id)
        {
            return View(_table.FindBy(ID: id, schema: true));
        }

        public ActionResult Create()
        {
            return View(_table.Prototype);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(FormCollection collection)
        {
            dynamic item = _table.CreateFrom(collection);
            try
            {
                _table.Insert(item);
                return RedirectToAction("Index");
            }
            catch
            {
                TempData["alert"] = "There was an error adding this item";
                return View();
            }
        }
        
        public ActionResult Edit(int id)
        {
            var model = _table.Get(ID: id);
            model._Table = _table;
            return View(model);
        }

        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            var model = _table.CreateFrom(collection);
            try
            {
                _table.Update(model, id);
                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                TempData["Error"] = "There was a problem editing this record";
                return View(model);
            }
        }

        public ActionResult Delete(int id)
        {
            return View();
        }


        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(int id, FormCollection collection)
        {
            try
            {
                _table.Delete(id);
                return RedirectToAction("Index");
            }
            catch
            {
                TempData["Error"] = "There was a problem deleting this record";
                return View("Index");
            }
        }
    }
}

CruddyController

abstracting to a base class the cruddyness:

namespace VidPub.Web.Infrastructure {
    public class CruddyController : ApplicationController {
        // IoC will need to inject a tokenStore in everything that inhertis hmmmmmm
        public CruddyController(ITokenHandler tokenStore) : base(tokenStore) {}

        protected dynamic _table;

        //all virtual so can override if necessary
        public virtual ActionResult Index() {
            return View(_table.All());
        }

        public virtual ActionResult Details(int id) {
            return View(_table.FindBy(ID: id, schema: true));
        }

        public virtual ActionResult Create() {
            return View(_table.Prototype);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Create(FormCollection collection) {
            dynamic item = _table.CreateFrom(collection);
            try {
                _table.Insert(item);
                return RedirectToAction("Index");
            }
            catch {
                TempData["alert"] = "There was an error adding this item";
                return View();
            }
        }

        public virtual ActionResult Edit(int id) {
            var model = _table.Get(ID: id);
            model._Table = _table;
            return View(model);
        }

        [HttpPost]
        public virtual ActionResult Edit(int id, FormCollection collection) {
            var model = _table.CreateFrom(collection);
            try {
                _table.Update(model, id);
                return RedirectToAction("Index");
            }
            catch (Exception ex) {
                TempData["Error"] = "There was a problem editing this record";
                return View(model);
            }
        }

        public virtual ActionResult Delete(int id) {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Delete(int id, FormCollection collection) {
            try {
                _table.Delete(id);
                return RedirectToAction("Index");
            }
            catch {
                TempData["Error"] = "There was a problem deleting this record";
                return View("Index");
            }
        }
    }
}

Review

  • Use what you know and get app to market
  • Big ORM in startup can get in the way
  • Build whats needed, no more
  • Avoided orm here
| | # 
# Friday, 23 September 2011
( jQuery | MVC | VidPub )

Razor automatically encodes HTML for us

@Snippets.FooterText()

public static dynamic FooterText() {
        return string.Format("&copy;{0} VidPub, Inc.", DateTime.Now.Year);
    }

image

This is bad..

Most of the Html helpers eg Html.Action, Html.ActionLink return an MvcHtmlString which inherits from HtmlString.  This is around to control the encoding of html.

@functions {
    public static dynamic FooterText() {
        return new HtmlString(string.Format("&copy;{0} VidPub, Inc.", DateTime.Now.Year));
    }
}

better.  This means we sidestep mvc’s always on encoding.

Partials / Helpers / Functions

Do a quotes class and return a string array.

@Html.Partial got confused when we were handing off dynamics stuff.  So easier to use a helper (and a function in the same file…nice)

@functions{
    public static dynamic Scrub(string quote) {
        return new HtmlString(quote);
    }
}

@helper User(string quote) {
    <div class="quote">
        @Scrub(quote)
    </div>    
}

 

<div class="container">
    <div class="column span-24">
        @foreach (dynamic item in VidPub.Web.Models.Quotes.FromUsers()) {
            @Quotes.User(item)
        }
    </div>
</div>

image

public class Quotes {
        public static dynamic FromUsers() {
            var quotes = new string[] {"I just rocked the interview blah blah"
                ,"This is really great"
                ,"Bough mastering jquery from vidpub and so far it rocks"
                ,"2Bough mastering jquery from vidpub and so far it rocks"
                ,"3Bough mastering jquery from vidpub and so far it rocks"
                ,"4Bough mastering jquery from vidpub and so far it rocks"
            };
            Random rnd = new Random();
            return quotes.OrderBy(x => rnd.Next()).ToList();
        }
    }

Rob has html encoding in the quotes… ie <b> in the acutal quote string, which is why he’s use a Scrub function.  I dont need it.

Jquery Cycle

@HTML.CSS("jquery_cycle.js")

in css

id is a #

class is a .

#user_quotes
{
    margin-bottom:32px;
    padding: 24px;
}

.quote
{
    font-family:"georgia";
    font-style:italic;
    font-size:2.1em;
    color:#4d4d4d;
}

.main
{
    min-height:320px;
}

image

<script>
    $(document).ready(function () {
        $("#user_quotes").cycle({ fx: 'fade', pause: 1, speed: 900, timeout: 4000 })
    });
</script>
<div class="container">
    <div id = "user_quotes" class = "column span-24">
        @foreach (dynamic item in VidPub.Web.Models.Quotes.FromUsers()) {
            @Quotes.User(item)
        }
    </div>
</div>

image

then it cycles through – cool!

Rendering Cycle

image

 

_ViewStart.cshtml

Index.cshtml  (this is the view or view template).. so render view before layout!

_Layout.cshtml (top level component that loads css, js, footer etc..).. so two chunks of html already done are injected into @RenderBody method

then all put into a stringbuilder, then put into the ResponseStream and then we have a webpage!

Review

Partials..hmmm.. unless paired with modelBinder which he’s going to discuss later.

Helpers are simple and paired with functions are great.

| | # 
# Thursday, 22 September 2011
( MVC | VidPub )

MVC3 Model binders are cool – form post can be structued into class, or method arguments.

So we did have a test for the original ASP.NET Membership:

Not looking for finite results, however testing the behaviour of the controller itself.

[HttpPost]
        public ActionResult Register(RegisterModel model) {
            if (ModelState.IsValid) {
                // Attempt to register the user
                MembershipCreateStatus createStatus;
                Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);

                if (createStatus == MembershipCreateStatus.Success) {
                    FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
                    return RedirectToAction("Index", "Home");
                }
                else {
                    ModelState.AddModelError("", ErrorCodeToString(createStatus));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

This test fails… Robs method of solving was to abstract out

        [Test]
        public void register_action_should_redirect_with_valid_email_pass() {
            var controller = new AccountController();
            var result = controller.Register(new RegisterModel {
                Email = "test@test.com",
                ConfirmPassword = "password",
                Password = "password",
                UserName = "test"
            });
            // if everything is okay in Register then it will call RedirectToActon which is a RedirectToRouteResult
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
        }

am getting a createStatus of InvalidAnswer if I run in nunit

If I used the front end with exactly the same parameters it works fine.

Wiring up Custom Authentication

Changed AccountController from model binding

 public ActionResult Register(RegisterModel model) {

to

public ActionResult Register(string email, string password, string confirm) {
Either is fine, but this makes passing from our test easier:
[Test]
        public void register_action_should_redirect_with_valid_email_pass() {
            var controller = new AccountController();
            var result = controller.Register("test@test.com", "password", "password");
            // if everything is okay in Register then it will call RedirectToActon which is a RedirectToRouteResult
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
        }

have wired up our new Users class Register method to handle registration:

[HttpPost]
        public ActionResult Register(string email, string password, string confirm) {
            var result = _users.Register(email, password, confirm);

            if (result.Success) {
                FormsAuthentication.SetAuthCookie(email, false /* createPersistentCookie */);
                return RedirectToAction("Index", "Home");
            }
            else {
                ViewBag.Message = result.Message;
            }

            // If we got this far, something failed, redisplay form
            return View();
        }

so just hands it off to Users.Register:

// Have put in _db here instead of using base for simplicity.

    //public class Users : DynamicModel {
    public class Users {
        DynamicModel _db;
        public Users() {
            _db = new DynamicModel("Membership", "Users", "ID");
        }
        //public Users() : base("VidPub", "Users", "ID", "Email") { }

        public dynamic Register(string email, string password, string confirm) {
            dynamic result = new ExpandoObject();
            result.Success = false;
            if (email.Length >= 6 && password.Length >= 6 && password.Equals(confirm)) {
                try {
                    //result.UserID = this.Insert(new { Email = email, HashedPassword = Hash(password) });
                    result.UserID = _db.Insert(new { Email = email, HashedPassword = Hash(password) });
                    result.Success = true;
                    result.Message = "Thanks for signing up!";
                }
                catch (SqlException ex) {
                    result.Message = "This email already exists in our system";
                }
            }
            else {
                result.Message = "Please check your email and password - they're invalid";
            }
            return result;
        }
        public static string Hash(string userPassword) {
            return
                BitConverter.ToString(SHA1Managed.Create().ComputeHash(Encoding.Default.GetBytes(userPassword))).Replace
                    ("-", "");
        }

However test fails with a nullReferenceException in FormsAuthentication.SetAuthCookie

Mocking – Moq.. Failed.. then Abstract out worked!

Forms authentication requires that certain classes and properties are setup just so for it to work.

Just trying to set a cookie that has some encrypted data of user information.

Need to fool the framework..

Substituting own classes for what forms authentication needs:

public class TestBase {

        public void SetContext(Controller controller) {

            var context = new Mock<HttpContextBase>();
            var request = new Mock<HttpRequestBase>();
            var response = new Mock<HttpResponseBase>();
            var session = new Mock<HttpSessionStateBase>();
            var server = new Mock<HttpServerUtilityBase>();
            var user = new GenericPrincipal(new GenericIdentity("test"), new string[0]);

            context.Setup(ctx => ctx.Request).Returns(request.Object);
            context.Setup(ctx => ctx.Response).Returns(response.Object);
            context.Setup(ctx => ctx.Session).Returns(session.Object);
            context.Setup(ctx => ctx.Server).Returns(server.Object);
            context.Setup(ctx => ctx.User).Returns(user);

            request.Setup(r => r.Cookies).Returns(new HttpCookieCollection());
            request.Setup(r => r.Form).Returns(new NameValueCollection());
            request.Setup(q => q.QueryString).Returns(new NameValueCollection());
            response.Setup(r => r.Cookies).Returns(new HttpCookieCollection());

            var rctx = new RequestContext(context.Object, new RouteData());
            controller.ControllerContext = new ControllerContext(rctx, controller);

        }

But this didn’t work.. so he’s going with an abstraction, so can test against this interface using a Fake.

  • Set client access
  • Remove client access
  • Get the token
public class FormsAuthTokenStore : ITokenHandler {

        public void SetClientAccess(string token) {
            FormsAuthentication.SetAuthCookie(token, true);
        }

        public void RemoveClientAccess() {
            FormsAuthentication.SignOut();
        }

        public string GetToken() {
            return FormsAuthentication.Decrypt(HttpContext.Current.Response.Cookies["auth"].Value).ToString();
        }
    }

    public interface ITokenHandler {
        string GetToken();
        void RemoveClientAccess();
        void SetClientAccess(string token);
    }

the Fake:

public class FakeTokenStore : ITokenHandler {
        string _token;

        public string GetToken() {
            return _token;
        }

        public void RemoveClientAccess() {
            _token = "";
        }

        public void SetClientAccess(string token) {
            _token = token;
        }
    }

however MVC app won’t run now as it doesn’t know about tokenStore, and the framework will trying and inject something in for us.

public class AccountController : Controller {
        Users _users;
        ITokenHandler _tokenStore;

        public AccountController(ITokenHandler tokenStore) {
            _users = new Users();
            _tokenStore = tokenStore;
        }

We’ve already got Ninject working, so lets just wire that up.

private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<ILogger>().To<NLogger>();
            kernel.Bind<ITokenHandler>().To<FormsAuthTokenStore>();
        }

Custom functionality: Only want to allow 1 person with an account to be logged in at a given time ie single session requirement

The token is going to be a Guid.

Simulate 2 different users logging in at the same time.

ApplicationController – borrowing concept from Rails… all controllers inherit off ApplicationController, which provides an IsLoggedIn public property.

Change AccountController to inherit from ApplicationController… just putting a class in the middle.

taking the _tokenStore stuff out of AccountController and into it’s base class.

[Test]
        public void isloggedin_returns_false_for_first_user_on_dual_login() {
            // we'll need two controller instances here
            // and a way to see if the user is logged in
            _controller.Register("test@test.com", "password", "password");
            var result = _controller.LogOn("test@test.com", "password");
            Assert.True(_controller.IsLoggedIn);

            var c2 = new AccountController(new FakeTokenStore());
            c2.LogOn("test@test.com", "password");

            //see if the first controller instance returns true for logged on
            Assert.False(_controller.IsLoggedIn);
        }

So first user is logged out if second user comes in with same credentials.

GIT

image

cd ..

git status

git commit –am “got token based authentication working

git checkout master

git merge membership – merges the membership branch with the master

git push origin master

git checkout membership  - switch back to membership branch.

Review

Testing – started with it.. good

Logon

Registration

But framework got in the way.

SQLCE was good (not for me)

Massive was good

A beautiful application: “Reads well, simple to understand, and gets the job done”

image

Rob doesn’t like this as lots of repetition.. and why not have it expressed more simply ie in the Users class.

if (email.Length >= 6 && password.Length >= 6 && password.Equals(confirm)) {

A rough sketch of where we are now

Image0420

image

| | # 
# Tuesday, 20 September 2011
( Git | Glimpse | MVC | Ninject | NLog | VidPub )

Notes from TekPub ASP.NET MVC3 series of tutorials.

  • Startup – big idea.. charge people money for
  • Good idea, well executed
  • Get site out there and pay the bills!

Why MVC3 in this Fast Paced Startup?

  • Good stable version of MVC
  • I know C#

What are we doing (Elevator pitch) and when are we going to do it?

1 sentence!

Platform and Tools

MVC3

Unfuddle for project mgt

image

or agilezen (kanban)

Source is on http://github.com/tekpub/mvc3

Setup a new MVC3 solution called VidPub.Web

image

Lib is for external dlls.  Docs are for docs.  Everything is under git

git init in \dev\VidPub

git add .

git commit –am “Initial load”

DropBox

mkdir e:\dropbox\repositories\vpub

image

in e:\dropbox\repositories\vpub

git init –bare

from \dev folder  git push origin master

image

So other people can then pull from that repository… it is the whole repo up there, not just source.

Test Project

Have just created a container.

Authentication

Don’t want concurrent authentcation.. ie users sharing accounts.  Could use token based.

Now, 1 Year, 3 Years

ASP.NET Membership – complicated..

Consistency of FileNames

Public\javscripts

Public\stylesheets

no more content directory

Model\AccountModels.cs to AccountModel

change CSS path in _Layout.cshtml

CDN

http://code.google.com/apis/libraries/devguide.html#jquery

image

make faster for user as they may have this cached in their browser.  Also save us a bit of bandwidth.  Cache up to a year.

If using Azure to host http://www.microsoft.com/windowsazure/msdn-benefits/ MSDN have got some benefits.

Logging

NLog using NuGet

Code Snippet
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

  <targets>
    <!--Useful for debugging-->
    <target name="console" xsi:type="ColoredConsole"
     layout="${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}" />

    <target name="file" xsi:type="File" fileName="${basedir}/App_Data//logs/site.log"
     layout="${date}: ${message}" />

    <target name="eventlog" xsi:type="EventLog" source="My App" log="Application"
    layout="${date}: ${message} ${stacktrace}" />

  </targets>

  <rules>

    <logger name="*" minlevel="Info" writeTo="file" />
    <logger name="*" minlevel="Fatal" writeTo="eventlog" />

  </rules>

</nlog>

in dev we’re going to log everything to a file in App_Data/logs

In \Infrastructure we’ve got some code to help us log with an interface extracted.

Code Snippet
public class NLogger : VidPub.Web.Infrastructure.Logging.ILogger {
        Logger _logger;
        public NLogger() {
            _logger = LogManager.GetCurrentClassLogger();
        }
        public void LogInfo(string message) {
            _logger.Info(message);
        }

        public void LogWarning(string message) {
            _logger.Warn(message);
        }

        public void LogDebug(string message) {
            _logger.Debug(message);
        }

        public void LogError(string message) {
            _logger.Error(message);
        }
        public void LogError(Exception x) {
            LogError(BuildExceptionMessage(x));
        }
        public void LogFatal(string message) {
            _logger.Fatal(message);
        }
        public void LogFatal(Exception x) {
            LogFatal(BuildExceptionMessage(x));
        }
        string BuildExceptionMessage(Exception x) {

            Exception logException = x;
            if (x.InnerException != null)
                logException = x.InnerException;

            string strErrorMsg = Environment.NewLine + "Error in Path :" + System.Web.HttpContext.Current.Request.Path;

            // Get the QueryString along with the Virtual Path
            strErrorMsg += Environment.NewLine + "Raw Url :" + System.Web.HttpContext.Current.Request.RawUrl;


            // Get the error message
            strErrorMsg += Environment.NewLine + "Message :" + logException.Message;

            // Source of the message
            strErrorMsg += Environment.NewLine + "Source :" + logException.Source;

            // Stack Trace of the error

            strErrorMsg += Environment.NewLine + "Stack Trace :" + logException.StackTrace;

            // Method where the error occurred
            strErrorMsg += Environment.NewLine + "TargetSite :" + logException.TargetSite;
            return strErrorMsg;
        }
    }

IoC

So we don’t have anything coupled tightly right from the start (eg logging which we’re doing next).. lets use IoC

Ninject.MVC3

Wire up in global.asax

 public class MvcApplication : NinjectHttpApplication  {

**NO this is not correct as in MVC3 it is bootstrapped, so just leave as:

public class MvcApplication : System.Web.HttpApplication {

Setting up logging in App_Start bootstrapper file.

“Every time you see a request for ILogger interface in a controller, return a new NLogger class”

 

kernel.Bind<ILogger>().To<NLogger>();

added in Logs/Site.log into our project.

Glimpse

Added in bookmarks into bar to turn on and off.

image

git add .

git commit –am “Added logging, IoC and rearranged stuff”

git push origin master (to save to dropbox)

 

current state of play of our filesystem.

image

| | # 
# Saturday, 16 July 2011
( MVC )

From Josh Holmes at WebMatrix event in Ireland (Part 4 of the video)

Add library package reference on references (nuget)

Using dependency injection framework Ninject.MVC3.. which leverages inside MVC3

Moq for mocking

 

Package manager console (next to output at bottom of screen)…powershell in Visual Studio

Install-package MVCScaffolding

scaffold controller product

Unobtrusive jquery for validation

| | # 
# Tuesday, 26 April 2011
( MVC )
If you want an Index and Index/guid
 
//public ActionResult Index()
//{
// //Guid eventId = id;
// //ViewBag.EventName = uow.Events.Single(e => e.Id == id).Name;
// return View(uow.Races.OrderBy(r => r.Name).ToList());
//}

//[RequireRequestValue("ideventguid")]
//[RequireRequestValue("id")]
public ActionResult Index(Guid idEvent)
{
ViewBag.EventName = uow.Events.Single(e => e.Id == idEvent).Name;
return View(uow.Races.OrderBy(r => r.Name).Where(r => r.EventU.Id == idEvent));
}

| | # 
# Friday, 08 April 2011
( LightSpeed | MVC )

Thanks to Sangsu for this http://stackoverflow.com/questions/5417220/change-ordering-in-mvc3-index-view

image

This is a simple implementation (not using a Grid):

public ActionResult Index(string sortColumn, bool? asc)
{
if (string.IsNullOrWhiteSpace(sortColumn))
sortColumn = "Name";

//if (!asc.HasValue)
// asc = true;

asc = asc ?? true; // coalesce does same as above


//if (sortColumn == "Name")
//{
// if (asc.Value)
// query = query.OrderBy(a => a.Name);
// else
// query = query.OrderByDescending(a => a.Name);
//}

//if (sortColumn == "IsEasternCountry")
//{
// if (asc.Value)
// query = query.OrderBy(a => a.IsEasternCountry);
// else
// query = query.OrderByDescending(a => a.IsEasternCountry);
//}

ViewBag.sortColumn = sortColumn;
ViewBag.asc = asc.Value;

// Use dynamic column names for sort order
Order order = Order.By(sortColumn);
if (!asc.Value)
order = order.Descending();

IList<Country> listOfCountries = uow.Find<Country>(new Query { Order = order});

return View(listOfCountries);
}


Notice the Lightspeed goodness.. have commented out the code it replaced! Thanks to Ivan http://stackoverflow.com/questions/5423728/mvccontrib-grid-sorting and the for coalesce tip above

View:

<table>
<tr>
<th></th>
<th>
@Html.ActionLinkWithColumnOrder("Name", "Index", (string)ViewBag.sortColumn, (bool)ViewBag.asc)
</th>
<th>
@Html.ActionLinkWithColumnOrder("IsEasternCountry", "Index", (string)ViewBag.sortColumn, (bool)ViewBag.asc)
</th>
<th>
IsWesternCountry
</th>
</tr>

@foreach (var item in Model) {
<tr>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
@Html.ActionLink("Details", "Details", new { id=item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
<td>
@item.Name
</td>
<td>
@item.IsEasternCountry
</td>
<td>
@item.IsWesternCountry
</td>
</tr>

and a helper:

public static class HtmlExtensions
{
public static MvcHtmlString ActionLinkWithColumnOrder(this HtmlHelper helper, string columnName, string action, string currentColumn, bool currentOrder)
{
object routeValues;
object htmlAttributes = null;
if (columnName == currentColumn)
{
routeValues = new { sortColumn = columnName, asc = !currentOrder };
htmlAttributes = new { @class = currentOrder ? "sort_asc" : "sort_desc" };
}
else
{
routeValues = new { sortColumn = columnName };
}

return helper.ActionLink(columnName, action, routeValues, htmlAttributes);
}
}

| | # 
# Thursday, 31 March 2011
( MVC )

http://www.joe-stevens.com/2010/02/17/asp-net-mvc-using-controller-updatemodel-when-using-a-viewmodel/

Great so you don’t have to manually set properties in an update method.

| | # 
# Saturday, 19 March 2011
( MVC | Razor )

ValidationSummary(false)

image

View (no javascript)

@model datePickerTemp2.Models.PersonEditModel

@Model.MessageToDisplay

@using (Html.BeginForm()) {
@Html.ValidationSummary(false)
<fieldset>
<legend>Person</legend>

<div class="editor-label">
@Html.LabelFor(m => m.Person.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(m => m.Person.FirstName)
@Html.ValidationMessageFor(m => m.Person.FirstName, "You must enter a First Name")
</div>

Model:

public class Person
{
[Required]
public string FirstName { get; set; }
[DataType(DataType.Date)]
public DateTime DateOfBirth { get; set; }
}

View (javascript)

@model datePickerTemp2.Models.PersonEditModel

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@Model.MessageToDisplay

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>

<div class="editor-label">
@Html.LabelFor(m => m.Person.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(m => m.Person.FirstName)
@Html.ValidationMessageFor(m => m.Person.FirstName, "You must enter a First Name")
</div>

<div class="editor-label">
@Html.LabelFor(m => m.Person.DateOfBirth)
</div>
<div class="editor-field">
@Html.EditorFor(m => m.Person.DateOfBirth)
@Html.ValidationMessageFor(m => m.Person.DateOfBirth)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
 
ValidationSummary(true)

image

There will be no postback if a firstname is blank.  DateOfBirth uses postback, however you have to have FirstName passing before it will actually postback.

| | # 
( MVC | Razor )

From http://blogs.msdn.com/b/stuartleeks/archive/2011/01/25/asp-net-mvc-3-integrating-with-the-jquery-ui-date-picker-and-adding-a-jquery-validate-date-range-validator.aspx

When implementing remember to add custom attribute to the correct property in the model:

DateType(DateType.Date)

and add a reference to DataAnnotations - System.ComponentModel.DataAnnotations

View

<div class="editor-label">
@Html.LabelFor(m => m.Person.DateOfBirth)
</div>
<div class="editor-field">
@Html.EditorFor(m => m.Person.DateOfBirth)
@Html.ValidationMessageFor(m => m.Person.DateOfBirth)
</div>

Use templated helpers to automatically wire up the date picker whenever it renders an editor for a date (ie a property annotated with DataType.Date).

If you add this annotation MVC3 will look for a partial view

Partial View - \EditorTemplates\Date.cshtml

@model DateTime
@Html.TextBox("", Model.ToString("dd/MM/yyyy"), new { @class = "date" }) **TODO Wire up
 
So when we render now:
 
image
The error you are seeing is because javascript validation is turned on, and its is getting confused with UK/US datetimes.

_Layout.cshtml

<script src="@Url.Content("~/Scripts/jquery-ui.js")" type="text/javascript"></script>
<link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" />
 
<script src="@Url.Content("~/Scripts/EditorHookup.js")" type="text/javascript"></script>

Scripts/EditorHookup.js

/// <reference path="jquery-1.4.4.js" />
/// <reference path="jquery-ui.js" />

$(document).ready(function () {
$('.date').datepicker({dateFormat: "dd/mm/yy"});
});

image

DateRange

image

public class Person
{
[Required]
public string FirstName { get; set; }
[DataType(DataType.Date)]
[DateRange("2010/12/01", "2010/12/16")]
public DateTime DateOfBirth { get; set; }
}

DateRange is simply:

public class DateRangeAttribute : ValidationAttribute
{
private const string DateFormat = "yyyy/MM/dd";
private const string DefaultErrorMessage = "'{0}' must be a date between {1:d} and {2:d}.";

public DateTime MinDate { get; set; }
public DateTime MaxDate { get; set; }

public DateRangeAttribute(string minDate, string maxDate)
: base(DefaultErrorMessage)
{
MinDate = ParseDate(minDate);
MaxDate = ParseDate(maxDate);
}

public override bool IsValid(object value)
{
if (value == null || !(value is DateTime))
{
return true;
}
DateTime dateValue = (DateTime)value;
return MinDate <= dateValue && dateValue <= MaxDate;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
name, MinDate, MaxDate);
}

private static DateTime ParseDate(string dateValue)
{
return DateTime.ParseExact(dateValue, DateFormat, CultureInfo.InvariantCulture);
}
}

| | # 
# Wednesday, 16 March 2011
( LightSpeed | MVC )

Html.DropDownListFor – haven’t figured this out yet

http://stackoverflow.com/questions/1297399/populating-asp-net-mvc-dropdownlist

MvcScaffold does it – however a bit obscure

ViewBag

//
// GET: /People/Create

public ActionResult Create()
{
ViewBag.Countries = uow.Countries.OrderBy(c => c.Name).ToList();
var person = new Person();
return View(person);
}
 
and in the view:
 
<div class="editor-field">
@Html.DropDownList("CountryId",
new SelectList(ViewBag.Countries as System.Collections.IEnumerable,
"Id", "Name", Model.CountryId))
</div>

image
The FK relation is Person.CountryId
 
However in LightSpeed all the PK’s are called .Id
| | # 
( LightSpeed | MVC )

To make is easy to save, edit etc..

Found that the example in FilmFestival doesn’t work in MVC3

To get it compiling I used:

//var bindResult = bindingContext.ValueProvider[fieldName];
var bindResult = bindingContext.ValueProvider.GetValue(fieldName);

But then it failed when I added in:

EntityModelBinder.Register(typeof(MvcPvAdmin.Models.LightSpeedModel1UnitOfWork).Assembly);

in Global.asax  it bombed in BindModel.
| | # 
( LightSpeed | MVC )

A very simple example using People, full CRUD:

image

Identity is handled by the DB

image

public class PeopleController : Controller
{
LightSpeedContext<LightSpeedModel1UnitOfWork> _context = new LightSpeedContext<LightSpeedModel1UnitOfWork>("Default");

//
// GET: /People/
public ActionResult Index()
{
using (var uow = _context.CreateUnitOfWork())
{
return View(uow.People.ToList());
}
}

//
// GET: /People/Details/5

public ActionResult Details(int id)
{
using (var uow = _context.CreateUnitOfWork())
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}
}

//
// GET: /People/Create

public ActionResult Create()
{
return View();
}

//
// POST: /People/Create

[HttpPost]
public ActionResult Create(Person person)
{
using (var uow = _context.CreateUnitOfWork())
{
if (!ModelState.IsValid)
return View(person);

uow.Add(person);
uow.SaveChanges();
return RedirectToAction("Index");
}
}

//
// GET: /People/Edit/5

public ActionResult Edit(int id)
{
using (var uow = _context.CreateUnitOfWork())
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}
}

//
// POST: /People/Edit/5

[HttpPost]
public ActionResult Edit(Person person, int id)
{
if (ModelState.IsValid)
{
using (var uow = _context.CreateUnitOfWork())
{
Person personToUpdateFromDb = uow.People.Single(p => p.Id == id);

personToUpdateFromDb.Name = person.Name;
personToUpdateFromDb.Age = person.Age;
personToUpdateFromDb.CountryId = person.CountryId;

uow.SaveChanges();
}

return RedirectToAction("Index");
}
return RedirectToAction("Index");
}

//
// GET: /People/Delete/5

public ActionResult Delete(int id)
{
using (var uow = _context.CreateUnitOfWork())
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}
}

//
// POST: /People/Delete/5

[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
using (var uow = _context.CreateUnitOfWork())
{
Person personToDelete = uow.People.Single(p => p.Id == id);
uow.Remove(personToDelete);
uow.SaveChanges();
}
return RedirectToAction("Index");
}

Validation

image

Name is not nullable, and must by less or equal to 5 in length

Age is nullable.

image

No code required to get this working.

Abstracting using statement

In Global.asax

internal static readonly LightSpeedContext<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork> LightSpeedDataContext = new LightSpeedContext<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork>("Default");

and a helper base class for controllers:

public class MvcPvAdminController : Controller
{
private const string UNIT_OF_WORK = "__UnitOfWork__";
private UnitOfWorkScopeBase<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork> _unitOfWorkScope;

public UnitOfWorkScopeBase<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork> UnitOfWorkScope
{
get
{
if (_unitOfWorkScope == null && System.Web.HttpContext.Current != null)
{
_unitOfWorkScope = System.Web.HttpContext.Current.Items[UNIT_OF_WORK] as PerRequestUnitOfWorkScope<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork>;
}

if (_unitOfWorkScope == null)
{
_unitOfWorkScope = new PerRequestUnitOfWorkScope<MvcPvAdmin.Models.LightSpeedModel1UnitOfWork>(MvcApplication.LightSpeedDataContext);

if (System.Web.HttpContext.Current != null)
{
System.Web.HttpContext.Current.Items[UNIT_OF_WORK] = _unitOfWorkScope;
}
}

return _unitOfWorkScope;
}
}

protected MvcPvAdmin.Models.LightSpeedModel1UnitOfWork uow
{
get { return UnitOfWorkScope.Current; }
}

protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (_unitOfWorkScope != null)
{
_unitOfWorkScope.Dispose();
}

base.OnResultExecuted(filterContext);
}

public new void Dispose()
{
if (_unitOfWorkScope != null)
{
_unitOfWorkScope.Dispose();
}

base.Dispose();
}

so here is my newly updates Controller:

public class PeopleController : MvcPvAdminController
{
//
// GET: /People/
public ActionResult Index()
{
return View(uow.People.ToList());
}

//
// GET: /People/Details/5

public ActionResult Details(int id)
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}

//
// GET: /People/Create

public ActionResult Create()
{
return View();
}

//
// POST: /People/Create

[HttpPost]
public ActionResult Create(Person person)
{
if (!ModelState.IsValid)
return View(person);

uow.Add(person);
uow.SaveChanges();
return RedirectToAction("Index");
}

//
// GET: /People/Edit/5

public ActionResult Edit(int id)
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}

//
// POST: /People/Edit/5

[HttpPost]
public ActionResult Edit(Person person, int id)
{
if (!ModelState.IsValid)
return View(person);

Person personToUpdateFromDb = uow.People.Single(p => p.Id == id);

personToUpdateFromDb.Name = person.Name;
personToUpdateFromDb.Age = person.Age;
personToUpdateFromDb.CountryId = person.CountryId;

uow.SaveChanges();
return RedirectToAction("Index");
}

//
// GET: /People/Delete/5

public ActionResult Delete(int id)
{
Person person = uow.People.Single(p => p.Id == id);
return View(person);
}

//
// POST: /People/Delete/5

[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
Person personToDelete = uow.People.Single(p => p.Id == id);
uow.Remove(personToDelete);
uow.SaveChanges();
return RedirectToAction("Index");
}
}

| | # 
# Tuesday, 15 March 2011
( LightSpeed | MVC )

d/load 3.1 version of LightSpeed.  In the samples directory there is an MVC Film example.  Use vs2008.

http://www.mindscapehq.com/staff/jeremy/index.php/2009/03/aspnet-mvc-part1/

http://www.mindscapehq.com/staff/jeremy/index.php/2009/03/aspnet-mvc-part2/

http://www.mindscapehq.com/staff/jeremy/index.php/2009/03/aspnet-mvc-part3/ – setting up the site

http://www.mindscapehq.com/staff/jeremy/index.php/2009/03/aspnet-mvc-part4/ – model binding

Model namespace is:  Filmfestival.Model

Testing

Using NUnit.  A TestFixture base class to setup the testing.  TestDriven.NET as a test runner.

Managing the LightSpeed Unit of Work on a Per Request basis

Sets up a base class for managing.

Home View

Pass a ViewModel

image

public class HomePageViewModel
{
public IList<Film> Films { get; set; }
public IList<Location> Locations { get; set; }
public Film FeaturedFilm { get; set; }
}

then

IList<Film> films = UnitOfWork.Films.ToList().OrderBy(o => Guid.NewGuid()).Take(8).ToList();

HomePageViewModel model = new HomePageViewModel()
{
Locations = UnitOfWork.Locations.ToList(),
Films = films,
FeaturedFilm = films[0]
};

return View(model)

Model Binder

/home

/home/location/wellington

/home/location/christchurch

/film/new

ModelBinder is setup in global.asax

EntityModelBinder.Register(typeof(FilmFestival.Model.UnitOfWork).Assembly);

Validation

image

This is done server side here using the LS model.

| | # 
# Monday, 14 March 2011

Downloaded EF CTP5 http://blogs.msdn.com/b/adonet/archive/2010/12/14/ef-feature-ctp5-released.aspx

Created a database and a model in EF

image

Added DbContext Generator (right click inside edmx file)

image

scaffold controller people –Force

Comment out generated dbcontext

Change peoplecontroller to point to new entities

image

Problem here is that FirstName is not required

image

yet in the model it is not nullable.

I can fix it by going to:

namespace EFTest.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

public partial class Person
{
// Primitive properties

public int PersonID { get; set; }
public int Age { get; set; }
public System.DateTime DateOfBirth { get; set; }
[Required]
public string FirstName { get; set; }

}
and adding in a Required field using DataAnnotations.

| | # 

Using VS2010. Create a model. 

image

this also puts in an entry into web.config

Create a controller

[HandleError]
public class CarsController : Controller
{

eftestEntities _db;

public CarsController()
{
_db = new eftestEntities();
}

//
// GET: /Cars/

public ActionResult Index()
{
var cars = _db.Cars.ToList();
return View(cars);
}

then right click and create a view:

image

I had to put data in the db, and only view works.

| | # 
# Friday, 11 March 2011

http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx

http://weblogs.asp.net/scottgu/archive/2010/08/03/using-ef-code-first-with-an-existing-database.aspx

Install-Package EFCodeFirst

Starting with a really simple example:

public class Dinner
{
public int DinnerID { get; set; }
public string Title { get; set; }
public DateTime EventDate { get; set; }
public string Address { get; set; }
public string HostedBy { get; set; }

then in DinnerController:

public class DinnerController : Controller
{
NerdDinnerReloadedContext context = new NerdDinnerReloadedContext();

//
// GET: /Dinner/

public ActionResult Index()
{
return View(context.Dinners.ToList());
}

//
// GET: /Dinner/Details/5

public ActionResult Details(int id)
{
return View();
}

//
// GET: /Dinner/Create

public ActionResult Create()
{
return View();
}

//
// POST: /Dinner/Create

[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
context.Dinners.Add(dinner);
context.SaveChanges();
return RedirectToAction("Index");
}

return View(dinner);
}

and the context:

public class NerdDinnerReloadedContext : DbContext
{
public DbSet<Dinner> Dinners { get; set; }

Very simple… and this does work, and will generate a database

image

Existing Database - Model First

Here I am defining my model

image

then wiring it up to the existing database

image

EF Code first uses a convention where context classes by default look for a connection string that has the same name as the context class.

image

The type 'MvcApplication1.Models.Cake' is not a supported entity type.

Possible helper: http://mvcscaffolding.codeplex.com/discussions/247163

| | # 
# Thursday, 10 March 2011
( MVC )

http://www.mvcconf.com/

http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/

http://blog.stevensanderson.com/2011/01/13/mvcscaffolding-standard-usage/

 

install-package MvcScaffolding

scaffold controller car

image

This uses EFCodeFirst (installed as a dependency of MvcScaffolding)

using System.ComponentModel.DataAnnotations;

namespace ScaffoldDemo.Models
{
public class Car
{
public int CarID { get; set; }
[Required]
public string Make { get; set; }
[Required]
public string Model { get; set; }
public int Year { get; set; }
}

Imported DataAnnotations and made 2 some required.

Repository Pattern

Currently controller code is directly coupled to the database.  Not good for unit testing.

public class CarsController : Controller
{
private ScaffoldDemoContext context = new ScaffoldDemoContext();

//
// GET: /Car/

public ViewResult Index()
{
return View(context.Cars.ToList());
}

Scaffold Controller Car –Repository –Force

image

So now we could unit test by passing a mock car repository into our controller.

image

People

public class Person
{
public int PersonId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string EmailAddress { get; set; }
}

Scaffold Controller Person –Repository

This makes the controller, views and updates the context.

image

1 Person has Many Cars

add in a PersonId field into car

image

Scaffold Controller Car -Repository –Force

Scaffold Controller Person -Repository –Force

Add a few people into /People, then in /Cars

image

Better way which shows more data:

public class Car
{
public int CarID { get; set; }
[Required]
public string Make { get; set; }
[Required]
public string Model { get; set; }
public int Year { get; set; }

public int PersonId { get; set; }

// so we can get the name of the person when showing the car
// 1 person can have many cars
public virtual Person Person { get; set; }
}

public class Person
{
public int PersonId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string EmailAddress { get; set; }

// 1 person can have many cars.
// virtual for EF eager load
public virtual ICollection<Car> Cars { get; set; }
}

image

image

How did it know to display Person.Name?  There is a convention eg Name, Subject etc..

T4 Templating

image

He implements a grid.

| | # 
( MVC )

Had to install Powershell 2 (running XP).

install-package EFCodeFirst.SqlServerCompact   (just need to do tab after EF to get code completion)

image

image

Using static so don’t have to new up the ComicDemo here:

This will add 3 new comics each time /home/showcomics is called!

image

image

MVCScaffolding

install-package MvcScaffolding

scaffold controller comicbook

image

What his did:

Scaffolding ComicBooksController...
Added controller 'Controllers\ComicBooksController.cs'
Added database context 'Models\MvcApplication2Context.cs'
Added 'ComicBooks' to database context 'Models\MvcApplication2Context.cs'
Added Create view at 'Views\ComicBooks\Create.cshtml'
Added Edit view at 'Views\ComicBooks\Edit.cshtml'
Added Delete view at 'Views\ComicBooks\Delete.cshtml'
Added Details view at 'Views\ComicBooks\Details.cshtml'
Added Index view at 'Views\ComicBooks\Index.cshtml'
Added _CreateOrEdit view at 'Views\ComicBooks\_CreateOrEdit.cshtml'

So have full working CRUD on 1 class – ComicBook.

| | # 
( Azure | MVC )

As there is no template currently for MVC and Azure on 1.3:

http://blog.smarx.com/posts/asp-net-mvc-in-windows-azure

http://www.jimandkatrin.com/CodeBlog/post/MVC3-in-Azure.aspx – Logging too

http://nerdwords.blogspot.com/2011/02/setting-up-mvc-3-for-azure-with_27.html

image

The trick to getting it working on Azure was to populate the database first.  If not then a it gave a very generic error.

| | # 
( MVC )

http://mvcmusicstore.codeplex.com/

http://www.asp.net/mvc/tutorials/mvc-music-store-part-1

image

Enumerating a List

public class StoreController : Controller
{
//
// GET: /Store/
public ActionResult Index()
{
var genres = new List<Genre> {
new Genre { Name = "Disco" },
new Genre { Name = "Jazz" },
new Genre { Name = "Rock" }
};

return View(genres);
}
 
Then in the razor:
 
@model IEnumerable<MvcMusicStore.Models.Genre>

@{
ViewBag.Title = "Store";
}

<h3>Browse Genres</h3>
<p>Select from @Model.Count() genres:</p>
<ul>@foreach (var genre in Model)
{
<li>@genre.Name</li>
}
</ul>

Very nice
image

Hyperlinks

This works, but if we renamed our controller then it would break.
<ul>@foreach (var genre in Model)
{
<li><a href="/Store/Browse?genre=@genre.Name"@genre.Name&lt;/a></li>
}
</ul>
 

Query Result Shaping

When /Store/Browse?genre=Rock
image
// GET: /Store/Browse?genre=Disco
public ActionResult Browse(string genre)
{
// Retrieve Genre and its associated albums from database
//var genreModel = storeDB.Genres.Include("Albums").Single(g => g.Name == genre);
var genreModel = storeDB.Genres.Single(g => g.Name == genre);
return View(genreModel);
}

I’m just displaying the name of the entity passed to the view, which is Rock.
<h2>
Browsing Genre: @Model.Name</h2>

We can take advantage of EF’s feature to get other related entities ie Albums.  This allows us to pre fetch the albums.  Eager loading.
 
 
 
 


ActionLink() makes it easy to build <a> links and takes care url path encoding.
 
 

Interesting Truncate method for HtmlHelper

Dropdowns (Html.DropDownList)

Role based authrorization on admin

ViewModels (page 108)

Ajax.ActionLink (instead of Html.ActionLink) to remove items from cart.

Problem with web.config

Account creation was unsuccessful. Please correct the errors and try again.

  • The password retrieval answer provided is invalid. Please check the value and try again.

http://mvcmusicstore.codeplex.com/discussions/233520

Strange issue, as I’m sure it worked yesterday.  This may not be quite right, but gets it working.

<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=152368
-->

<configuration>
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />

<add name="MusicStoreEntities"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MvcMusicStore.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>

<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>

<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>

<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
</profile>

<roleManager enabled="true">
<providers>
<clear/>
<add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>

<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>

<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

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

</configuration>

Images from the database

http://forums.asp.net/p/1652452/4299847.aspx/1?How+to+load+image+from+database+in+mvcmusicstore+sample

| | # 
( MVC )

http://www.pluralsight-training.net/microsoft/players/PSODPlayer.aspx?author=scott-allen&name=mvc3-introduction&mode=live&clip=0&course=aspdotnet-mvc3

image

// id is in routes somewhere so mvc knows already to pass it through.
public ActionResult Hello(string id)
{
return Content(string.Format("Hello world {0}", id));
}
Query String

image

public ActionResult Hello(string name)
{
return Content(string.Format("Hello world {0}", name));
}

| | # 
( MVC | Razor )
ViewBag

Convenient late-bound way to pass bits of information to a view.

// GET: /HelloWorld/Welcome
public ActionResult Welcome(string name, int numTimes = 1)
{
ViewBag.Message = "Hello " + name;
ViewBag.NumTimes = numTimes;
return View();
}
 
String Formatting in Razor
<td>
@String.Format("{0:d}", item.ReleaseDate)
</td>
<td>
@item.Genre
</td>
<td>
@String.Format("{0:c}", item.Price)
</td>

Date {0:g} to {0:d} (that is, from general date to short date).

Price property from {0:F} to {0:c} (from float to currency).

Keyboard Shortcuts

Don’t seem to work.. however Right Click keyboard next to space, then go to Controller is useful.

Ctrl M Ctrl C – Add Controller

Ctrl M Ctl V – Add View

 

HTML Helper Methods

Html.LabelFor – displays the name of the field

Html.EditorFor – displays an input element where the user can enter a value.

Html.ValidationMessageFor

<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>

<div class="editor-label">
@Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>

Posting

In the controller:

[HttpPost]
public ActionResult Create(string title, DateTime releaseDate, string genre, decimal price)
{

or

[HttpPost]
public ActionResult Create(Movie newMovie)
{
if (ModelState.IsValid)
{
db.Movies.Add(newMovie);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return View(newMovie);
}
}

MovieInitializer

If the model changes in the code, this provides a way to recreate the db put in sample data.

public class MovieInitializer : DropCreateDatabaseIfModelChanges<MovieDBContext>
{
protected override void Seed(MovieDBContext context)
{
var movies = new List<Movie> {

new Movie { Title = "When Harry Met Sally",
ReleaseDate=DateTime.Parse("1989-1-11"),
Genre="Romantic Comedy",
Rating="R",
Price=7.00M},

new Movie { Title = "Ghostbusters 2",
ReleaseDate=DateTime.Parse("1986-2-23"),
Genre="Comedy",
Rating="R",
Price=9.00M},
};

movies.ForEach(d => context.Movies.Add(d));
}
}

then wire up:

protected void Application_Start()
{
DbDatabase.SetInitializer<MovieDBContext>(new MovieInitializer());

AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}

Setting Precision in DB
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Movie>().Property(p => p.Price).HasPrecision(18, 2);
}
}
image

Validation

Add to the Model and then it is enforced everywhere in the app.

System.ComponentModel.Validation – can apply to any class or property

[Required(ErrorMessage = "Genre must be specified")]
public string Genre { get; set; }

[Required(ErrorMessage = "Price Required")]
[Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
public decimal Price { get; set; }

[StringLength(5)]
public string Rating { get; set; }

EF will enforce too as well as the front end – both Javascript,(jquery.validate?) and serverside.

image

Update
[HttpPost]
public ActionResult Edit(Movie model)
{
try
{
var movie = db.Movies.Find(model.ID);

UpdateModel(movie);
db.SaveChanges();
return RedirectToAction("Details", new { id = model.ID });
}
catch (Exception)
{
ModelState.AddModelError("", "Edit Failure, see inner exception");
}

return View(model);
}

UpdateModel(movie) copies the edited data in model into the movie in the db.

Cloudifying

image

The database had been created in SQLServerExpress.  I then added this user as an owner / admin for the whole system (dev only!)

| | # 
# Friday, 17 July 2009
A simple MVC (Model View Controller) implementation..code by Jimmy Chandra
http://stackoverflow.com/questions/1107720/mvc-c-simplest-possible-implementation

Why use MVC?  Similar reasons to MVP - testability and seperation of concerns

Simple.MVC.zip (167.25 KB)



1. Program.cs runs, running Main.. as is standard in a Win Forms app.  Difference from MVP is that main here is instantiating and running a controller class first of all, instead of newing up the view, which calls the controller/presenter.
static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            TopEmployeeController controller = new TopEmployeeController();
            Application.Run(controller.View as Form);
        }
2. Controller is instantiated and constructor called:
        private ITopEmployeeView _view;
        private Employee _employee;

        private bool _monitoring;

        public TopEmployeeController()
        {
            _employee = new Employee();
            _view = new TopEmployeeForm(this, _employee);
        }
3. Controller news up an employee (dumb).. then a view of type TopEmployeeForm, passing its self (the controller) and employee to it.

4. View (the observer) subscribes to the model (the subject) so that when model.OnPropertyChange is called, view.UpdateView is called.  This is the observer pattern, made easier in C# using events.. which is using a delegate.
// view
public TopEmployeeForm(ITopEmployeeController controller, Employee model)
        {
            _controller = controller;
            _model = model;

            //Let the model know that this view is interested if the model change
            _model.OnPropertyChange += new Action(UpdateView);

            InitializeComponent();
}
6. When Button is pressed on the view.  The controller.GetTopEmplyee method is called.  This calls model.MontorChanges:
// controller
public void GetTopEmployee()
{
if (!_monitoring)
{
_monitoring = true;
_employee.MonitorChanges();
}
}



7. MonitorChanges does some waiting code, then every second calls FirePropertyChange(). Which calls model.OnPropertyChange.
Which is really a delegate to View.UpdateView
// model
public event Action OnPropertyChange;

private void FirePropertyChange()
        {
            var propChange = OnPropertyChange;
            if (propChange != null)
            {
                OnPropertyChange();
            }
        }

8. The view method which runs every second and displays the name on the form.
// view
        public void UpdateView()
        {
            // to do with threading
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(UpdateView));
            }
            else
            {
                TopEmployeeName = _model.FullName;
            }
        }




| | #