MVC

by MikeHogg25. June 2012 10:06

 

In my second or third MVC3 project, I have really enjoyed Model Binding and my familiarity with the MVVM pattern in WPF translated well.  Web projects are much more enjoyable in c# for me than they were in the old webforms days.  Additionally, I found features in c# 4.0 that I really like, and frequently find myself taking advantage of nullable parameters, and lambda expressions.  I was asked to replicate legacy code in MVC3, which simply retrieved a feed of xml from a web service and turned them into a searchable data store of links.  Caching was a new feature of this.  The number of links was relatively small enough, that I decided to use file, not database, for the store, and simply creating my data model class with simple .net types allowed me to save my models in Properties.Settings.   In this code sample you will see the model, including a list of IEnumerable Headlines of either type of an enum, since we use this to serve up actually two different pages- InTheNews for public third party publications (WBAL, Women’s World) and NewsReleases (company generated PR)…

 

 

 

namespace SomeBase.Models
{
public enum NewsType { InTheNews, NewsReleases };
public class NewsModel
    {
public string Title { get { return (NewsType == NewsType.InTheNews) ? "In The News" : "News Releases"; } set { var x = value; } }
public NewsType NewsType { get; set; }
public DateTime LastUpdateDate { get; set; }
public IEnumerable<Headline> Headlines { get; set; }
public string SearchString { get; set; }
public string SearchType { get; set; }
public List<System.Web.Mvc.SelectListItem> SearchTypes { get; set; }
        [DisplayFormat(DataFormatString = "{0:" + MH.Web.Mvc3.lib.CONST.DATEFORMATSTRING_SHORT + "}", ApplyFormatInEditMode = true)]
public DateTime? SearchStartDate { get; set; }
        [DisplayFormat(DataFormatString = "{0:" + MH.Web.Mvc3.lib.CONST.DATEFORMATSTRING_SHORT + "}", ApplyFormatInEditMode = true)]
public DateTime? SearchEndDate { get; set; }
    }
public class Headline
    {
        [DisplayFormat(DataFormatString = "{0:" + MH.Web.Mvc3.lib.CONST.DATEFORMATSTRING_SHORT + "}")]
public DateTime PostDate { get; set; }
public HtmlString Link { get; set; }
public NewsType NewsType { get; set; }
public string ArticleType { get; set; }
    }
}

In the Controller, I use an abstract class because this might be served from the Desktop Web Site, or another project, for the Mobile site.  Most of the functionality is in the abstract controller, but the final  controller must implement the Save and Load functions of whatever data store they use, in our case the Properties.Settings.   Here I get to use the new Xml.Linq libraries which make loading an xml document from a url and parsing it one liners…

 

 

private Models.NewsModel GetFilteredNews(Models.NewsModel news)
        {
            Models.NewsModel allnews = GetAllNews(news.NewsType);
            news.SearchTypes = allnews.SearchTypes; // recreate dropdownlist- lost on postback?
            news.Headlines = allnews.Headlines
                .Where(h => h.NewsType == news.NewsType &&
                    (news.SearchEndDate == null || h.PostDate < news.SearchEndDate) &&
                    (news.SearchStartDate == null || h.PostDate > news.SearchStartDate) &&
                    (news.SearchType == null || news.SearchType == h.ArticleType) &&
                    (String.IsNullOrEmpty(news.SearchString) || h.Link.ToString().ToLower().Contains(news.SearchString.ToLower()))
                    ).OrderByDescending(h => h.PostDate);
 return news;
        }
private Models.NewsModel GetAllNews(Models.NewsType newstype)
        {
            Models.NewsModel news = GetSavedNews();
 if (news.LastUpdateDate < DateTime.Now.AddDays(-1))
            {
 // get feed from vocus 
                Models.Headline[] latest = GetVocusFeed();
 if (latest.Count() > 0)  // if vocus is down?
                {
                    news.Headlines = latest;
                    news.LastUpdateDate = DateTime.Now;
                    SaveNews(news);
                }
            }
 // process for the view
            news.NewsType = newstype;
            news.SearchTypes = news.Headlines.Where(h=>h.NewsType == newstype && !String.IsNullOrEmpty(h.ArticleType))
                .OrderBy(h => h.ArticleType).Distinct()
                .Select(h => new System.Web.Mvc.SelectListItem
                {
                    Text = h.ArticleType,
                    Value = h.ArticleType
                }).ToList();
 return news;
        }
