Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Monday, 06 October 2014
( EF6 | MVC5 )

Problem: 10s on laptop to bring up a project in debug

Cache Symbols (did nothing for me) - http://stackoverflow.com/questions/12567984/visual-studio-debugging-loading-very-slow

image
4s from F5 to load (on fast desktop) on IE and Chrome
Instant on no debug

| | # 
# Sunday, 23 February 2014
( EF6 | Humour )

Added EF to Repository and Integration tests project.

namespace Humour.Respository
{
    public class HumourContext : DbContext
    {
        public HumourContext() : base("Humour") { }
        public DbSet<Story> Stories { get; set; }
    }
}

Making the db name Humour

image
EF Uses .\SQLEXPRES by default if no connection string is specified.

However want to use repositories and not call the context directly from external code.

To implement Unit of Work (that enabled you to make multiple changes to your data and submit them to the database all at once) the repos shouldn’t created instances of HumourContext themselves in each public method.  Useful to have a factory.

Additionally it would be useful if the same HumourContext instance was used for the entire HTTP request ie can share same instance across multiple pieces of code running in the same request.  Giving opportunity to treat multiple db updates as a single unit.

Building a Context Storage Mechanism

Implement a factory class that can create and return instances of HumourContext.

  • DataContextFactory – static class with a static GetDataContext method (EF repositories project)
  • DataContextStorageFactory - (infrastructure project as not tied to specific EF implementation)
  • IDataConextStorageContainer
  • HttpDataContextStorageContainer – web related projects
  • ThreadDataContextStorageContainer – desktop apps and unit test projects
/// <summary>
/// Manages instances of the ContactManagerContext and stores them in an appropriate storage container.
/// </summary>
public static class DataContextFactory
{
    /// <summary>
    /// Clears out the current ContactManagerContext.
    /// </summary>
    public static void Clear()
    {
        var dataContextStorageContainer = DataContextStorageFactory<HumourContext>.CreateStorageContainer();
        dataContextStorageContainer.Clear();
    }

    /// <summary>
    /// Retrieves an instance of ContactManagerContext from the appropriate storage container or
    /// creates a new instance and stores that in a container.
    /// </summary>
    /// <returns>An instance of ContactManagerContext.</returns>
    public static HumourContext GetDataContext()
    {
        var dataContextStorageContainer = DataContextStorageFactory<HumourContext>.CreateStorageContainer();
        var humourContext = dataContextStorageContainer.GetDataContext();
        if (humourContext == null)
        {
            humourContext = new HumourContext();
            dataContextStorageContainer.Store(humourContext);
        }
        return humourContext;
    }
}
[TestMethod]
public void CanExecuteQueryAgainstDataContext()
{
    string randomString = Guid.NewGuid().ToString().Substring(0, 25);
    var context = DataContextFactory.GetDataContext();
    var story = new Story
    {
        Title = "test",
        Content = randomString,
        DateCreated = DateTime.Now,
        DateModified = DateTime.Now
    };

    context.Stories.Add(story);
    context.SaveChanges();

    var storyCheck = context.Stories.First(st => st.Content == randomString);
    storyCheck.Should().NotBeNull();
}

Interesting way to generate a random string!

Configuring Model’s Business Rules

  • Property level eg required field, max length etc..
  • Object level eg copare 2 fields with each other

Property Level

Attributes or fluent API.

Could use OnModelCreating in EF to put on fluent API rules.  A better way is EntityTypeConfiguration class.

/// <summary>
/// Configures the behavior for a person in the model and the database.
/// </summary>
public class StoryConfiguration : EntityTypeConfiguration<Story>
{
    /// <summary>
    /// Initializes a new instance of the PersonConfiguration class.
    /// </summary>
    public StoryConfiguration()
    {
        Property(x => x.Title).IsRequired().HasMaxLength(25);
        Property(x => x.Content).IsRequired().HasMaxLength(2048);
    }
}

and wired up through:

public class HumourContext : DbContext
{
    public HumourContext() : base("Humour") { }
    public DbSet<Story> Stories { get; set; }

    /// <summary>
    /// Configures the EF context.
    /// </summary>
    /// <param name="modelBuilder">The model builder that needs to be configured.</param>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new StoryConfiguration());
    }
}

Object Level Validation

/// <summary>
/// Determines whether this object is valid or not.
/// </summary>
/// <param name="validationContext">Describes the context in which a validation check is performed.</param>
/// <returns>A IEnumerable of ValidationResult. The IEnumerable is empty when the object is in a valid state.</returns>
public abstract IEnumerable<ValidationResult> Validate(ValidationContext validationContext);

