TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

Monday, February 13, 2012 #

The very first project I did using MVC 3 went live and we made the news! :)

 

posted @ 6:40 PM | Feedback (0)

Wednesday, March 30, 2011 #

Developing a search form should be one of the simplest Web development tasks in the world. I can do it in a few minutes with ASP.NET WebForms, but for some reason with MVC it kicked my ass! Then I remembered back to the good old 90s when contract rates were high, the economy was great, and I was 20lbs lighter.

...oh and classic ASP forced you to create forms where you could either do a POST or a GET. Remember GET? Yeah it's beeen a while if you've been an ASP.NET developer. But welcome back! That is...if you are using MVC.

It is actually pretty simple. If, unlike me, you by chance bump into the blog post instead of trying to find the (IMO) really BAD answers on stackoverflow.com (at least to-date) and various corners of the Interwebz googling things with Bing or binging things with Google....but I digress.

Here's the answer:

  1. Make your form do an HTTP GET.
  2. Use Nuget to download PagedList
  3. Use Model Binding in your controller action method.
  4. Use RouteValueDictionary in the PagedList Pager HTML helper.
  5. ???
  6. Profit!

Here's the code for the ViewModel class, controller, and View (in that order):

using PagedList;

 

namespace SearchFormResultPagingExample.Models {

    public class SearchViewModel {

        public int? Page { get; set; }

        public string EmailAddress { get; set; }

        public string LastName { get; set; }

        public IPagedList<Contact> SearchResults { get; set; }

        public string SearchButton { get; set; }

    }

}

 

using System.Linq;

using System.Web.Mvc;

using SearchFormResultPagingExample.Models;

using PagedList; //NOTE: use Nuget to reference PagedList

 

namespace SearchFormResultPagingExample.Controllers {

    public class SearchController : Controller {

        const int RecordsPerPage = 25;

 

        public ActionResult Index(SearchViewModel model) {

            if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue) {

                var entities = new AdventureWorksEntities();

                var results = entities.Contacts.Where(c => c.LastName.StartsWith(model.LastName) && c.EmailAddress.StartsWith(model.EmailAddress))

                    .OrderBy(o => o.LastName);

 

                var pageIndex = model.Page ?? 0;

                model.SearchResults = results.ToPagedList(pageIndex, 25);

            }

            return View(model);

        }

    }

}

 

@model SearchFormResultPagingExample.Models.SearchViewModel

@using PagedList.Mvc;

 

@using (Html.BeginForm("Index", "Search", FormMethod.Get)) {

    @Html.ValidationSummary(false)

    <fieldset>

        <legend>Contact Searchlegend>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.EmailAddress)

        div>

        <div class="editor-field">

            @Html.EditorFor(model => model.EmailAddress)

            @Html.ValidationMessageFor(model => model.EmailAddress)

        div>

 

        <div class="editor-label">

            @Html.LabelFor(model => model.LastName)

        div>

        <div class="editor-field">

            @Html.EditorFor(model => model.LastName)

            @Html.ValidationMessageFor(model => model.LastName)

        div>

        <p>

            <input name="SearchButton" type="submit" value="Search" />

        p>

    fieldset>

}

 

@if (Model.SearchResults != null && Model.SearchResults.Count > 0) {

    foreach (var result in Model.SearchResults) {

            <hr />

               <table width="100%">

                      <tr>

                             <td valign="top" width="*">

                        <div style="font-weight: bold; font-size:large;">@result.LastName, @result.FirstNamediv>

                        @result.Title<br />

                        @result.Phone<br />

                        @result.EmailAddress

                             td>

                      tr>

               table>

    }

        <hr />

       

        @Html.PagedListPager(Model.SearchResults,

            page => Url.Action("Index", new RouteValueDictionary() {

               { "Page", page },

               { "EmailAddress", Model.EmailAddress },

               { "LastName", Model.LastName }

            }),

            PagedListRenderOptions.PageNumbersOnly)

}

The magic is that MVC coerces the querystring back into the model and vice versa.

Hope this helps anyone trying to figure this out.

posted @ 9:43 PM | Feedback (3)

Sunday, February 13, 2011 #

