Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Real World MVC
1. A Real World MVC Application
with some other groovy stuff thrown in
James Johnson
Developer Advocates
Los Angeles C# User Group
Tuesday, November 1, 2011
2. Who I am
Founder and President of the Inland Empire .NET User’s Group
Three time and current Microsoft MVP – CAD
Software developer by day
Serial netrepreneur by night
Partner in Developer Advocates
Easily distracted by acronyms and shiny objects
3. Developer Advocates
Helping small to medium size software companies through
• Evangelism and Advocacy
• Blog Articles
• Webcasts
• Presentations
• User group meetings
• Code Camps
• Regional Tech Conferences
4. Agenda
• Demo of web site
• Comparing Web Forms to MVC
• Entity Framework
• Administration
• Bad words
• Uploading images
• Ajax and JavaScript
• Ratings
• Paging
• Searching
• Tweeting
5. Aphirm.it
• New thought and spiritual affirmations
• Always have been a new thinker and hippie
• Sister site to Windows Phone 7 app
• Some bad words will be shown for the demo
7. ASP.NET MVC
• Models
• Views
• Controllers
• ViewModels
• No Postbacks
• Very limited use of existing server controls
• Clean HTML makes CSS and JavaScript easier
• What all the cool kids are using these days
10. Entity Framework
• Version 4 released with .NET 4.0
• New version (4.1) allows for model-first, code-first or
database-first development
• Maps POCO objects to database objects
• A collection of things instead of a dataset of rows
• “things” are the entities
11. Entity Framework
• Why?
• Adds a layer of abstraction between database and code
• DBA can structure database how they want
• Developer can map to the database how they want
• Rename entities for more comfortable use
• Entity Framework handles the mappings
12. Entity Framework
• Entity Data Model – EDM
• Deals with the entities and relationships they use
• Entities
• Instance of EntityType
• Represent individual instances of the objects
• Customer, book, shoe, aphirmit
• Fully typed
• Relationships between look up tables are mapped as
associations in the EDMX
13. Entity Framework
• csdl
• Conceptual Schema Definition Language
• The conceputal schema for the EDM
• EntityContainer, EntitySet, EntityType definitions
• ssdl
• Store Schema Definition Language
• Schematic representation of the data store
• msl
• Mapping Specification Language
• Sits between the csdl and ssld and maps the entity
properties
14. Entity Framework
Lazy Loading
A design pattern to defer initialization until needed
context.ContextOptions.DeferredLoadingEnabled=true;
List<BookCase> cases = context.BookCase.ToList();
foreach(var bookcase in cases)
{
var books = bookcase.Books;
}
15. Entity Framework
Eager Loading
Use if you will be needing every related entity
List<BookCase> cases =
context.BookCase.Include(“Books”).ToList();
foreach(var bookcase in cases)
{
var books = bookcase.Books;
}
16. Entity Framework
Contexts
• The context is the instance of the entity
• Passing an entity around to tiers breaks the context
• Just like the song, make sure the context remains the same
17. Entity Framework
Contexts
public class ModelHelper
{
private static CourseEntities _db;
public static CourseEntities CourseEntities
{
get
{
if(_db == null)
_db = new CourseEntities();
return _db;
}
set { _db = value; }
}
}
private readonly CourseEntities _db = new CourseEntities();
18. Entity Framework
Contexts
private Student AddStudent(Student student, Course course)
{
student.Courses.Add(course);
_db.SaveChanges();
}
Didn’t work because course was in a different context
private Student AddStudent(Student student, Course course)
{
var newStudent = GetStudent(student.Id);
var newCourse = GetCourse(course.Id);
newStudent.Courses.Add(newCourse);
_db.SaveChanges();
}
19. Entity Framework
LINQ to Entities
• Very similar to LINQ to SQL
• Major difference
LINQ to SQL – SingleOrDefault()
LINQ to Entities – FirstOrDefault()
21. Entity Framework
LINQ to Entities
Adding (Inserting)
public void AddThing(Thing thing)
{
_db.AddToThings(thing) //this will be a list
_db.SaveChanges() // of AddTo<Entities>
}
Editing (Updating)
public void EditThing(Thing thing)
{
_db.Things.Attach(new Thing{Id=thing.Id});
_db.Things.ApplyCurrentValues(thing);
_db.SaveChanges();
}
22. Entity Framework
Repositories
• Repository pattern encapsulates code into a separate class
• Allows for easy changes
• Can use it to swith database providers or new technologies
• Stephen Walther – ASP.NET MVC Framework, Sams
• stephenwalther.com
• “Download the code” link
• Add the two projects to your solution
• Add references to your project
23. Entity Framework
Repositories
using GenericRepository
public class MyController
{
private readonly IGenericRepository _repo;
private readonly CourseEntities _db;
public MyController()
{
_repo = new
EFGenericRepository.EFGenericRepository(_db);
}
}
24. Entity Framework
Repositories
// get
_repo.Get<Thing>(id);
// edit
_repo.Edit(thing);
// create
_repo.Create(thing);
// delete
_repo.Delete(thing);
// list
var list = _repo.List<Thing>().Where(x =>
x.Name.Contains(myName));
25. Aphirm.it
• New thought and spiritual affirmations
• Always have been a new thinker and hippie
• Sister site to Windows Phone 7 app
• Some bad words will be shown for the demo
26. Aphirm.it
• Administration
• Approve aphirm.its
• Set IsApproved to true
• Send email to user
• Check for badges
• Tweet new aphirm.it
27. Aphirm.it
SetBadges
public void SetBadges(Aphirmit affirmation)
{
var memberId = affirmation.MemberId;
_db.ResetPostBadges(memberId); //SP in database
//get all the aphirmations the user has created
var aphList =
_aphirmitHelper.GetAphirmitsByMember(memberId)
.Where(x => x.IsApproved.Equals(true)).ToList();
var aphCount = aphList.Count;
if (aphCount >= 0) //beginner
AddPostBadge(100, memberId);
}
28. Aphirm.it
AddPostBadge
private void AddPostBadge(int badgeId, int memberId)
{
// BadgeIdis the internal Id
varbadge = _db.Badges.Single(x => x.BadgeId.Equals(badgeId));
varnewBadge = new MemberBadge()
{ MemberId = memberId,
BadgeId = badge.Id
};
_db.AddToMemberBadges(newBadge);
_db.SaveChanges();
}
30. Aphirm.it
Uploading Images
[HttpPost]
public ActionResult
UploadAvatar(IEnumerable<HttpPostedFileBase> attachments)
{
if (attachments == null)
return Content("no file was uploaded");
HttpPostedFileBase img = attachments.ElementAt(0);
var imagePath = _generalHelper.UploadAvatar(img);
return Json(new { status = imagePath }, "text/plain");
}
31. Aphirm.it
Uploading Images
function onSelect(e) {
var extArray = new Array(".png", ".gif", ".jpg", ".jpeg");
var $status = $("div.status");
$status.hide();
var file = e.files[0];
var inArray = $.inArray(file.extension, extArray);
if (inArray == -1) {
$("div.status").html("Sorry, only PNG, GIF, or JPG
images are allowed.").show();
e.preventDefault();
return false;
}
}
32. Aphirm.it
Uploading Images
function onAvatarComplete() {
var userName = $("#avatarPath").val();
var url = "/Profile/GetMemberAvatar/" + userName;
$.getJSON(url, function (json) {
//append something to the string so that the image will be
refreshed.
json = json + "?" + new Date();
$("#memberAvatar").attr("src", json);
});
}
33. Aphirm.it
User Registration
• Be as minimal as possible
• Don’t ask for all possible data at start
• Go easy, can always come back for more
34. Aphirm.it
User Registration
• Use Ajax/JavaScript to help the user
• Check for existing username before submitting
• Check for existing email and format
35. Aphirm.it
Ajax/JavaScript
Validate username
function validateUserName(elem) {
var $elem = $(elem);
var userName = $elem.val();
$elem.attr("valid", true);
var url = "/Account/IsExistingUser/";
$.get(url, { name: userName }, function (json) {
if (json) {
$("#userNameTaken").fadeIn();
$elem.attr("valid",
false).removeClass("valid").addClass("invalid");
} else {
$("#userNameTaken").fadeOut();
$elem.removeClass("invalid").addClass("valid");
}
});
}
37. Aphirm.it
Paging
• Use paging when there is more data than can easily be viewed
• Easy to add page to display
• There are several jQuery grids available
• I just like mine
38. Aphirm.it
Paging
[HttpGet]
public ActionResult All(int? id)
{
int pageIndex;
if (id == null)
pageIndex = 0;
else
pageIndex = (int)id - 1;
var pageSize = _generalHelper.GetListSize();
var affirmationList =
_affirmationHelper.GetAphirmitRange(pageIndex, pageSize);
decimal totalAffirmations = _affirmationHelper.GetApprovedCount();
var pageNumbers = Math.Ceiling(totalAffirmations / pageSize);
ViewBag.PageNumbers = pageNumbers;
ViewBag.CurrentPage = pageIndex;
ViewBag.ListCount = pageSize;
ViewBag.Total = totalAffirmations;
return View("View", affirmationList);
39. Aphirm.it
Bad Words
Need to check text for any bad words the user may have used
var status = false;
var badWords = _repo.List<BadWord>().ToList();
foreach (var bWord in badWords.Where(bWord =>
aphirmit.Text.IndexOf(bWord.Word) > -1))
{
status = true;
}
40. Aphirm.it
Bad Words
var hasBadWords = _affirmationHelper.HasBadWords(affirmation);
if (hasBadWords)
{
TempData["HasBadWords"] = true;
return RedirectToAction("Edit", new {id = affirmation.Id});
}
41. Aphirm.it
Bad Words
if(TempData["HasBadWords"] != null && (bool)
TempData["HasBadWords"])
{
<div class="editor-label error">
Your Aphirmit has some unsavory words. If you would
like to have it published, you will need to clean it up.
</div>
}
43. Thank you
James Johnson
Email: james.johnson@developeradvocates.com
Blog: www.latringo.me
Twitter: latringo
PayPal: members@iedotnetug.org
Slides: www.slideshare.net/latringo/real-world-mvc
Inland Empire .NET User’s Group
2nd Tuesday of each month
www.iedotnetug.org
Hinweis der Redaktion
This model helper creates the context if it is null, otherwise it just returns the context
This is how you setup the controller to use the repository. Create instances to the repository and context, then new up the repository with the context
As you can see, using the repository, greatly streamlines your code in how you interact with the entities.
Show code – AphirmitController – 139, Views/Aphirmits/View.cshtml - 74