/// <summary>
/// Determines whether this object is valid or not.
/// </summary>
/// <returns>A IEnumerable of ValidationResult. The IEnumerable is empty when the object is in a valid state.</returns>
public IEnumerable<ValidationResult> Validate()
{
    var validationErrors = new List<ValidationResult>();
    var ctx = new ValidationContext(this, null, null);
    Validator.TryValidateObject(this, ctx, validationErrors, true);
    return validationErrors;
}

DomainEntity implements DataAnnotations.IValidatableObject.  So each entity must override Validate

Can’t do dynamic rules on attributes eg DateOfBirth can’t be later than today.

Calling validate on the Collections eg StoryCollection

/// <summary>
/// Validates this object. It validates dependencies between properties and also calls Validate on child collections;
/// </summary>
/// <param name="validationContext"></param>
/// <returns>A IEnumerable of ValidationResult. The IEnumerable is empty when the object is in a valid state.</returns>
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    yield break;
    if (StoryType == StoryType.None)
    {
        yield return new ValidationResult("Type can't be None.", new[] { "Type" });
    }

    //if (DateOfBirth < DateTime.Now.AddYears(Constants.MaxAgePerson * -1))
    //{
    //    yield return new ValidationResult("Invalid range for DateOfBirth; must be between today and 130 years ago.", new[] { "DateOfBirth" });
    //}
    //if (DateOfBirth > DateTime.Now)
    //{
    //    yield return new ValidationResult("Invalid range for DateOfBirth; must be between today and 130 years ago.", new[] { "DateOfBirth" });
    //}

    foreach (var result in Votes.Validate())
    {
        yield return result;
    }

Custom Validate method is not called as long as one of the Required or other attributes, or property-based rules set using the Fluent API are causing an objec to be invalid.

Add more tests to StoryTests class to test this validation

**problem – tests only work with attribute level validation.. if I put isRequired in StoryConfiguration tests don’t work