Once you get past all of the cool MVC simple-mode demos which always have things like typing dates into textboxes, no Ajax, etc...you start to dig into what I personally find to be the most work with MVC: enriching the UI. WebForms can kind of spoil us with things like update panels, Ajax control toolkit, or 3rd party solutions such as Telerik Ajax Controls. I've burned many hours with UI enhancements using jQuery. Yes, this is a confession not a complaint. For example, autocomplete is a fairly simple concept; however, this also involves changing up style sheets, or adding additional behavior to examples you might find on Google such as what I've illustrated below: filtering the autocomplete list by employee title.

 I'm used to cranking out WebForms apps very quickly, whereas MVC has been a bit more tedious. So I thought I'd post some things I worked on yesterday that will hopefully save other people some time. jQuery Autocomplete and Dropdownlist replacements are very nice plugins. Here's some code that leverages both of them.


The View

@model AdventureWorks.Common.SearchEmployeeViewModel
<h2>Search Employees</h2>
<
link href="@Url.Content("~/Content/jquery.autocomplete.css")" rel="stylesheet" type="text/css" />
<
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>
<
link href="@Url.Content("~/Content/jquery.dropdownReplacement-0.5.css")" rel="stylesheet" type="text/css" />
<
script src="@Url.Content("~/Scripts/jquery.scrollTo-1.4.2.js")" type="text/javascript"></script>
<
script src="@Url.Content("~/Scripts/jquery.bgiframe.min.js")" type="text/javascript"></script>
<
script src="@Url.Content("~/Scripts/jquery.dropdownReplacement-0.5.js")" type="text/javascript"></script>
<
script src="@Url.Content("~/Scripts/jquery.autocomplete.min.js")" type="text/javascript"></script>

<!-- this should go in its own js file... -->
<script type="text/javascript">
   
$(document).ready(function () {
        $(
"#Title").dropdownReplacement({ selectCssWidth: 600 });
        $(
"input#LastName").autocomplete('@Url.Action("Find")', { cacheLength: 0, delay: 500, extraParams: { title: function () { return $("#Title").val(); } } });
    });
</script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(
true)
   
<fieldset>
       
<legend>SearchEmployeeViewModel</legend>
           
<div class="editor-label">@Html.LabelFor(model => model.LastName)</div>
           
<div class="editor-field">
               
@Html.TextBoxFor(model => model.LastName, htmlAttributes: new { style = "width: 300px " })
                @Html.ValidationMessageFor(model => model.LastName)
           
</div>
           
<div class="editor-label">@Html.LabelFor(model => model.Title)</div>
           
<div class="editor-field">
               
@Html.DropDownListFor(model => model.Title, new SelectList(Model.TitleList))
                @Html.ValidationMessageFor(model => model.Title)
           
</div>
           
<p><input type="submit" value="Search" /></p>
   
</fieldset>
}


The Controller

using System.Web.Mvc;
using AdventureWorks.BusinessInterface;
using AdventureWorks.Common;

namespace MvcDemo.Controllers {
   
public class EmployeeController : Controller {
       
EmployeeBusinessInterface _employeeBusinessInterface = new EmployeeBusinessInterface();

       
public ActionResult Search() {
           
var model = new SearchEmployeeViewModel();
            model.TitleList = _employeeBusinessInterface.GetTitleList();
           
return View(model);
        }

        public ActionResult Find(string q, int limit, string title) {
           
string[] lastNames = _employeeBusinessInterface.FindLastNames(q, title, limit);
           
return Content(string.Join("\n", lastNames));
        }
    }
}


The Model

using System.Collections.Generic;
using System.Linq;

namespace AdventureWorks.BusinessInterface {
   
public class EmployeeBusinessInterface {
        DataAccess.
AdventureWorksEntities _adventureWorksEntities = new DataAccess.AdventureWorksEntities();
       
public string[] FindLastNames(string q, string title, int limit) {
           
return _adventureWorksEntities.Employees.Where(e => e.Title == title && e.Contact.LastName.StartsWith(q))
                                                    .Select(e => e.Contact.LastName)
                                                    .Distinct()
                                                    .Take(limit)
                                                    .ToArray();
        }

        public List<string> GetTitleList() {
           
return _adventureWorksEntities.Employees.Select(e => e.Title).Distinct().OrderBy(name => name).ToList();
        }
    }
}


The ViewModel

using System.Collections.Generic;

