Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Saturday, 21 December 2013
( MVC3 )

IoC - Inversion of Control containers – eg a RestaurantController isn’t in control of it’s own dependencies anymore

So could remove the default constructor

public class RestaurantController : Controller
{
    IOdeToFoodDb _db;

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

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

image
MVC not happy when remove parameterless constructor

image

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

Very useful to create applications which are:

  • flexible
  • pluggable
  • testable

Useful for nested dependencies eg where a Controller needs a Logger, which needs an SMTP server

| | # 
# Monday, 27 August 2012

Video from MIX11 http://channel9.msdn.com/Events/MIX/MIX11/FRM13

GUI

Adding Athlete object

image

Using GUI to do the scaffolding… used this Template.

image
Got this error doing a many to many relationship on Scaffolding the MedalAwards controller.

public class Athlete
    {
        public int AthleteId { get; set; }
        public string Name { get; set; }
        public string Country { get; set; }
    }
    //many athletes can have many medaltypes
    public class MedalAward
    {
        public int Id { get; set; }
        public string Event { get; set; }

        //the FK property
        public int MedalTypeId { get; set; }
        public virtual MedalType MedalType { get; set; }

        //now link to athlete..could put AthleteId property here
        //[ForeignKey("WinnerId")]
        //public virtual Athlete Winner { get; set; }
        public int AthleteId { get; set; }
        public virtual Athlete Athlete { get; set; }

    }

    public class MedalType
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }


Shifted my model to a simple many to many.

image
Many to many working!

image
Alt V E O to get Package Manager Console
Making a repository pattern.

image
Templates knew that we’ll be need the 3 repos.  Does eager loading so not loads of db calls for each query.

image
get-package

public class Athlete
    {
        //public int AthleteId { get; set; }
        [Key] public Guid CompetitorUniqueRef { get; set; }
        [Required, StringLength(53)] public string Name { get; set; }
        public string Country { get; set; }
    }
    //many athletes can have many medaltypes
    public class MedalAward
    {
        public int Id { get; set; }
        public string Event { get; set; }

        //the FK property
        public int MedalTypeId { get; set; }
        public virtual MedalType MedalType { get; set; }

        //now link to athlete..could put AthleteId property here
        //[ForeignKey("WinnerId")]
        //public virtual Athlete Winner { get; set; }
        //public int AthleteId { get; set; }
        public Guid AthleteId { get; set; }
        public virtual Athlete Athlete { get; set; }
    }

changed the PK of athlete to be something different and not conventional.

image
after a re-scaffold

Seed Data (pre code Migrations)

//could use codeMigrations
    class MyInitialiser : DropCreateDatabaseIfModelChanges<OlympicsContext>
    {
        protected override void Seed(OlympicsContext context)
        {
            //sample medal types
            var gold = new MedalType {Name = "Gold"};
            var silver = new MedalType {Name = "Silver"};
            var bronze = new MedalType {Name = "Bronze"};

            foreach (var medalType in new[] {gold,silver,bronze})
                context.MedalTypes.Add(medalType);

            //sample athletes
            var dave = new Athlete { Name = "Dave Mateer", Country = "Great Britain", CompetitorUniqueRef = new Guid("96ac9b12-b992-4377-bd85-3dde1a760aa5") };
            var usain = new Athlete { Name = "Usain Bolt", Country = "Jamaica", CompetitorUniqueRef = new Guid("a283b455-b109-434c-8800-4a80f914cc38") };
            var bob = new Athlete { Name = "Bob Smith", Country = "New Zealand", CompetitorUniqueRef = new Guid("a9e55b45-e7f9-44bb-bd9e-d3c2a08b02ce") };

            foreach (var athlete in new[] { dave, usain, bob })
                context.Athletes.Add(athlete);

            //sample medal awards
            var awards = new[]
                             {
                                 new MedalAward {Event = "100m Sprint", Athlete = usain, MedalType = gold},
                                 new MedalAward {Event = "50k", Athlete = dave, MedalType = silver}
                             };
            foreach (var award in awards)
            {
                context.MedalAwards.Add(award);
            }

        }
    }