[TestMethod]
public void StoryWithTypeStoryTypeNoneIsInvalid()
{
    var story = new Story();
    story.StoryType = StoryType.None;
    story.Validate().Count(x => x.MemberNames.Contains("StoryType")).Should().BeGreaterThan(0);
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    yield break;
    if (StoryType == StoryType.None)
    {
        yield return new ValidationResult("StoryType can't be None.", new[] { "StoryType" });
    }

Dealing With Database Initialisation

  • Use a specialised version of DropCreateDatabaseIfModelChanges at dev time
  • At prod time turn off database initialiser.. ie model is not checked

Have setup so that default data is there for integration tests.  Can also override the seed method and put in own default data for tests.

/// <summary>
/// Used to initialize the HumourContext from Integration Tests
/// </summary>
public static class HumourContextInitializer
{
    /// <summary>
    /// Sets the IDatabaseInitializer for the application.
    /// </summary>
    /// <param name="dropDatabaseIfModelChanges">When true, uses the MyDropCreateDatabaseIfModelChanges to recreate the database when necessary.
    /// Otherwise, database initialization is disabled by passing null to the SetInitializer method.
    /// </param>
    public static void Init(bool dropDatabaseIfModelChanges)
    {
        if (dropDatabaseIfModelChanges)
        {
            Database.SetInitializer(new MyDropCreateDatabaseIfModelChanges());
            using (var db = new HumourContext())
            {
                db.Database.Initialize(false);
            }
        }
        else
        {
            Database.SetInitializer<HumourContext>(null);
        }
    }
}
/// <summary>
/// A custom implementation of HumourContext that creates a new Story and Vote.
/// </summary>
public class MyDropCreateDatabaseIfModelChanges : DropCreateDatabaseIfModelChanges<HumourContext>
{
    /// <summary>
    /// Creates a new Story and Vote
    /// </summary>
    /// <param name="context">The context to which the new seed data is added.</param>
    protected override void Seed(HumourContext context)
    {
        var story = new Story
        {
            Title = "Banana",
            StoryType = StoryType.Joke,
            Content = "asdf",
        };
        story.Votes.Add(CreateVote());
        context.Stories.Add(story);
    }

    private static Vote CreateVote()
    {
        return new Vote { IPAddress = "192.168.1.1" };
    }
}

then wired up to tests via:

dev web.config:

<contexts>
  <context type="Humour.Repository.HumourContext, Humour.Repository" disableDatabaseInitialization="false">
    <databaseInitializer type="Humour.Repository.MyDropCreateDatabaseIfModelChanges, Humour.Repository" />
  </context>
</contexts>

prod:

<contexts>
  <context type="Humour.Repository.HumourContext, Humour.Repository" disableDatabaseInitialization="true">
    <!--<databaseInitializer type="Humour.Repository.MyDropCreateDatabaseIfModelChanges, Humour.Repository" />-->
  </context>
</contexts>

Implementing a Base Repository Class

Abstract generic repository methods into a base

Search

public class StoryRepository : Repository<Story>, IStoryRepository
{
    public IEnumerable<Story> FindByTitle(string title)
    {
        return DataContextFactory.GetDataContext().Set<Story>()
            .Where(st => st.Title == title).ToList();
    }
}

Unit Of Work

/// <summary>
/// Represents a unit of work
/// </summary>
public interface IUnitOfWork : IDisposable
{
    /// <summary>
    /// Commits the changes to the underlying data store.
    /// </summary>
    /// <param name="resetAfterCommit">When true, all the previously retrieved objects should be cleared from the underlying model / cache.</param>
    void Commit(bool resetAfterCommit);

    /// <summary>
    /// Undoes all changes to the entities in the model.
    /// </summary>
    void Undo();
}
/// <summary>
/// Creates new instances of a unit of Work.
/// </summary>
public interface IUnitOfWorkFactory
{
    /// <summary>
    /// Creates a new instance of a unit of work
    /// </summary>
    IUnitOfWork Create();

    /// <summary>
    /// Creates a new instance of a unit of work
    /// </summary>
    /// <param name="forceNew">When true, clears out any existing in-memory data storage / cache first.</param>
    IUnitOfWork Create(bool forceNew);
}

Concrete:

/// <summary>
/// Defines a Unit of Work using an EF DbContext under the hood.
/// </summary>
public class EFUnitOfWork : IUnitOfWork
{
    /// <summary>
    /// Initializes a new instance of the EFUnitOfWork class.
    /// </summary>
    /// <param name="forceNewContext">When true, clears out any existing data context first.</param>
    public EFUnitOfWork(bool forceNewContext)
    {
        if (forceNewContext)
        {
            DataContextFactory.Clear();
        }
    }

    /// <summary>
    /// Saves the changes to the underlying DbContext.
    /// </summary>
    public void Dispose()
    {
        DataContextFactory.GetDataContext().SaveChanges();
    }

    /// <summary>
    /// Saves the changes to the underlying DbContext.
    /// </summary>
    /// <param name="resetAfterCommit">When true, clears out the data context afterwards.</param>
    public void Commit(bool resetAfterCommit)
    {
        DataContextFactory.GetDataContext().SaveChanges();
        if (resetAfterCommit)
        {
            DataContextFactory.Clear();
        }
    }

    /// <summary>
    /// Undoes changes to the current DbContext by removing it from the storage container.
    /// </summary>
    public void Undo()
    {
        DataContextFactory.Clear();
    }
}
/// <summary>
/// Creates new instances of an EF unit of Work.
/// </summary>
public class EFUnitOfWorkFactory : IUnitOfWorkFactory
{
    /// <summary>
    /// Creates a new instance of an EFUnitOfWork.
    /// </summary>
    public IUnitOfWork Create()
    {
        return Create(false);
    }

    /// <summary>
    /// Creates a new instance of an EFUnitOfWork.
    /// </summary>
    /// <param name="forceNew">When true, clears out any existing data context from the storage container.</param>
    public IUnitOfWork Create(bool forceNew)
    {
        return new EFUnitOfWork(forceNew);
    }
}

and it working:

Because the UoW is based on interfaces it is easy to use in unit testable environments eg

public class PeopleController : BaseController
{
    private readonly IPeopleRepository _peopleRepository;
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;
    const int PageSize = 10;

    /// <summary>
    /// Initializes a new instance of the PeopleController class.
    /// </summary>
    public PeopleController(IPeopleRepository peopleRepository, IUnitOfWorkFactory unitOfWorkFactory)
    {
        _peopleRepository = peopleRepository;
        _unitOfWorkFactory = unitOfWorkFactory;
    }
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateAndEditPerson createAndEditPerson)
{
    if (ModelState.IsValid)
    {
        try
        {
            using (_unitOfWorkFactory.Create())
            {
                Person person = new Person();
                Mapper.Map(createAndEditPerson, person);
                _peopleRepository.Add(person);
                return RedirectToAction("Index");
            }
        }
        catch (ModelValidationException mvex)
        {
            foreach (var error in mvex.ValidationErrors)
            {
                ModelState.AddModelError(error.MemberNames.FirstOrDefault() ?? "", error.ErrorMessage);
            }
        }
    }
    return View();
}

The controller receives an instance of IPeopleRepository and IUnitOfWorkFactory.  Both parameters are based on interfaces so it’s easy to pass other types in during testing.  At runtime we’ll use IoC.

This makes our app a lot easier to change as:

  • Add property to Model class
  • Use the field in the UI

Compared to having to modify SP’s.

Managing Relationships

Problem with orphaned records if we delete a Story – Votes wouldn’t be deleted.

Do this on an override at HumourContext

/// <summary>
/// Hooks into the Save process to get a last-minute chance to look at the entities and change them. Also intercepts exceptions and
/// wraps them in a new Exception type.
/// </summary>
/// <returns>The number of affected rows.</returns>
public override int SaveChanges()
{
    // Need to manually delete all "owned objects" that have been removed from their owner, otherwise they'll be orphaned.
    var orphanedObjects = ChangeTracker.Entries().Where(
      e => (e.State == EntityState.Modified || e.State == EntityState.Added) &&
        e.Entity is IHasOwner &&
          e.Reference("Owner").CurrentValue == null);

    foreach (var orphanedObject in orphanedObjects)
    {
        orphanedObject.State = EntityState.Deleted;
    }

    try
    {
        var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added);
        foreach (DbEntityEntry item in modified)
        {
            var changedOrAddedItem = item.Entity as IDateTracking;
            if (changedOrAddedItem != null)
            {
                if (item.State == EntityState.Added)
                {
                    changedOrAddedItem.DateCreated = DateTime.Now;
                }
                changedOrAddedItem.DateModified = DateTime.Now;
            }
        }
        return base.SaveChanges();
    }
    catch (DbEntityValidationException entityException)
    {
        var errors = entityException.EntityValidationErrors;
        var result = new StringBuilder();
        var allErrors = new List<ValidationResult>();
        foreach (var error in errors)
        {
            foreach (var validationError in error.ValidationErrors)
            {
                result.AppendFormat("\r\n  Entity of type {0} has validation error \"{1}\" for property {2}.\r\n", error.Entry.Entity.GetType().ToString(), validationError.ErrorMessage, validationError.PropertyName);
                var domainEntity = error.Entry.Entity as DomainEntity<int>;
                if (domainEntity != null)
                {
                    result.Append(domainEntity.IsTransient() ? "  This entity was added in this session.\r\n" : string.Format("  The Id of the entity is {0}.\r\n", domainEntity.Id));
                }
                allErrors.Add(new ValidationResult(validationError.ErrorMessage, new[] { validationError.PropertyName }));
            }
        }
        throw new Humour.Infrastructure.ModelValidationException(result.ToString(), entityException, allErrors);
    }
}