namespace AdventureWorks.Common {
   
public class SearchEmployeeViewModel {
       
public string LastName { get; set; }
       
public string FirstName { get; set; }
        
public string Title { get; set; }
       
public List<string> TitleList { get; set; }
    }
}

So now we have a really nice dropdown where you can set the height and scroll unlike a regular HTML Select control.


And our Autocomplete assists the user with the search.


As an aside, I'm starting to change my mind about needing a Common, BI, DAL, but I definitely think the ViewModel and MVVM pattern is the way to go using only strongly-typed Views. I'm also playing around with putting some of the current logic for Common, BI into the ViewModel class with the goal of keeping the Controller code as clean as possible. This means the ViewModel class will be encapsulating the logic necessary to call into the Entity Framework (in this case) classes to fill itself with data, handle validation with annotations, etc eliminating the need to have a bunch of library projects. In this example the Controller calls to the BI to fill the ViewModel class with data, the jQuery Autocomplete Controller method basically does the same thing to get a list of names. Seems to work pretty well so far.

posted @ 10:02 AM | Feedback (6)

Sunday, January 30, 2011 #

Since MVC 3 is driving me more into client scripting, I started playing around with calling WCF services from JavaScript, but what I didn't know is that you can actually create a proxy by going to the .svc URL followed by “/js”. For example, if I create the following service:

namespace MvcApplication1 {
    [
ServiceContract(Namespace="")]
    [
AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   
public class SimpleMath {
        [
OperationContract]
       
public int Add(int x, int y) {
           
return x + y;
        }
    }
}

...and a quick servicemodel entry to my .config

<system.serviceModel>
    <
behaviors>
        <
endpointBehaviors>
            <
behavior name="MvcApplication1.SimpleMathAspNetAjaxBehavior">
                <
enableWebScript/>
            </
behavior>
        </
endpointBehaviors>
    </
behaviors>
    <
serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <
services>
        <
service name="MvcApplication1.SimpleMath">
            <
endpoint address="" behaviorConfiguration="MvcApplication1.SimpleMathAspNetAjaxBehavior" binding="webHttpBinding" contract="MvcApplication1.SimpleMath"/>
        </
service>
    </
services>
</
system.serviceModel>

Now my JavaScript can call into the service. To verify, you can open a browser and look at the proxy:

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

//SimpleMathStuff.js
function Add(x, y) {
   
var svc = new SimpleMath();
    svc.Add(x, y,
function (result) {
        alert(result);
    });
}

In my .cshtml file I call the Add() function defined above. As you can see, the second script reference is pointing at the service /js

<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<
script src="SimpleMath.svc/js" type="text/javascript"></script>
<
script src="../../Scripts/SimpleMathStuff.js" type="text/javascript"></script>

<input type="button" onclick="Add(2,2)" value="Add 2 + 2" />

Voilà! You now have a completely useless math function...but being the smart developer that you are, I'm sure you can find more interesting and realistic solutions using the same tools. And seriously, Add is so much cooler than “hello world” isn't it?  :)

Happy coding!

 

 

 

posted @ 5:25 PM | Feedback (4)

Sunday, January 23, 2011 #

MVC 3 was released just recently and I'm starting to get some strong feelings about what looks ugly and what looks clean for an MVC app. First of all, the Razor view engine is very slick and looks much cleaners than MVC 2. However, you can pretty easily make an MVC app look ugly - which IMO is 90% of the gripe the anti-WebForms people complain about. I still really love WebForms for LOB applications, and they certainly promote RAD. The complaint is that code-behind files can end up with a lot of code. So, to avoid the same issues with MVC:

  • Use ViewModel classes and avoid ViewData like the plague
    MVC is supposed to support separation of concerns? So why let your EF objects bleed through to the view?
  • Keep your business logic in your model.
    If code-behinds are ugly in WebForms, business logic in your controllers are going to make your code look even worse in MVC.
  • In order to avoid that icky feeling I get with EF used directly in my MVC Web app, create a real model.
    So far what seems cleanest is to create your EDMX (if you are not using code-first) in a DataAccess library. Create a BusinessInterface layer which the UI references along with a common library to hold your DTOs, or in this case ViewModel classes.

To clarify I'll show some example code using the AdventureWorks database for the EDMX, and display a list of employees in a WebGrid with Ajax paging and sorting. The EDMX will be the only thing in the DataAccess layer so I will not show that code. Let's start with the DTOs:

namespace AdventureWorks.Common {
   
public class GridExampleEmployee {
       
public int EmployeeID { get; set; }
       
public int ContactID { get; set; }
       
public string FullName { get; set; }
       
public string LastName { get; set; }
       
public string FirstName { get; set; }
       
public string MiddleName { get; set; }
       
public string Title { get; set; }
    }
}

namespace AdventureWorks.Common {
   
public class GridExampleViewModel {
       
public int TotalRecords { get; set; }
        
public List<GridExampleEmployee> Employees { get; set; }
    }
}

My BusinessInterface method will simply LINQ query the EDMX object model in the DataAccess DLL and return results within a DTO defined above.

using System.Linq;
using AdventureWorks.Common;
using AdventureWorks.DataAccess;

namespace AdventureWorks.BusinessInterface {
   
public class GridExampleSystem {
       
public GridExampleViewModel GetGridExampleViewModel(int page, int records, string sort) {
           
var fnResult = new GridExampleViewModel();
           
var edmx = new AdventureWorksEntities();
           
var queryResults = from c in edmx.Contacts
                               
join emp in edmx.Employees
                               
on c.ContactID equals emp.ContactID
                               
select new GridExampleEmployee {
                                    EmployeeID = emp.EmployeeID,
                                    ContactID = c.ContactID,
                                    FullName = c.LastName +
", " + c.FirstName + " " + c.MiddleName,
                                    LastName = c.LastName,
                                    FirstName = c.FirstName,
                                    MiddleName = c.MiddleName,
                                    Title = emp.Title
                                
};

             fnResult.TotalRecords = queryResults.Count();
             
switch (sort) {
               
case "Title":
                    queryResults = queryResults.OrderBy(r => r.Title);
                   
break;
               
default:
                    queryResults = queryResults.OrderBy(r => r.LastName);
                   
break;
            }

            fnResult.Employees = queryResults.Skip(page * records).Take(records).ToList();
           
return fnResult;
        }
    }
}


Now my controller class doesn't get messed up with any logic that should really be in my model (calculations, extra data fields such as “FullName” in this case, data access logic, etc).

using System.Web.Mvc;
using AdventureWorks.BusinessInterface;
using AdventureWorks.Common;

namespace MvcDemo.Controllers {
   
public class GridExampleController : Controller {
       
public const int PageSize = 10;
        
       
public ActionResult Index(int? page, string sort) {
           
var sys = new GridExampleSystem();
           
var startPage = 0;
           
if (page.HasValue && page.Value > 0) {
                startPage = page.Value - 1;
            }
           
GridExampleViewModel list = sys.GetGridExampleViewModel(startPage, PageSize, sort);
            
return View(list);
        }
    }
}


And I'm ready to crank out my View:

@using AdventureWorks.Common
@
using ThisController = MvcDemo.Controllers.GridExampleController
@model GridExampleViewModel

@{ ViewBag.Title = "Index"; }

@{
   
var grid = new WebGrid(canPage: true, rowsPerPage: ThisController.PageSize, canSort: true, ajaxUpdateContainerId: "grid");
    grid.Bind(Model.Employees, rowCount: Model.TotalRecords, autoSortAndPage:
false);
    grid.Pager(
WebGridPagerModes.All);
    @grid.GetHtml(htmlAttributes:
new { id="grid" },
        columns: grid.Columns(
            grid.Column(format: (item) => Html.ActionLink(
"Edit", "Edit", new { EmployeeID = item.EmployeeID, ContactID = item.ContactID })),
            grid.Column(
"FullName"),
            grid.Column(
"Title")
        ));
}

By the way, I took the CSS from http://www.freecsstemplates.org/. Takes a few minutes to replace the generated default stuff Visual Studio gives you. :)

Just my $.02 so far. I plan on building on this example over time just for fun. If anyone is interested I'll be zipping up the code files...this example is available here. Enjoy!

posted @ 11:48 AM | Feedback (1)

Sunday, June 27, 2010 #

With all of the hype around Entity Framework and LINQ to SQL, there are still some of us out there that are still going old school and rolling our own POCOs. I know ORMs are supposed to be super-duper-bleeding-edge, but as much as I want to be like all of the other cool kids, for large reporting-based systems I end up writing some prettty nasty stored procs and fill up my POCOs. At my last gig we had a pretty sweet architecture built around a custom "typed table" concept. The idea was to simplify the DAL code (e.g. DataAdapter.Fill(customTable)) while getting the syntactic sugar in the IDE. Another idea I have is similar to that, except in the form of an extension method on DataReader.