private Models.Headline[] GetNewsFeed()
        {
            List<Models.Headline> headlines = new List<Models.Headline>();
            System.Xml.Linq.XDocument collateral = System.Xml.Linq.XDocument.Load(
http://somenewsfeed.com/Custom/CustomXmlFeed.aspx?something=something);
            System.Xml.Linq.XDocument news = System.Xml.Linq.XDocument.Load(
                http://somenewsfeed.com/Custom/CustomXMLFeed.aspx?somethingelse=something);
            headlines.AddRange(news.Descendants(System.Xml.Linq.XName.Get("NewsResults")).Select(e => new Models.Headline
            {
                ArticleType = GetField(e, "News_MediaOutletSortName"),
                Link = GetLink(e, "News"),
                NewsType = Models.NewsType.InTheNews,
                PostDate = DateTime.Parse(GetField(e, "News_NewsDate"))
            }).Where(e => e.Link != null));
            headlines.AddRange(collateral.Descendants(System.Xml.Linq.XName.Get("SomeResults")).Select(e => new Models.Headline
            {
                ArticleType = GetField(e, "A_Name"),
                Link = GetLink(e, "ALink"),
                NewsType = Models.NewsType.NewsReleases,
                PostDate = DateTime.Parse(GetField(e, "PublishDate"))
            }).Where(e => e.Link != null));
 return headlines.ToArray();
        }

 

 

The html page is simple enough, with clean model binding and the html helpers.  Note the handy “If” MvcHtmlString extension and the use of templating for Headlines. 

 

 

<h2 style="margin:30px;">@Html.DisplayFor(m=>m.Title)</h2>
@using (Html.BeginForm())
{
        @Html.ActionLink("Request Customized News", "RequestCustomizedNews").If(Model.NewsType == SomeBase.Models.NewsType.NewsReleases)
<div style="float:left; width:200px; display:block;">
 <h3 >Search Archives</h3>
 <div class="display-label">Headline</div>
 <div class="display-field">
                @Html.EditorFor(model => model.SearchString)
 </div>
 
 <div class="display-label">Type</div>
 <div class="display-field">
                @Html.DropDownListFor(m=>m.SearchType, Model.SearchTypes, "All")
 </div>
 <div class="display-label">From:</div>
 <div class="display-field">
                @Html.TextBoxFor(model => model.SearchStartDate, new { @class = "calendar", @Value=Model.SearchStartDate.HasValue? Model.SearchStartDate.Value.ToString(MH.Web.Mvc3.lib.CONST.DATEFORMATSTRING_SHORT): null })
 </div>
 <div class="display-label">To:</div>
 <div class="display-field">
                @Html.TextBoxFor(model => model.SearchEndDate, new { @class = "calendar" })
 </div>
 <input type="submit" value="Go" />
</div>
<div style="float:left;">
 <h3>Recent Articles</h3>
 <div>
                @Html.DisplayFor(m => m.Headlines)
 </div>
</div>
</div>
}
I am able to serve this one page “News” as two different Urls- InTheNews and NewsReleases, because of the enum, and MVC’s routing mechanism, which allows me to add a custom route, with a custom constraint.  (I have become a DRY fan, and both pages were nearly identical as it was, so I could hardly bear to repeat the code of two Views, two controller Gets, two controller Posts.)  I would make this more dynamic the next time I touched it by iterating through an enum parameter instead of passing hardcoded values but it was a neat exercise nonetheless…

 

 

 

{Global.asax}

 // Abouts
            routes.MapRoute(
                "About", // Route name
                "About/{newstype}", // URL with parameters
 new { controller = "About", action = "News" }, // Parameter defaults
 new { newstype = new OptionalConstraint(new string[]{"InTheNews", "NewsReleases"}) },
 new string[1] { "Some.Controllers" }
            );
public class OptionalConstraint : IRouteConstraint
    {
string[] _mappedactions;
public OptionalConstraint(string[] mappedactions)
        {
            _mappedactions = mappedactions;
        }
public bool Match(System.Web.HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
 if (routeDirection == RouteDirection.IncomingRequest)
            {
 return values[parameterName] == UrlParameter.Optional || _mappedactions.Contains(values[parameterName].ToString(), StringComparer.OrdinalIgnoreCase);
 
            }
 return false;
        }
    }

Tags:

MVC

Add comment

biuquote
  • Comment
  • Preview
Loading

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more.amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here