I've got class named PagedList which looks like this:
public class PagedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public bool HasPrevious
{
get
{
return (this.PageIndex > 0);
}
}
public bool HasNext
{
get
{
return (this.PageIndex + 1 < this.TotalPages);
}
}
public PagedList(IEnumerable<T> source, int pageIndex, int pageSize)
{
this.PageIndex = pageIndex;
this.PageSize = pageSize;
this.TotalCount = source.Count();
this.TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(this.PageIndex * this.PageSize).Take(this.PageSize));
}
}
In my Customers controller I use it like so:
public ActionResult Index(int? page)
{
IRepository<UserInfo> profile = ObjectFactory.GetInstance<IRepository<UserInfo>>();
const int pageSize = 25;
IQueryable<UserInfo> profiles = profile.GetAll().OrderBy(x => x.LastName).AsQueryable<UserInfo>();
var pagedCustomers = new PagedList<UserInfo>(profiles, page ?? 0, pageSize);
return View(pagedCustomers);
}
And finally in my Index.cshtml file I set up my Model like so:
#model System.Web.Mvc.ViewPage<PagedList<Clusteris.Data.UserInfo>>
And When I go Model. it's a huge list, nothing to do with the PagedList I created in the Customers controller.
Anyone got any ideas on what I'm doing wrong?
UPDATE:
Screenshot:
PagedList is enumerable. To access individual UserInfo object use #foreach(var userInfo in Model)
Someone else mentioned about the use of IQueryable. It's a good practice to use ToArray() or ToList() at boundaries. Your repository is a boundary, so I would consider moving the paging logic to it and make it return either the PagedList object or the data necessary to construct one.
Your model class right now is System.Web.Mvc.ViewPage<PagedList<Clusteris.Data.UserInfo>> as opposed to PagedList<Clusteris.Data.UserInfo> - this is likely why you are seeing all of this strange data on your model.
System.Web.Mvc.ViewPage is a component used to render models and shouldn't be the model itself. Change your model type to just PagedList<Clusteris.Data.UserInfo> and it should work.
Related
I am currently using <ObjectGraphDataAnnotationsValidator/> to validate complex models.
So far so good, except that there is also a requirement to check against the database to see if a record with the same value already exists.
I have tried implementing the <CustomValidator/> as per advised in https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-5.0#validator-components
However, it seems to only work for the top level properties.
And the <ObjectGraphDataAnnotationsValidator/> does not work with remote validations (or does it!?)
So say that I have:
*Parent.cs*
public int ID {get;set;}
public List<Child> Children {get;set;}
*Child.cs*
public int ID {get;set;}
public int ParentID {get;set}
public string Code {get;set;}
<EditForm Model="#Parent">
.
.
.
Child.Code has a unique constraint in the database.
I want to warn users "This 'Code' already exists! Please try entering a different value.", so that no exceptions will be thrown.
For now, I am a bit lost as to where my next step is.
In the past with asp.net core mvc, I could achieve this using remote validations.
Is there an equivalent to remote validations in blazor?
If not, what should I do to achieve the same result, to remotely validate the sub properties for complex models?
Any advises would be appreciated. Thanks!
[Updated after #rdmptn's suggestion 2021/01/24]
ValidationMessageStore.Add() accepts the struct FieldIdentifier, meaning that I can simply add a overload of the CustomValidator.DisplayErrors to make it work:
public void DisplayErrors(Dictionary<FieldIdentifier, List<string>> errors)
{
foreach (var err in errors)
{
messageStore.Add(err.Key, err.Value);
}
CurrentEditContext.NotifyValidationStateChanged();
}
Full example below:
#using Microsoft.AspNetCore.Components.Forms
#using System.ComponentModel.DataAnnotations
#using System.Collections.Generic
<EditForm Model="parent" OnSubmit="Submit">
<ObjectGraphDataAnnotationsValidator></ObjectGraphDataAnnotationsValidator>
<CustomValidator #ref="customValidator"></CustomValidator>
<ValidationSummary></ValidationSummary>
#if (parent.Children != null)
{
#foreach (var item in parent.Children)
{
<div class="form-group">
<label>Summary</label>
<InputText #bind-Value="item.Code" class="form-control"></InputText>
</div>
}
}
<input type="submit" value="Submit" class="form-control"/>
</EditForm>
#code{
private CustomValidator customValidator;
private Parent parent;
public class Parent
{
public int Id { get; set; }
[ValidateComplexType]
public List<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Code { get; set; }
}
protected override void OnInitialized()
{
parent = new Parent()
{
Id = 1,
Children = new List<Child>()
{
new Child()
{
Id = 1,
ParentId = 1,
Code = "A"
},
new Child()
{
Id = 1,
ParentId = 1,
Code = "B"
}
}
};
}
public void Submit()
{
customValidator.ClearErrors();
var errors = new Dictionary<FieldIdentifier, List<string>>();
//In real operations, set this when you get data from your db
List<string> existingCodes = new List<string>()
{
"A"
};
foreach (var child in parent.Children)
{
if (existingCodes.Contains(child.Code))
{
FieldIdentifier fid = new FieldIdentifier(model: child, fieldName: nameof(Child.Code));
List<string> msgs = new List<string>() { "This code already exists." };
errors.Add(fid, msgs);
}
}
if (errors.Count() > 0)
{
customValidator.DisplayErrors(errors);
}
}
}
The [Remote] validation attribute is tied to MVC and is not usable for Blazor.
ObjectGraphDataAnnotationsValidator is not enough. In addition, each property, that represents an object with possible validation needs to be decorated with a [ValidateComplexType] attribute.
In your CustomValidatior, you can see DI to get your API service to call your API and validate your constraint.
public class Parent
{
...other properties...
[ValidateComplexType]
public List<Child> Children {get; set; }
}
public class Child
{
...other properties...
[Required]
[IsUnique(ErrorMessage = "This 'Code' already exists! Please try entering a different value.")]
public String Code {get; set;}
}
public class IsUniqueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = (IYourApiService)validationContext.GetService(typeof(IYourApiService));
//unfortunately, no await is possible inside the validation
Boolean exists = service.IsUnique((String)value);
if(exists == false)
{
return ValidationResult.Success;
}
return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName });
}
}
You might want to check out FluentValidation as this library provide features for asynchronous validation. I'm not sure if this validator can be used inside Blazor WASM.
I have the following viewModel:
namespace Flashcard.Models
{
public class CreateCardViewModel
{
[HiddenInput(DisplayValue = false)]
public int SetId { get; set; }
[Required]
public ICollection<Side> Sides { get; set; }
}
}
I use this ViewModel against the Card Controller:
public class CardController : Controller
{
//
// GET: /Card/
public ActionResult Create(int setId)
{
var model = new CreateCardViewModel();
var side = new Side() {Content = "Blank Side"};
model.SetId = setId;
model.Sides.Add(side);
return View(model);
}
}
However when I call the Create action, I get a nullReferenceException because model.Sides is null, which does not seem to be the same as empty. I believe I created an empty ICollection Sides in the ViewModel - why is it null in the controller?
For some context - a Card can have one or several Sides. I'm trying to always add a Side whenever a Card is created.
you need to initiate a Collection and assign it to the property of your object as follows:
public class CardController : Controller
{
//
// GET: /Card/
public ActionResult Create(int setId)
{
var model = new CreateCardViewModel();
var side = new Side() {Content = "Blank Side"};
model.SetId = setId;
model.Sides = new List<Side>();
model.Sides.Add(side);
return View(model);
}
}
Your collection is null. make one and assign to your prop.
I am working on an MVC project where controller actions deal with Assets. Different controllers take in the assetId parameter in different way: Some controllers simply get int assetId, other int id, and other using a complex object AssetDTO dto (which contains a property that holds the assetId)
I am writing an ActionFilter that is added to the action method and is provided with the actionParameter name where I can get the asset value.
Action Method:
[AssetIdFilter("assetId")]
public ActionResult Index(int assetId)
{
...
}
The attribute is defined as:
public class AssetIdFilterAttribute : ActionFilterAttribute
{
public string _assetIdParameterKey { get; set; }
public AssetIdFilterAttribute (string assetIdParameterKey)
{
_assetIdParameterKey = assetIdParameterKey;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int assetId;
if (Int32.TryParse(filterContext.ActionParameters[_assetIdParameterKey].ToString(), out assetId))
{
......
}
}
This works as expected, but will only work when the assetId is provided as a primitive. I am not sure what to do when the assetId is provided within a complex object into the action method.
Will I need to parse each object differently depending on the type? I am hoping I can specify some kind of dot-notation in the AssetIdFilter to tell it where the assetId is located: dto.assetId
Any way I can use dynamics? or reflection?? ect.???
and here dynamic comes to the rescue.you can change the actionFilterAttribute to be :
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
dynamic assetIdHolder = filterContext.ActionParameters[_assetIdParameterKey];
if (assetIdHolder.GetType().IsPrimitive)
{
//do whatever with assetIdHolder
}
else
{
//do whatever with assetIdHolder.assetId
}
}
cheers!
Well, yes, you answered your question. One way would be to use dot notation:
//simple case:
[AssetId("id")]
public ActionResult Index(string id) {
//code here
}
//complex case:
[AssetId("idObj", AssetIdProperty = "SubObj.id")]
public ActionResult index(IdObject idObj) {
//code here
}
And AssetIdAttribute is as follows:
public class AssetIdAttribute : ActionFilterAttribute
{
public string _assetIdParameterKey { get; set; }
public string AssetIdProperty { get; set; }
public AssetIdFilterAttribute(string assetIdParameterKey)
{
_assetIdParameterKey = assetIdParameterKey;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int assetId;
var param = filterContext.ActionParameters[_assetIdParameterKey];
int.TryParse(GetPropertyValue(param, this.AssetIdProperty).ToString(), out assetId);
//you code continues here.
}
private static string GetPropertyValue(object souce, string property)
{
var propNames = string.IsNullOrWhiteSpace(property) || !property.Contains('.') ? new string[] { } : property.Split('.');
var result = souce;
foreach (var prop in propNames)
{
result = result.GetType().GetProperty(prop).GetValue(result);
}
return result.ToString();
}
}
The code does not have null checks when calling ToString and when calling GetProperty though. Also, it does not check the success of TryParse. Please apply these corrections when used.
Maybe this code could be written using dynamic, but at the end dynamic usage is compiled into object using reflection (something like what I have done here), thus no big difference to me.
Also, maybe it would be more clear to have a parameter like "idObj.SubObj.id", but that again depends on the preference, and the code will become a little bit more complex.
I am creating a fluent HtmlHelper in MVC - to create a grid based on HTML.
I am aware of mvc contrib and WebGrid - but I am making my own and have a specific problem:
I have to enter this:
#Html.DSGridFor().AddColumn(x=>x.FirstOrDefault().Message)
but I want to be able to type this:
#Html.DSGridFor().AddColumn(x=>x.Message)
The code that gets called when I start with #Html.DSGridFor() - taking in the page based model.
public static DSGridHelper<TModel> DSGridFor<TModel>(this HtmlHelper<TModel> html)
{
return new DSGridHelper<TModel>(html);
}
and then within the class DSGridHelper I have this:
public DSGridHelper<TModel> AddColumn(Expression<Func<TModel, dynamic>> property, string HeaderText = null)
{
string ColumnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
public List<DSGridColumn> DSColumnList { get; set; }
and the column class at the moment is really basic:
public class DSGridColumn
{
public DSGridColumn()
{
}
public string ColumnName { get; set; }
public string HeaderText { get; set; }
}
I can get this code working fine with string based column names, but I want the declaring code in the razor page to be simple in format and strongly typed. At the moment I have to type x=>x.First().Message but I really only need x=>x.Message to identify the column.
I appreciate any help.
UPDATE
Thanks to Justin I can now provide my/our code.
View:
#(Html.DSGridFor3().AddColumn(x => x.Message)
.AddColumn(x => x.Host)
.ToMvcString())
HTML Helper call:
public static DSGridHelper3<T> DSGridFor3<T>(this HtmlHelper<IEnumerable<T>> htmlHelper)
{
return new DSGridHelper3<T>(htmlHelper);
}
Returning class:
public class DSGridHelper3<T>
{
private HtmlHelper _htmlHelper;
//private IEnumerable<T> _dataList;
public List<DSGridColumn> DSColumnList { get; set; }
public DSGridHelper3(HtmlHelper<IEnumerable<T>> htmlHelper)
{
_htmlHelper = htmlHelper;
// _dataList = htmlHelper.ViewData.Model;
DSColumnList = new List<DSGridColumn>();
}
public DSGridHelper3<T> AddColumn(Expression<Func<T, object>> property)
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = columnName;
DSGC.HeaderText = columnName;
DSColumnList.Add(DSGC);
return this;
}
public MvcHtmlString ToMvcString()
{
sb.Append("<table>");
sb.Append("<tr>");
sb.Append("<td>");
sb.Append("hello world within a table");
sb.Append(#"</td>");
sb.Append("<td>");
sb.Append("hello world within a table");
sb.Append(#"</td>");
sb.Append(#"</tr>");
sb.Append(#"</table>");
return new MvcHtmlString(sb.ToString());
}
}
UPDATE 2
If you wanted to manually insert a different type (perhaps because you are going to get a small amount of table data from ViewData rather than the model of the page) then here is some more code:
View:
#(Html.DSGridFor3<DanSoftware.MVC.Areas.Errors.Code.ELMAH_Error>().AddColumn(x => x.Message).ToMvcString();)
Alternative signature for the DSGridHelper ...helper
public static DSGridHelper3<T> DSGridFor3<T>(this HtmlHelper htmlHelper)
{
return new DSGridHelper3<T>(htmlHelper);
}
Additional constructor:
public DSGridHelper3(HtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
// _dataList = htmlHelper.ViewData.Model;
DSColumnList = new List<DSGridColumn>();
}
Hope this helps someone and thanks Justin!
I dont have Visual Studio with me but I'll take a stab at this...
I would take in a collection as a datatype either in your DsGridFor method or in the AddColumn method. This will allow you to send Strongly-typed arguments from a collection. Say you wanted a generic method of AddColumn for a given collection with access to the class properties vs the collection methods, it would look something like this (just an example):
public static DSGridHelper<T> AddColumn<T>(this HtmlHelper<IEnumerable<T>> htmlHelper, Expression<Func<T, object>> property) where T : class
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
For your situation, to new-up a DsGridHelper class I might explicitly set a model-type first and then add overloads as I go:
public static DSGridHelper<T> DSGridFor<T>(this HtmlHelper<IEnumerable<T>> htmlHelper) where T : class
{
return new DSGridHelper<T>(htmlHelper);
}
And then my DsGridHelper might look something like this:
public class DsGridHelper<T>
{
private HtmlHelper _htmlHelper;
private IEnumerable<T> _dataList;
public DsGridHelper(HtmlHelper<IEnumerable<T>> htmlHelper)
{
_htmlHelper = htmlHelper;
_dataList = htmlHelper.ViewData.Model;
}
public DsGridHelper<T> AddColumn(Expression<Func<T, object>> property)
{
string columnName = (property.Body as MemberExpression).Member.Name;
DSGridColumn DSGC = new DSGridColumn();
DSGC.ColumnName = ColumnName;
DSGC.HeaderText = HeaderText ?? ColumnName;
DSColumnList.Add(DSGC);
return this;
}
}
I am fairly new to MVC, but after playing with it (MVC 3/Razor), I am hooked.
I have a few questions:
1) What is the best, or most widely used pattern to develop MVC apps in? Repository, DDD, UOW?
2) I am using the Entity Framework 4, so could some please explain to me or point me to a good source that will explain the Repository Pattern w/EF4? Doesn't EF4 take place as the business layer and the data access layer? Does the Repository Pattern even provide a benefit?
3) Also, one last question, could someone explain the whole relationship between the Controller, the Model and the View? I get the basics, but maybe a little more in depth of the correct way to use it. View Models - Say I have a view that displays customer info, and one that edits it, should I have a view model and an edit model, or can the be passed around?
4) Examples??
Thanks for the help up front,
$("Sam")
** EDIT **
Am I on the right track here:
Public Class HomeController
Inherits System.Web.Mvc.Controller
Function Index(ByVal id As Integer) As ActionResult
Return View(New HomeModel)
End Function
<HttpPost()> _
Function Index(ByVal Model As HomeModel) As ActionResult
Return View(Model)
End Function
End Class
Public Class HomeModel
Private _Repository As IRepository(Of Customer)
Public Property Customer As Customer
Public Sub New()
End Sub
Public Sub New(ByVal ID As Integer)
_Repository = New CustomerRepository
Customer = _Repository.GetByID(ID)
End Sub
End Class
Public Interface IRepository(Of T)
Function GetByID(ByVal ID As Integer) As T
Sub Add(ByVal Entity As T)
Sub Delete(ByVal Entity As T)
End Interface
Public Class CustomerRepository
Implements IRepository(Of Customer)
Public Sub Add(ByVal Entity As Customer) Implements IRepository(Of Customer).Add
End Sub
Public Sub Delete(ByVal Entity As Customer) Implements IRepository(Of Customer).Delete
End Sub
Public Function GetByID(ByVal ID As Integer) As Customer Implements IRepository(Of Customer).GetByID
Return New Customer With {.ID = ID, .FirstName = "Sam", .LastName = "Striano"}
End Function
End Class
Public Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
End Class
I use generic repositories that get instantiated in a service class (using Dependency Injection with Ninject).
The service class essentially performs two functions:
It provides all the methods that the controller will consume.
It has a property called ViewModel, that essentially maps the data that the views need into a MyViewModel class.
The Controller consumes the service class. With this "pattern", your controllers look like:
namespace ES.eLearningFE.Areas.Courses.Controllers
{
public partial class CourseController : Controller
{
ICourseDisplayService service;
public CourseController(ICourseDisplayService service)
{
this.service = service;
}
public virtual ActionResult Display(int CourseId, int StepOrder, string PupilName, string TutorName)
{
service.CourseId = CourseId;
service.StepOrder = StepOrder;
service.PupilName = PupilName;
service.TutorName = TutorName;
if (Request.IsAjaxRequest())
{
return PartialView(service.ViewModel);
}
else
{
return View(service.ViewModel);
}
}
}
}
The ViewModel class only hold display data and no methods (except the odd really simple method to retrieve data from another property that is, for example a List<> object).
Works really well. An example of a service class:
namespace ES.eLearning.Domain.Services.Courses
{
public class SqlCourseDisplayService : ICourseDisplayService
{
DataContext db;
public SqlCourseDisplayService(DbDataContextFactory contextFactory)
{
db = contextFactory.Make();
CoursesRepository = new SqlRepository<Course>(db);
StepsRepository = new SqlRepository<CourseStep>(db);
StepLinksRepository = new SqlRepository<StepLink>(db);
UserCoursesRepository = new SqlRepository<UserCourse>(db);
CourseTutorsRepository = new SqlRepository<CourseTutor>(db);
UsersRepository = new SqlRepository<User>(db);
}
#region ICourseDisplayService Members
public ViewModels.CourseDisplayVM ViewModel
{
get
{
return new ViewModels.CourseDisplayVM
{
CourseId = this.CourseId,
CourseName = this.Course.Name,
Steps = this.Steps,
ActiveStepIndex = this.ActiveStepIndex,
CurrentStepIndex = this.CurrentStepIndex,
Pupil = new UserDto { UserId = this.PupilId, UserName = this.PupilName },
Tutors = this.GetTutors(this.CourseId),
Tutor = tutorName == null ? null : new UserDto { UserName = this.TutorName, UserId = this.TutorId}
};
}
}
#region Entities
int courseId;
public int CourseId
{
get
{
if (courseId == 0) throw new ApplicationException("Invalid Course Id!");
return courseId;
}
set
{
if (value == 0) throw new ApplicationException("Invalid Course Id!");
try
{
Course = (from c in CoursesRepository.Query where c.CourseId == value select c).First();
Steps = Course.CourseSteps.ToList();
courseId = value;
}
catch {throw new ApplicationException("No Course found for Course Id: " + value);}
}
}
public Data.Course Course { get; private set; }
public int StepOrder { get; set; }
public List<Data.CourseStep> Steps { get; private set; }
public int ActiveStepIndex
{
get
{
if (PupilName == null)
{
throw new ApplicationException("Pupil not set!");
}
if (CourseId == 0)
{
throw new ApplicationException("Course not set!");
}
try
{
var x = (from uc in UserCoursesRepository.Query where (uc.IdCourse == CourseId) && (uc.UserName == PupilName) select uc).First();
return x.ActiveStepIndex;
}
catch { throw new ApplicationException("Could not get Active Step!"); }
}
}
#endregion
#region Users
string tutorName;
public string TutorName
{
get
{
if (tutorName == null) throw new ApplicationException("Invalid call to get Tutor Name [Null Tutor Name]!");
return tutorName;
}
set
{
tutorName = value;
TutorId = (Guid)Membership.GetUser(tutorName).ProviderUserKey;
}
}
public Guid TutorId { get; set; }
string pupilName;
public string PupilName
{
get { return pupilName; }
set
{
pupilName = value;
PupilId = (Guid)Membership.GetUser(pupilName).ProviderUserKey;
}
}
public Guid PupilId { get; set; }
#endregion
#region Utility Properties
public int CurrentStepIndex { get; set; }
public int StepCount
{
get
{
return Steps == null ? 0 : Steps.Count();
}
}
#endregion
#region Private Utilities
private List<UserDto> GetTutors(int CourseId)
{
return (from ct in CourseTutorsRepository.Query join u in UsersRepository.Query
on ct.TutorName equals u.UserName
where (ct.CourseId == courseId)
select new UserDto { UserName = ct.TutorName, UserId = u.UserId }).ToList();
}
#endregion
#region Repositories
private IRepository<Course> CoursesRepository
{
get;
set;
}
private IRepository<CourseStep> StepsRepository
{
get;
set;
}
private IRepository<StepLink> StepLinksRepository
{
get;
set;
}
private IRepository<UserCourse> UserCoursesRepository
{
get;
set;
}
private IRepository<CourseTutor> CourseTutorsRepository
{
get;
set;
}
private IRepository<User> UsersRepository
{
get;
set;
}
#endregion
#endregion
}
}
May not be everyone's choice, but hey, it works for me... AND (more importantly) my clients and their users.
Edit
As requested in the comment below, the Repository that I use:
namespace ES.eLearning.Domain
{
public class SqlRepository<T> : IRepository<T> where T : class
{
DataContext db;
public SqlRepository(DataContext db)
{
this.db = db;
}
#region IRepository<T> Members
public IQueryable<T> Query
{
get { return db.GetTable<T>(); }
}
public List<T> FetchAll()
{
return Query.ToList();
}
public void Add(T entity)
{
db.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
db.GetTable<T>().DeleteOnSubmit(entity);
}
public void Attach(T entity)
{
db.GetTable<T>().Attach(entity);
}
public void Save()
{
db.SubmitChanges();
}
#endregion
}
}
And the IRepository Interface:
namespace Wingspan.Web.Mvc
{
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> FetchAll();
IQueryable<TEntity> Query {get;}
void Add(TEntity entity);
void Delete(TEntity entity);
void Attach(TEntity entity);
void Save();
}
}
This should help you getting started. There are a lot of tutorials and videos available; for example:
Understanding Models, Views and Controllers
The ASP.NET MVC 2.0 basics and excellent introduction by Scott Hanselman. Personally one of my favorite speakers.
And also at www.asp.net; there are a few tutorials/examples to help you getting started. For example the Music Store sample
Unfortunately, I'm not so familiar with EF4/Repository pattern. But here's a blogpost about this pattern.
1) I would say that the repository pattern is the most widely used, then there is inversion of controll too.
2) I can't really point out the benefits with using a repository for entity framework other than that the controller should not know about how to acces data other then asking a repository. This makes it easy to switch it out sometime.
You can also eager load the data to make sure that the view don't call the database in every iteration of a foreach, for example a collection of users to display data from a child entity. You can probly do this anyway, but I feel that the repository is the right place to do it.
3) I can't tell you about the concept in a more in depth way, but I can tell some about viewmodels. In my opinion you should only use viewmodels if there is anything more then one entity you want to send to the view, for example a list of countries. You can alo use a viewmodel to "flatten" out very complex objects.
I would defiantly say the repository pattern is used a lot. This pattern can be used with Dependency Injection. Using Dependency Injection makes Unit Testing a breeze because you can snap different repositories to an abstract repoistory. Check out http://ninject.org/ for a simple to use Dependecy injector for .NET.
View Models should hold display data and transfer that data from the controller to the view. If you want to edit and display customer info, take a look at this