First, we create a custom attribute type. This is much like LINQ to SQL where the column mapping is made between the property and the query result (or column name) from the database. We'll apply this to our POCO properties where applicable.

namespace PocoFiller {
     [AttributeUsage(AttributeTargets.Property)]
    public class ColumnMappingAttribute : Attribute {
        string source = string.Empty;
 
        public ColumnMappingAttribute() { }
 
        public string Source { get { return this.source; } set { this.source = value; } }
    }
}

Next, we create the extension method on data reader. It has to account for nullable types as well as non-nullables and we do that using some reflection trickery (yes this could be optimized and cleaned up a bit, but you get the idea)...

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Reflection;
 
namespace PocoFiller {
    static public class DataReaderExtensions {
        static public System.Collections.Generic.List<T> ToList<T>(this SqlDataReader reader) where T : new() {
            return reader.ToList<T>(true);
        }
 
        static public System.Collections.Generic.List<T> ToList<T>(this SqlDataReader reader, bool ignoreConvertExceptions) where T : new() {
            var results = new List<T>();
            while (reader.Read()) {
                T result = new T();
                results.Add(result);
                Type t = result.GetType();
 
                PropertyInfo[] properties = t.GetProperties(System.Reflection.BindingFlags.IgnoreCase
                        | System.Reflection.BindingFlags.Public
                        | System.Reflection.BindingFlags.Instance);
 
                foreach (var property in properties) {
                    object[] attributes = property.GetCustomAttributes(typeof(ColumnMappingAttribute), true);
                    if (attributes != null && attributes.Length > 0) {
                        ColumnMappingAttribute cma = attributes[0] as ColumnMappingAttribute;
                        
                        object dataValue = reader[cma.Source];
                        if (DBNull.Value == dataValue) dataValue = null;
 
                        if (dataValue != null && property.PropertyType != dataValue.GetType()) {
                            try {
                                if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
                                    Type[] genericArgs = property.PropertyType.GetGenericArguments();
                                    if (genericArgs.Length > 0) {
                                        dataValue = Convert.ChangeType(dataValue, genericArgs[0]);
                                    }
                                }
                                else {
                                    dataValue = Convert.ChangeType(dataValue, property.PropertyType);
                                }
                            }
                            catch {
                                throw;
                            }
                        }
 
                        try {
                            property.SetValue(result, dataValue, null);
                        }
                        catch (ArgumentException) {
                            if (!ignoreConvertExceptions) {
                                throw;
                            }
                        }
 
                    }
                }
            }
            return results;
        }
    }
}

Then we simply create our POCO type(s) and apply the attribute to map the properties to the column values. In this example, I alias FirstName to "first_name" just for example...

namespace PocoFiller {
    public class Employee {
        [ColumnMapping(Source = "ContactID")]
        public int ContactID { getset; }
 
        [ColumnMapping(Source = "EmployeeID")]
        public int? EmployeeID { getset; }
 
        [ColumnMapping(Source = "first_name")]
        public string FirstName { getset; }
 
        [ColumnMapping(Source = "LastName")]
        public string LastName { getset; }
 
        [ColumnMapping(Source = "Title")]
        public string Title { getset; }
 
        [ColumnMapping(Source = "HireDate")]
        public DateTime? HireDate { getset; }
    }
}

Use the ToList<T> extension method and voila!

using System;
using System.Data.SqlClient;
using System.Data;
using System.Collections.Generic;
 
namespace PocoFiller {
    class Program {
        const string connectionString = "Integrated Security=SSPI;Data Source=.;Initial Catalog=AdventureWorks";
 
        static void Main(string[] args) {
            var sql = @"select c.ContactID, e.EmployeeID, c.FirstName first_name, c.LastName, e.Title, e.HireDate
                        from HumanResources.Employee e inner join Person.Contact c
                         on c.ContactID = e.ContactID
                        where e.ManagerID = 21";
 
            var employees = new List<Employee>();
            using (var connection = new SqlConnection(connectionString))
            using (var command = new SqlCommand(sql, connection)) {
                command.CommandType = CommandType.Text;
                connection.Open();
 
                using (SqlDataReader reader = command.ExecuteReader()) {
                    employees = reader.ToList<Employee>();
                }
            }
 
            foreach (var employee in employees) {
                Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", employee.ContactID,
                    employee.EmployeeID,
                    employee.FirstName,
                    employee.LastName,
                    employee.Title);
            }
        }
    }
}