Implementing IDateTracking

See code above!

Improving Error Messages Generated

image
Rather cryptic error message.

image
Better message – showing type, ID and the reason.

See code above too for this!

| | # 
# Friday, 24 January 2014
( EF6 | MVC5 | Unity | UoW )

http://blog.longle.net/2014/01/06/unit-of-work-unity-3-quick-start-video/

  • Entity Framework Power Tools
  • UoW & Repo Framework
  • Generating EF Mappings and POCO’s
  • Upgrading the stack to EF 6.2
  • Basic Customer CRUD Use Case with MVC Scafolding Template w/ Async
  • Refactoring the CustomerController to use UoW & Repo Framework w/ Async
  • Why the ICustomerService Approach?
  • Why Commit Unit of Work Outside the Service vs. versa
  • Quick Examples with Eager Loading, Filter, and Sorting

image

| | # 
# Thursday, 09 January 2014
( EF6 | MVC5 | Project )

Looking at admin interface and how to handle foreign keys / drop downs.

@*@Html.DropDownList("StoryTypeID", String.Empty)*@
@Html.DropDownList("StoryTypeID")

Getting rid of the blank type in the dropdown list.

image

image

image

image
The standard first deployment screen!

  <system.web>
    <authentication mode="None" />
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1" />
    <customErrors mode="Off"/>
    
  </system.web>

Added customErrors into web.config.. and suddenly it broke into life.  Hmm – initially a timeout.  Took customErrors out and it still works.

image
A working app.  Important.

Deploying and Cleaning up

Because I’d like to really encourage collaboration in teams I’m going to be as open as possible and put this code on GitHub.

I’ve got lots of commits and legacy code in current local git repo, but only want to push up current.  Basically tidy up.

image
Don’t want this publish profile in github.

image
Thats all we need – in fact don’t need packages, as NuGet would get these anyway.

http://robertcorvus.com/visual-studio-command-to-delete-all-bin-and-obj-folders-in-solution/