Putting in a seeder of data when the database has to be regenerated.

Fluent Mappings

image
Mapping what is in MVC to the DB.  This created a new table in the DB called PEOPLE, and a column called TERRITORY_OF_ORIGIN

Final Product

Keeping things conventional makes scaffolding easier to mock something up quickly, and to remember the syntax.

image
Many to many relationship

image

| | # 
# Sunday, 26 August 2012

http://msdn.microsoft.com/en-us/data/gg702906

A 10 minute video on EF Model First

image
Navigation property allows to go from Blog to the Post, just using properties.

image
Right click on design surface and do Generate Database

image
Right click on designer and can do Run SQL.

image
Viewing database in Server Explorer in VS.

image
Using VS2010 so needed to D/l the DbContext Generator

Add Code Generation Item on design surface.

image
Created an Empty list of CRUD methods, and now creating views.

image
Wire up the Controller to the lightweight DbContext class created by the DbContext generator template (new in 4.1)

| | # 
# Monday, 03 October 2011

Works well with Azure and SQL now.

Out of the box MVC3 in latest tools update (as of 4th Oct 2011) we get:

  <connectionStrings>
    <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
      providerName="System.Data.SqlClient" />
    <add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnet_44a5afb3d91849c4b1eb154a09f03a95;Integrated Security=True;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

out of the box it needs both connections.

http://www.hanselman.com/blog/IntroducingSystemWebProvidersASPNETUniversalProvidersForSessionMembershipRolesAndUserProfileOnSQLCompactAndSQLAzure.aspx

The AspNetSql*** looks like it is legacy.  If we comment out, then everything can go through the new DefaultProviders, which give easy access to Azure.

Here is my web.config for reference:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.diagnostics>
    <trace>
      <listeners>
        <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics">
          <filter type="" />
        </add>
      </listeners>
    </trace>
  </system.diagnostics>
  
  <connectionStrings>
    <!--<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
      providerName="System.Data.SqlClient" />-->
    <!--<add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnet_44a5afb3d91849c4b1eb154a09f03a95;Integrated Security=True;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />-->

    <!-- local -->
    <add name="DefaultConnection" connectionString="Data Source=.\;Initial Catalog=PubTricks_Dev;Integrated Security=True;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />

    <!-- Azure -->
    <!--<add name="DefaultConnection" connectionString="Data Source=xxx.database.windows.net;Initial Catalog=PubTricks;User ID=dave;Password=xxx;Trusted_Connection=False;Encrypt=True;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />-->
    
  </connectionStrings>
  
  
  <appSettings>
    <add key="webpages:Version" value="1.0.0.0" />
    <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>

    <customErrors mode="Off"></customErrors>
    
    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
    </authentication>
    
    <membership defaultProvider="DefaultMembershipProvider">
      <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="/" />-->
        <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    
    <profile defaultProvider="DefaultProfileProvider">
      <providers>
        <clear />
        <!--<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />-->
        <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </profile>
    
    <roleManager enabled="true" defaultProvider="DefaultRoleProvider">
      <providers>
        <clear />
        <!--<add connectionStringName="ApplicationServices" applicationName="/"  name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" />-->
        
        <add applicationName="/" name="AspNetWindowsTokenRoleProvider"  type="System.Web.Security.WindowsTokenRoleProvider" />
        
        <add connectionStringName="DefaultConnection" applicationName="/" name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </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>
    <sessionState mode="InProc" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </sessionState>
  </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>

and web.release.config

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=xxx.database.windows.net;Initial Catalog=PubTricks;User ID=dave;Password=xxx;Trusted_Connection=False;Encrypt=True;MultipleActiveResultSets=True"
      xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
  </connectionStrings>
  
  <system.web>
    <!-- turn this on in prod - keep commented out for useful errors on live-->
    <!--<compilation xdt:Transform="RemoveAttributes(debug)" />-->

  </system.web>
</configuration>

I use the commented out trick in web.config for Azure, so can easily use the website admin tool to admin the live sites users.

image

| | # 
# 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
| | #