posted @ 1:39 PM | Feedback (0)

Wednesday, April 21, 2010 #

Not really a messagebox I want to see from my IDE

 

posted @ 9:58 AM | Feedback (0)

Saturday, June 06, 2009 #

Interested in writing a LINQ query to fill a DataTable? Normally I don't condone writing ugly code but let's have some fun. In this example, an anonymous type array is created followed by a DataTable. Then a LINQ query that includes a generic Func delegate encapsulating a lambda expression which is invoked to create a new DataRow based on the DataTable structure and then fill it with the properties from the anonymous type. The results (IEnumerable) are then added to the DataTable and then the DataTable is serialized to the Console output stream. In the words of Dilbert: if this is still too readable I can photocopy it and then run it through the fax machine a few times...

using System;

using System.Data;

using System.Linq;

 

namespace App {

    class Program {

        static void Main(string[] args) {

            var pretendThisIsADatabaseTable = new[] { new { Name = "Steve", Age = 33 }, new { Name = "Doug", Age = 34 } };

 

            //Create empty destination data table

            DataTable personTable = new DataTable();

            personTable.TableName = "PersonTable";

            personTable.Columns.Add("Name", typeof(string));

            personTable.Columns.Add("Age", typeof(int));

 

            //Query anonymous type array as DataRow objects

            var results = from person in pretendThisIsADatabaseTable

                          select new Func<DataRow, string, int, DataRow>(

                              (DataRow row, string name, int age) => {

                                  row["Name"] = name;

                                  row["Age"] = age;

                                  return row;

                              }

                          )

                          .Invoke(personTable.NewRow(), person.Name, person.Age);

 

            //put rows into data table

            results.ToList().ForEach(row => personTable.Rows.Add(row));

 

            personTable.WriteXml(Console.Out);

        }

    }

}




EDIT: everything above is pure cheese. Here's a real solution via my old co-worker Shawn :)

class Program
    {
        static void Main(string[] args)
        {
            var data = new[] {
                            new {FirstName="Bill", LastName="Clinton", ID=1, WasPresident=true}
                            , new {FirstName="John", LastName="McCain", ID=2, WasPresident=false}
                            , new {FirstName="George", LastName="Bush", ID=2, WasPresident=true}
            };
 
            DataTable table = (from d in data
                               where d.WasPresident==true
                               select d).AsDataTable();
 
            table.WriteXml(Console.Out);
        }
    }
 
Extension method below is required for the sugar above (AsDataTable)
 
static class Extensions
    {
        public static DataTable AsDataTable<T>(this IEnumerable<T> enumberable)
        {
            DataTable table = new DataTable("Generated");
 
            T first = enumberable.FirstOrDefault();
            if (first == null)
                return table;
 
            PropertyInfo[] properties = first.GetType().GetProperties();
            foreach (PropertyInfo pi in properties)
                table.Columns.Add(pi.Name, pi.PropertyType);
 
            foreach (T t in enumberable)
            {
                DataRow row = table.NewRow();
                foreach (PropertyInfo pi in properties)
                    row[pi.Name]=t.GetType().InvokeMember(pi.Name,BindingFlags.GetProperty,null, t, null);
                table.Rows.Add(row);
            }
 
            return table;
        }
    }

posted @ 9:20 AM | Feedback (0)

Wednesday, June 03, 2009 #