##########################################################
# PLEASE READ:
#
# This script will be loaded by Visual Studio only when
# the solution is loaded therefore any changes you make
# to it will not be effective until after you exit Visual
# Studio and reload the solution.
##########################################################
function global:DeleteBinObj()
{
    Get-ChildItem .\ -include bin,obj -recu -Force | remove-item -force -recurse
}
image

image

image
This was with no packages from NuGet.

image
This seems excessive – EF is 30MB

image
Found that I was referencing EF6.0.0 in web and 6.0.2 in test.  So upgraded web (which seems to have speeded startup time of project up)

Driving Out From Tests

So have got:

  • Rudimentary framework (I suspect will need to refactor out into separate projects Web and Core.. but not yet)
  • Source controlled on GitHub
  • Working live on: http://britishhumour.azurewebsites.net/
  • 6 passing tests – a few integration

Want to make sure current site is ‘working’, and want to drive out functionality from tests now.

tried to test WinterDb directly from tests class for Integration testing:

Error    1    The type 'Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext`1<T0>' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.AspNet.Identity.EntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.    E:\Dev\Winter Project\Winter\Winter.Web.Tests\Repository\StoryRepositoryTests.cs    20    17    Winter.Web.Tests

Its because I’m using IdentityDbContext and shortcutting.. Hmm.  Used NuGet to get around this.

Currently all logic is in Model (good) and Controller (just for UI rendering, so that is fine).

Made context Sets virtual to help with lazy loading and change tracking.

Making admin screen – changing size of input boxes

All done with Bootstap3

http://www.mytecbits.com/microsoft/dot-net/bootstrap-3-with-asp-net-mvc-5

Adding content and using the site

image

Layout work will have to be done soon.

image

image

Admin interface coming together.

| | # 
# Wednesday, 08 January 2014
( EF6 | MVC5 | Project )

Am using the database in Winter.Web\App_Data\Winter.mdf and using

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Winter.mdf;Initial Catalog=Winter;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

However in the test project the |DataDirectory| is in Winter.Web.Tests so need to point to Winter.Web directory

<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=e:\\dev\\winter project\\winter\\winter.web\\app_data\\Winter.mdf;Initial Catalog=Winter;Integrated Security=True" providerName="System.Data.SqlClient" />

This works but relative would be much better.  Stick with this for now as it works.

photo

Initial sketch of front page (and only real page)

image
As all in 1 project can use trick to pull the ApplicationDbContext used for authentication into WinterDb

@if (User.IsInRole("admin"))
{
    <li>@Html.ActionLink("Admin", "Index", "Story")</li>
}

Menu now reacts if the users is in admin role or not.

image

image
Mocking out the front end.

image
Beginnings of an admin interface.

Working on foreign keys and drop downs working.

image
A useful trick is to get the model sorted out, then do an Add Controller (and associated views) which gives a good starting point.

| | # 
# Tuesday, 07 January 2014
( EF6 | MVC5 | Project )

Enabling migrations – have currently got a POCO:

namespace Winter.Core.Model
{
    public class Story
    {
        public int StoryID { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    }
}

Install EF into Infrastructure

ALT V E O to get package manager console up

Enable-migrations

Change

Update-database to kick it into life

Ended up looking at Azure again, and it has stopped working (free trial ended).. but didn’t roll over into a paid one.  And credit card wouldn’t work.

image

Set startup project to Infrastructure! To get update-database –verbose to use that (instead of web project) or

Enable-Migrations -ContextTypeName Web.Infrastructure.ContextName

Bring together Core and Infrastructure projects?

http://stackoverflow.com/questions/14891640/implementing-bounded-context-to-entity-framework-based-infrastructure

“Assuming you have a complex business domain and significant business logic, it can be worth making the effort as you have to isolate your domain layer from infrastructure concerns. However, this is often not the case. If you are mostly just moving data from the database to the UI and back again, then this is overengineering and you should seek something with fewer moving parts.”

Don’t need to isolate and test infrastructure currently as it is just EF.

What are we testing?

The main goal of this project is to drive it out via testing.  So goal is to have many unit tests which are totally independent.

Want a fat model and skinny controller.

Use interfaces

Telerik JustMock

namespace Winter.UnitTests.Web
{
    [TestClass]
    public class WinterUnitTests
    {
        // Unit test as injecting in a fake repository
        [TestMethod]
        public void Index_GivenAFakeRepository_ShouldReturnAListOfAllStories()
        {
            var storyRepository = Mock.Create<IStoryRepository>();
            Mock.Arrange(() => storyRepository.GetAllStories())
                .Returns(new List<Story>()
                {
                    new Story { StoryID=1, Title="title1", Content="content1"},
                    new Story { StoryID=2, Title="title2", Content="content2"}
                })
                .MustBeCalled();

            // Act
            HomeController controller = new HomeController(storyRepository);
            ViewResult result = controller.Index() as ViewResult;
            IEnumerable<Story> model = result.Model as IEnumerable<Story>;

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

however much more elegant? is to have our own fake

Do we really need a repository pattern over it all, as have EF?

CTRL R + CTRL T – run current test

[TestMethod]
public void StoriesDb_GivenANewStory_ShouldSaveToDb_AndThenRevertBackDB()
{
    using (var db = new StoryRepository())
    {
        var story = new Story { Title = "test", Content = "testcontent" };

        db.Stories.Add(story);
        db.SaveChanges();

        var result = db.Stories;

        Assert.AreEqual(4, result.Count());

        db.Stories.Remove(story);
        db.SaveChanges();
        Assert.AreEqual(3, result.Count());
    }
}

Integration testing the repository.  Also implemented the transactional stuff so don’t need to remove.

Starting to build up domain model:

public class Story
{
    public int StoryID { get; set; }

    [Required]
    public string Title { get; set; }
        
    public string Content { get; set; }
}

Simplify

From Scott Allen’s code http://pluralsight.com/training/Courses/TableOfContents/mvc4-building I’ve simplified the project even further to see if it’s robust enough to do the job.

So now I’ve got a Web project and a Tests project.

image

| | # 
# Wednesday, 18 December 2013
( EF6 | MVC4 | MVC5 )

Taken and modified from Scott Allen’s http://pluralsight.com/training/Courses/TableOfContents/mvc4-building

This is using MVC5 (with the new Identity provider), and AutomaticMigrationsEnabled.  I’m kept with 1 DbContext for now for the ease of auto migrations being able to do Update-Database without any parameters.

public interface IOdeToFoodDb : IDisposable
{
    IQueryable<T> Query<T>() where T : class;
    void Add<T>(T entity) where T : class;
    void Update<T>(T entity) where T : class;
    void Remove<T>(T entity) where T : class;
    void SaveChanges();
}

// Was ApplicationDbContext (which contains User and Role in MVC5)
public class OdeToFoodDb : IdentityDbContext<ApplicationUser>, IOdeToFoodDb
{
    public OdeToFoodDb()
        : base("DefaultConnection")
    { }

    public DbSet<Restaurant> Restaurants { get; set; }
    public DbSet<RestaurantReview> Reviews { get; set; }

    IQueryable<T> IOdeToFoodDb.Query<T>()
    {
        return Set<T>();
    }

    void IOdeToFoodDb.Add<T>(T entity)
    {
        Set<T>().Add(entity);
    }

    void IOdeToFoodDb.Update<T>(T entity)
    {
        Entry(entity).State = System.Data.Entity.EntityState.Modified;
    }

    void IOdeToFoodDb.Remove<T>(T entity)
    {
        Set<T>().Remove(entity);
    }

    void IOdeToFoodDb.SaveChanges()
    {
        SaveChanges();
    }
}

and calling

public class HomeController : Controller
{
    IOdeToFoodDb _db;

    public HomeController()
    {
        _db = new OdeToFoodDb();
    }

    public HomeController(IOdeToFoodDb db)
    {
        _db = db;
    }

    public ActionResult Index(string searchTerm = null, int page = 1)
    {
        var model = _db.Query<Restaurant>()
                        //.Restaurants
                        .OrderByDescending(r => r.Reviews.Average(review => review.Rating))
                        .Where(r => searchTerm == null || r.Name.StartsWith(searchTerm))
                        .Select(r => new RestaurantListViewModel
                                {
                                    Id = r.Id,
                                    Name = r.Name,
                                    City = r.City,
                                    Country = r.Country,
                                    NumberOfReviews = r.Reviews.Count()
                                })
                            .ToPagedList(page, 10);
        return View(model);
    }

primarily good for testing:

[TestMethod]
public void Index()
{
    // Arrange
    var db = new FakeOdeToFoodDb();
    db.AddSet(TestData.Restaurants);
    HomeController controller = new HomeController(db);
    // Was needed for a call in HomeController for isAjaxRequest.
    //controller.ControllerContext = new FakeControllerContext();

    // Act
    ViewResult result = controller.Index() as ViewResult;
    IEnumerable<RestaurantListViewModel> model = result.Model as IEnumerable<RestaurantListViewModel>;

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

Injecting in a FakeDb

public class FakeOdeToFoodDb : IOdeToFoodDb
{
    public IQueryable<T> Query<T>() where T : class
    {
        return Sets[typeof(T)] as IQueryable<T>;
    }

    void IDisposable.Dispose() { }

    public void AddSet<T>(IQueryable<T> objects)
    {
        Sets.Add(typeof(T), objects);
    }

    public void Add<T>(T entity) where T : class
    {
        Added.Add(entity);
    }

    public void Update<T>(T entity) where T : class
    {
        Updated.Add(entity);
    }

    public void Remove<T>(T entity) where T : class
    {
        Removed.Add(entity);
    }

    public void SaveChanges()
    {
        Saved = true;
    }

    public Dictionary<Type, object> Sets = new Dictionary<Type, object>();
    public List<object> Added = new List<object>();
    public List<object> Updated = new List<object>();
    public List<object> Removed = new List<object>();
    public bool Saved = false;
}

The FakeDb which operates as an in-memory collection.

| | # 
# Wednesday, 11 December 2013
( EF6 | MVC4 | MVC5 )

public class Employee
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

EF can do change tracking if make virtual. Using alt to add virtual everywhere.

Migrations


namespace eManager.Web.Infrastructure
{
    public class DepartmentDb : DbContext, IDepartmentDataSource
    {
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Department> Departments { get; set; }

        IQueryable<Employee> IDepartmentDataSource.Employees
        {
            get { return Employees; }
        }

        IQueryable<Department> IDepartmentDataSource.Departments
        {
            get { return Departments; }
        }
    }
}

Super simple database abstraction.

image
Hmm –authentication is wired into MVC5 in Models\IdentityModels… but inherits from IdentityDbContext

Stripped out the authentication stuff for now to get compiling.  Commented out AccountController and ApplicationDbContext classes.

image

image
Enabling automatic migrations

Seed allows us to initally populate eg

  • Lookup tables

Update-Database

image

Hmmmm am not getting migration file created nor database created in App_Data

Version2

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDepartmentDataSource
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Department> Departments { get; set; }

        IQueryable<Employee> IDepartmentDataSource.Employees
        {
            get { return Employees; }
        }

        IQueryable<Department> IDepartmentDataSource.Departments
        {
            get { return Departments; }
        }
    }

To make a simple happy path for easy single migrations, I’ve added our entities to the existing context.

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(ApplicationDbContext context)
    {
        context.Departments.AddOrUpdate(d => d.Name,
                new Department { Name="Engineering"},
                new Department { Name="Sales"},
                new Department { Name="Shipping"},
                new Department { Name="Human Resources"}
                );

        // Could you use this way
        if (!context.Users.Any(u => u.UserName == "dave"))
        {
            var roleStore = new RoleStore<IdentityRole>(context);
            var roleManager = new RoleManager<IdentityRole>(roleStore);

            var userStore = new UserStore<ApplicationUser>(context);
            var userManager = new UserManager<ApplicationUser>(userStore);
            var user = new ApplicationUser { UserName = "dave" };

            userManager.Create(user, "letmein");
            roleManager.Create(new IdentityRole { Name = "admin" });
            userManager.AddToRole(user.Id, "admin");
        }
    }
}

Alt V E O – Package Manager console

update-database –verbose

| | # 
# Thursday, 05 December 2013
( Azure | EF6 | MVC5 )

New Web Site – Custom Create

image

image

winterjAT9VB56yf

z…

image
Bottom right, download publish profile.

Actually in VS publish web project, import from azure,

image

image
Had to remember to select the correct db.  Actually I didn’t push out Executre code first.

image
Hmm database doesn’t look like it is there.

Did execute code first, and got authentication working

<system.web>
    <customErrors mode="Off"/>
    <authentication mode="None"/>
    <compilation debug="true" targetFramework="4.5.1"/>
    <httpRuntime targetFramework="4.5.1"/>

Actually had to just make sure customErrors were off

image

Ahh, it turned out it did do the first identity migration, but not the second library.

Ended up doing a generate scripts and http://social.msdn.microsoft.com/Forums/windowsazure/en-us/259af3d5-4016-43e2-9a84-7a17d4f52673/im-unable-to-create-a-new-table-on-sql-azure?forum=ssdsgetstarted

Needed to get rid of the ON PRIMARY stuff in the dump out to 2008R2

image

Then it worked:

image
We have a live site, connecting to a database!

Alt B O – toggle beteen debug / release

Alt B H – publish

| | # 
# Tuesday, 03 December 2013
( EF6 )

To make admin better, lets have different schema for our db entities

public class BooksDb : DbContext
    {
        public BooksDb()
            : base("DefaultConnection")
        {
            Database.Log = sql => Debug.Write(sql);
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultSchema("library");
            base.OnModelCreating(modelBuilder);
        }

        public DbSet<Book> Books { get; set; }
    }

Forcing a different schema on each context.

image
Out of sync now when I ran the web app.

PM> Add-Migration -ConfigurationTypeName Books.Web.DataContexts.BookMigrations.Configuration "DefaultSchema"

Which creates

public partial class DefaultSchema : DbMigration
    {
        public override void Up()
        {
            MoveTable(name: "dbo.Books", newSchema: "library");
        }
        
        public override void Down()
        {
            MoveTable(name: "library.Books", newSchema: "dbo");
        }
    }
Update-Database -ConfigurationTypeName Books.Web.DataContexts.BookMigrations.Configuration

And identity

image
Data is still intact during migrations

| | # 
( EF6 | Glimpse | MVC5 )

public class BooksDb : DbContext
    {
        public BooksDb()
            : base("DefaultConnection")
        {
            Database.Log = sql => Debug.Write(sql);
        }

        public DbSet<Book> Books { get; set; }
    }

When app is in debug, will be able to see in Output window in VS.

image
First time EF is being used, checks to see if DB is in sync with migrations.

Could parse and only log queries > 2000ms

Glimpse

image

image

 

image
Push the on button

Only enabled for local server by default.

image

image
Seeing what SQL has ran, and how long.

| | # 
( EF6 | MVC5 )

Goal is to CRUD up Books now that we have Books entities, and a Books table.

image

image

image
Created books controller and put on [Authorize]

<ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("Books", "Index", "Books")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                </ul>

In Views\Shared\_Layout.cshtml

image
Responsive UI and authorization, and CRUD.

image
Validation too, but notice the Enum category is freeform text – not good.

Editor Templates

image
Adding a global template for Genre.

Reflect over the Enum type to get values, so can render on the view.  As the database is only storing the int values.

namespace Books.Web.Helpers
{
    public static class EnumHelpers
    {
        public static IEnumerable<SelectListItem> GetItems(this Type enumType)
        {
            if (!typeof(Enum).IsAssignableFrom(enumType))
            {
                throw new Exception("Type must be an Enum");
            }

            var names = Enum.GetNames(enumType);
            var values = Enum.GetValues(enumType).Cast<int>();

            // Stitch together 2 sequences
            var items = names.Zip(values, (name, value) =>
                new SelectListItem
                {
                    Text = name,
                    Value = value.ToString()
                }
                );

            return items;
        }
    }
}
@using Books.Entities
@using Books.Web.Helpers
@{
    var items = typeof(Genre).GetItems();
}

@Html.DropDownList("", items)

image
However spaces are not elegant

public enum Genre
    {
        [Display(Name="Non Fiction")]
        NonFiction,
        Romance,
        Action,
        [Display(Name = "Science Fiction")]
        ScienceFiction
    }

And wire up:

public static IEnumerable<SelectListItem> GetItems(this Type enumType)
        {
            if (!typeof(Enum).IsAssignableFrom(enumType))
            {
                throw new Exception("Type must be an Enum");
            }

            var names = Enum.GetNames(enumType);
            var values = Enum.GetValues(enumType).Cast<int>();

            // Stitch together 2 sequences
            var items = names.Zip(values, (name, value) =>
                new SelectListItem
                {
                    Text = GetName(enumType,name),
                    Value = value.ToString()
                }
                );

            return items;
        }

        static string GetName(Type enumType, string name)
        {
            var result = name;

            var attribute = enumType
                            .GetField(name)
                            .GetCustomAttributes(inherit: false)
                            .OfType<DisplayAttribute>()
                            .FirstOrDefault();

            if (attribute != null)
            {
                result = attribute.GetName();
            }

            return result;
        }

image

image
However on edit, selected category not working so:

public static IEnumerable<SelectListItem> GetItems(this Type enumType, int? selectedValue)
        {
            if (!typeof(Enum).IsAssignableFrom(enumType))
            {
                throw new Exception("Type must be an Enum");
            }

            var names = Enum.GetNames(enumType);
            var values = Enum.GetValues(enumType).Cast<int>();

            // Stitch together 2 sequences
            var items = names.Zip(values, (name, value) =>
                new SelectListItem
                {
                    Text = GetName(enumType,name),
                    Value = value.ToString(),
                    Selected = value == selectedValue
                }
                );

            return items;
        }

Pass in the selectedValue to the helper, and do true or false for selected.

@{
    var items = typeof(Genre).GetItems((int?)Model);
}

Problem on Index view that it still displays ScienceFiction.. but solvable in a similar way, using a global DisplayTemplate

| | #