For a while now I've been in "lurker" mode in all of the .NET community blogs. One of the interesting transitions that's been going on is the demo code for...well...just about everything including LINQ (whether it's the ADO.NET Entity Framework, LINQ-to-SQL, etc) or even ASP.NET MVC which includes using LINQ. Specifically, the removal of application "layers." Don't get me wrong, I think logically there's great patterns of cohesion to support the whole ability necessary for things like ASP.NET MVC's indirection. However sometimes, looking at this code (not to mention writing it) gives me a sense of going backwards into monolithic-type architecture. So I've been pondering from an architecture/design perspective how to implement all of this cool stuff but at the same time keep my mind at ease. So in playing around with Northwind (my favorite example database of all time :) )

  • Data Access Layer - Simply generate an ADO.NET Entity Framework Model  (.edmx)
  • Business Interface - This layer contains our use-cases where the UI leverages its services via messages, data structures, whatever makes sense.
  • Common Library - Since I don't like the idea of ADO.NET EF objects as my DTOs, I'd rather roll my own POJO (C#) objects (or even use DataTable or derivative of it).

//Common Library Assembly DTO Class

namespace Northwind.Common {

    public class CustomerEntity {

        public string Address { get; set; }

        public string City { get; set; }

        public string CompanyName { get; set; }

        public string ContactName { get; set; }

        public string ContactTitle { get; set; }

        public string Country { get; set; }

        public string CustomerID { get; set; }

        public string Fax { get; set; }

        public string Phone { get; set; }

        public string PostalCode { get; set; }

        public string Region { get; set; }

    }

}

 

//Business Interface (or Facade) Assembly

namespace Northwind.BusinessInterface {

    static public class CustomerInterface {

 

        static public List<Northwind.Common.CustomerEntity> GetAll() {

            using (var nwEntities = new NorthwindAPI.NorthwindEntities()) {

                var results = from custs in nwEntities.Customers

                              select new Northwind.Common.CustomerEntity() {

                                  Address = custs.Address,

                                  City = custs.City,

                                  CompanyName = custs.CompanyName,

                                  ContactName = custs.ContactName,

                                  ContactTitle = custs.ContactTitle,

                                  Country = custs.Country,

                                  CustomerID = custs.CustomerID,

                                  Fax = custs.Fax,

                                  Phone = custs.Phone,

                                  PostalCode = custs.PostalCode,

                                  Region = custs.Region

                              };

 

                Debug.WriteLine(((ObjectQuery)results).ToTraceString());

 

                return results.ToList();

            }

        }

    }

}

 

//Client Console App

namespace TestClient {

    class Program {

        static void Main(string[] args) {

            List<Northwind.Common.CustomerEntity> custs = Northwind.BusinessInterface.CustomerInterface.GetAll();

            custs.ForEach(c => Console.WriteLine(c.CustomerID));

        }

    }

}

 

This is nothing new as far as architecture ideas go - many designers are running into the same philosophical conflict, but what is interesting is the other questions it raises. When you've had tried-and-true methodologies for many years, it's difficult to simply jump ship and feel good about compiling LINQ queries into your code versus writing stored procs, or using ORM entities throughout your application versus creating more vanilla DTOs.  For something intended as only a Web application, how much do I care about tier separation versus logic-layers? We're assuming the application will end up only being a Web application. If it turns into a system that then needs to become published into a service, do you then refactor at that point, or do you plan ahead by separating layers as I'm inclined to do? What is the maintenance impact? How can we assume that our database is already designed and fleshed-out enough to the point where it is OK to use an ORM against it? Does this mean we design our database first? Generally it makes more sense to come up with our use-cases first, which is more of the BusinessInterface layer (and UI to some extent) before we identify what our system entity/attribute and data storage look like. If we start with use-cases (or tests for our TDD enthusiasts), will the database ever change during the development cycle? If so, will having to refactor my model and the corresponding ORM-generated classes have a significant impact on my team during development?

It is interesting stuff to think about. I'll keep lurking and see what others come up with too. Drop me a line if you have any interesting opinions on the subject. :)

 

posted @ 10:15 AM | Feedback (0)

Tuesday, March 10, 2009 #

Long title - but that says it all. Today I wasted a large chunk of time trying to figure out why I was getting a challenge dialog on my ASP.NET site. The site was configured to use an application pool that I made. The app pool was set to run as a domain account identity and the NTFS permissions on all of the folders looked fine...but I kept getting prompted to enter my user credentials. Buried in a sea of security event log entries were several kerberos errors. It turns out that on this server, kerberos was on by default.

The solution was to run a simple command line to force IIS to use NTLM:

adsutil.vbs set w3svc/3/NTAuthenticationProviders "NTLM"

http://support.microsoft.com/default.aspx?scid=kb;EN-US;871179

Hope this saves someone some time if you run into the same problem.  :)

posted @ 7:44 PM | Feedback (0)