I'm starting to use AutoMapper and some doubts arose.
Where is the correct way to map a dto into a domain model?
I'm doing this:
DTO:
public class PersonInsert
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonInsert personInsert)
{
if (ModelState.IsValid)
{
new PersonService().Insert(personInsert);
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(PersonInsert personInsert)
{
var person = Mapper.Map<PersonInsert, Person>(personInsert);
return new PersonRepository().Insert(person);
}
}
Repository:
public class PersonRepository
{
internal int Insert(Person person)
{
_db.Person.Add(person);
_db.SaveChanges();
return person.Id;
}
}
So, is this correct? should my service knows about domain? or should I make the bind in repository only? is correct to use [Required] in DTO?
I would almost never create an entity from a DTO - I explain why below. I would use a request object to allow a factory method to build the entity:
Request:
public class InsertPersonRequest
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(InsertPersonViewModel viewModel)
{
if (ModelState.IsValid)
{
InsertPersonRequest request = InsertPersonViewModelMapper.CreateRequestFrom(viewModel);
new PersonService().Insert(request );
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(InsertPersonRequest request)
{
var person = Person.Create(request.name, request.LastName);
return new PersonRepository().Insert(person);
}
}
Repository stays the same.
This way all logic for creating the Person are located in the Factory method of the person, and so business logic is encapsulated in the domain - derived fields, default fields etc.
The problem with what you are doing is that the DTO has to be created in the UI, then all fields are mapped to the entity - this is a sure fire way for business logic to seep into the service layer, UI, or anywhere it is not supposed to be.
PLease read that again - This is a very serious mistake I see made time and time again.
I would however, use AutoMapper in the service layer to return a DTO:
Service:
public class PersonService
{
public PersonDto GetById(intid)
{
var person = new PersonRepository().GetById(id);
var personDto = Mapper.Map<Person, PersonDto>(person);
return personDto
}
}
Is this correct?
I personally don't see anything wrong with having your service do the mapping
Is it correct to use [Required] in DTO
No, DTOs should have no business logic whatsoever. They should be used purely for transmitting data across different tiers/layers of your application.
DataAnnotations are typically used on ViewModels for client/server side validation, therefore, I would add another separation into your model and introduce a ViewModel for your Insert action e.g.
public class PersonViewModel
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonViewModel personViewModel)
{
if (ModelState.IsValid)
{
var personDto = Mapper.Map<PersonViewModel, PersonDto>(personViewModel);
new PersonService().Insert(personDto);
...
}
...
}
}
Service:
public class PersonService
{
public int Insert(PersonDto personDto)
{
var person = Mapper.Map<PersonDto, Person>(personDto);
return new PersonRepository().Insert(person);
}
}
It may seem overkill in this scenario (considering the only difference is the [Required] attribute). However, in a typical MVC application you would want to ensure a clean separation between your ViewModels and your business models.
I would say that your PersonService could be seen as part of the domain layer (or Application layer directly above the domain) of your architecture and the controller and DTO is in a layer above that. That means you shouldn't have a reference to the DTO in your PersonService signatures and instead use the domain Person class here. So the Mapping code should go into the Controller. This ensures that your domain logic is not affected by changes to the webservice contract which could really be just one way to use your PersonService.
I would also introduce an interface for your repository which is injected into your PersonService because the PersonService again shouldn't need to know about concrete data access implementations.
As for the [Required] attribute, I don't see a problem with having this on the DTO because it just states the data contract of your webservice method. Anybody calling your webservice should adhere to this data contract. Of course this requirement will typically also be reflected somewhere in your domain code, maybe by throwing an exception etc.
In ASP.NET MVC the typical use of DTO is being part of something called viewmodel. Viewmodel is a class that will combine one to several DTOs into one class tailored for view presentation and posting values back to server.
What you doing is correct, no issues with that, but data annotations should reside on view models, rather than DTOs. Unless you call your DTO a view model, then its fine.
Please read the following posting about model (Domain Model) vs ViewModel in ASP.NET MVC world:
ASP.NET MVC Model vs ViewModel
Confused with Model vs ViewModel
Hope this helps
I think it is fine to have annotations on the DTOs, such as [Required], MaxLength, Range etc.
Your DTO can come in from any (possibly untrusted) source (Not just your website, but from another endpoint, WCF service, etc). All requests will be funneled to your Service/Business Layers, so you will need to validate the input before performing your business logic (simple guard checks). Having the annotations on the DTO simply describe the needed input to perform the task at hand. Passing an object with annotations is not peforming validation.
However, I believe you should be validating the DTO information is correct in the service/business layer (and annotations are a nice way to check this).
Just my thoughts on the situation :)
Related
I am a bit lost right now... I've never seen this much divergent information regarding solution to the problem. But let us start from the beginning.
I am using ASP.NET MVC with Repositories injected to Controllers, thanks to the Ninject. I have 2 simple Entities: Admin with a list of created blog entries and Entries with one virtual Admin field.
Admin:
public class Admin
{
[Key, ScaffoldColumn(false)]
public int Id { get; set; }
[Required(ErrorMessage = "Zły login.")]
[StringLength(20), MinLength(3)]
[RegularExpression(#"^[a-zA-Z0-9]*$", ErrorMessage = "Special characters are not allowed.")]
public string Login { get; set; }
[Required(ErrorMessage = "Złe hasło.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
[Display(Name = "Hasło")]
public string Password { get; set; }
public virtual List<Entry> CreatedEntries { get; set; } // napisane aktualności przez danego admina
}
Entry:
public class Entry
{
[Key, ScaffoldColumn(false)]
public int Id { get; set; }
[StringLength(200, MinimumLength = 2)]
[DataType(DataType.Text)]
[Display(Name = "Tytuł")]
public string Title { get; set; }
[Required, StringLength(2000), MinLength(3)]
[Display(Name = "Treść")]
[UIHint("tinymce_jquery_full"), AllowHtml]
public string Text { get; set; }
public virtual Admin Admin { get; set; }
}
You probably know where it is going, since this problem is... "classic" on stackoverflow.
In the Controller I want to bind one object to another:
entry.Admin = repAdmins.GetAdmin(User.Identity.Name);
repEntries.AddEntry(entry);
In the repository:
public void AddEntry(Entry entry)
{
db.Entries.Add(entry);
db.SaveChanges();
}
Of course I can't do that, because of famous "An entity object cannot be referenced by multiple instances of IEntityChangeTracker", which is a result of having separate database contexts in each repository.
When I was searching for a solution I already knew that probably the best way to solve it is to use one common context. And then I discovered Unit Of Work pattern. But here's when the real problems starts.
On many sites the solution to this is a bit different.
The repositories must have common generic interface (which I don't want to use, because I don't need to have each CRUD operation on each Entity, plus sometimes I need to have extra methods like "IfExists", etc.)
On few sites I've read that this whole abstraction is not needed, since abstraction is already provided with Entity Framework and UoW is implemented in DbContext (whatever that means)
The Unit Of Work pattern (at least from examples on the internet) seems to be a real pain for me...
I need some guidance... I learn ASP.NET MVC for only a year. For me it seems like it's a "triumph of form over content". Because... What I simply need is to bind one object to another. I'm starting to think that it was better when I simply had a context object in the Controller and I didn't need to build Eiffel Tower to achieve what's mentioned above :\ However I like idea of repositories...
I'll open by simply answering the question straight-out. Simply, your repository should take the context as a dependency (it should have a constructor that accepts a param of type DbContext). Your context should be managed by Ninject, and then injected into your repository and/or your controller. That way, everything always uses the same context. You should do all this in "request" scope, so that the context is specific to the current request.
That said, I'd like to hit some of your other points. First, a repository is just a method of access. It really shouldn't be dependent on the entity. It's okay to have methods that you don't intend to use on a particular entity: just don't use them. However, if you do want to enforce this, you can always use generic constraints and interfaces. For example, let's say you don't want update available on a particular entity. You could have interfaces like:
public interface ICreateable
{
}
public interface IUpdateable : ICreateable
{
}
Then, your entity that should not be updated will implement only ICreateable while other entities (which allow update) would implement IUpdateable (which by interface inheritance, also implement ICreateable). Finally, you would add constraints on your repository methods:
public void Create<TEntity>(TEntity entity)
where TEntity : class, ICreateable
public void Update<TEntity>(TEntity entity>)
where TEntity : class, IUpdateable
Since, the entity in question only implements ICreatable, it will not be eligible to be used as a type param to Update, so there's then no way to utilize that method.
Next, the advice to not use the repository/UoW patterns with Entity Framework is indeed because Entity Framework already implements these patterns. The repository pattern exists as a way to contain all the database querying logic (constructing SQL statements and such) in one place. That is the "abstraction" we're talking about here. In other words, instead of directly constructing SQL statements in your application code, that code is abstracted away into a repository. However, this is exactly what Entity Framework does, which is why you don't need to do it again. The Unit of Work pattern exists as a method to orchestrate the work of multiple repositories, allowing things like transactions. However, again, Entity Framework does all this.
The only reason to add any further abstraction is if you want to abstract the actual provider, i.e. Entity Framework itself. For example, you could have an interface like IRepository and then create implementations like EntityFrameworkRepository, NHibernateRepository, WebApiRepository, etc. Your application would only ever depend on IRepository, and you could then sub in different implementations as needed. If you're not going to do this, or you will always be using Entity Framework, then you might as well just use your context directly. Any further abstraction is just something else to maintain with no benefit at all to your application.
Finally, yes, the Unit of Work pattern is a real pain to everyone, not just you. Which is why I forgo it entirely. I use what I call a "truly generic repository", which utilizes generic methods and interfaces to handle any entity I want to throw at it. That means it acts not only as a repository but also a unit of work as well. You only need one instance per context and it's provider-agnostic. For more information check out the article I wrote on the subject over on my website.
The following example shows how to use the same context within multiple repositories. To simplify it, I did not use interfaces and nor did I use a container to inject dependencies.
Controller class:
public class HomeController : Controller
{
Context context;
AdminRepository adminRepository;
EntryRepository entryRepository;
public HomeController()
{
context = new Context();
adminRepository = new AdminRepository(context);
entryRepository = new EntryRepository(context);
}
// GET: Home
public ActionResult Index()
{
string login = "MyLogin";
Admin admin = adminRepository.GetAdmin(login);
Entry entry = new Entry() { Admin = admin};
entryRepository.AddEntry(entry);
return View(entry);
}
}
Repositories:
public class AdminRepository
{
Context context;
public AdminRepository(Context context)
{
this.context = context;
// This seeds the database
Admin admin = new Admin() { Login = "MyLogin" };
this.context.Admins.Add(admin);
this.context.SaveChanges();
}
public Admin GetAdmin(string login)
{
return context.Admins.Where(a => a.Login == login).FirstOrDefault();
}
}
public class EntryRepository
{
Context context;
public EntryRepository(Context context)
{
this.context = context;
}
public void AddEntry(Entry entry){
context.Entrys.Add(entry);
context.SaveChanges();
}
}
Context class:
public class Context : DbContext
{
public Context()
{
Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
Database.Initialize(true);
}
public DbSet<Admin> Admins { get; set; }
public DbSet<Entry> Entrys { get; set; }
}
Modified Models:
public class Admin
{
public int Id { get; set; }
public string Login { get; set; }
}
public class Entry
{
public int Id { get; set; }
public virtual Admin Admin { get; set; }
}
I am using AutoMapper to automatically map properties between entities that exist within different levels of my app.
All has been well, until I tried to introduce a new calculated property value in a business entity, with properties being auto-mapped from a data entity: The new calculated property value in the destination business entity is returning null every time.
I am using MEF for dependency injection, and am therefore specifying Interfaces for the source and destination types when using AutoMapper.
I think what is happening is that AutoMapper is creating a simple implementation of the destination class with the specified Interface (which would therefore not have the implementation code for the calculated property, thereby return null, which is what I am seeing), instead of the appropriate destination class being created by MEF according to the MEF Export definitions for the specified destination interface.
I am not at all sure how to proceed.
Defining the Data Entity
My data entity interface:
public interface IMyDataEntity
{
string MyProperty1 { get; set; }
}
The implementation of the data entity interface, also defining the association between interface/implementation to be used by MEF:
[Export(typeof(IMyDataEntity))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MyDataEntity: IMyDataEntity
{
public string MyProperty1 { get; set; }
}
Defining the Business Entity
My business entity interface:
public interface IMyBusinessEntity
{
string MyProperty1 { get; set; }
string MyCalculatedProperty1 { get; }
}
The implementation of the business entity interface, also defining the association between interface/implementation to be used by MEF:
[Export(typeof(IMyBusinessEntity))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MyBusinessEntity: IMyBusinessEntity
{
public string MyProperty1 { get; set; }
public string MyCalculatedProperty1 {
get{ return "Test"; }
}
}
Mapping with AutoMapper
Trying to map from the Data Entity to the Business Entity:
IMyBusinessEntity myBusinessEntity
= Mapper.Map<IMyDataEntity, IMyBusinessEntity>(myDataEntity,
opts => opts.CreateMissingTypeMaps = true);
The Issue
myBusinessEntity.MyCalculatedProperty1 returns null.
I've been trying to figure this out for days but there seems to be very little info on this particular subject with ASP.NET MVC. I've been Googling around for days and haven't really been able to figure anything out about this particular issue.
I've got a 3 layer project. Business, DAL and UI/Web layer. In the DAL is dbcontext, repository and unit of work. In the business layer is a domain layer with all the interfaces and the EF models. In the business layer there is also a service layer with DTOs for the EF models and a generic repository service that accesses the repository. This picture should help explain it.
My problem is that i just can't seem to figure out how to use the DTOs to transfer data from the business layer.
I've created service classes for the DTOs. I've got a ImageDTO and model and same for image anchors. I've created a service class for each DTO. So i've got a image service and anchor service. These services inherit the repository service and at the moment implement their own services. But thats about as far as i have gotten. Since these services have constructors that receive a IUnitOfWork interface via IoC i've pretty much gotten stranded.
If i reference the service directly from the UI everything works as it should but i just can't get my mind around how to use DTOs to transmit data both from the service layer to the UI layer and the other way around.
My service layer:
Business/Services/DTOs
public class AnchorDto
{
public int Id { get; set; }
public int x1 { get; set; }
public int y1 { get; set; }
public int x2 { get; set; }
public int y2 { get; set; }
public string description { get; set; }
public int imageId { get; set; }
public int targetImageId { get; set; }
public AnchorDto(int Id, int x1, int y1, int x2, int y2, string description, int imageId, int targetImageId)
{
// Just mapping input to the DTO
}
}
public class ImageDto
{
public int Id { get; set; }
public string name { get; set; }
public string title { get; set; }
public string description { get; set; }
public virtual IList<AnchorDto> anchors { get; set; }
public ImageDto(int Id, string name, string title, string description, IList<AnchorDto> anchors )
{
// Just mapping input to DTO
}
}
Business/Services/Services
public class RepoService<TEntity> : IRepoService<TEntity> where TEntity : class
{
private IRepository<TEntity> repo;
public RepoService(IUnitOfWork repo)
{
this.repo = repo.GetRepository<TEntity>();
}
public IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return repo.Get(filter, orderBy, includeProperties);
}
public TEntity GetByID(object id)
{
return repo.GetByID(id);
}
public void Insert(TEntity entity)
{
repo.Insert(entity);
}
public void Delete(object id)
{
repo.Delete(id);
}
public void Delete(TEntity entityToDelete)
{
repo.Delete(entityToDelete);
}
public void Update(TEntity entityToUpdate)
{
repo.Update(entityToUpdate);
}
}
The Image Service, the IImageService interface is currently empty until i figure out what i need to implement.
public class ImageService : RepoService<ImageModel>, IImageService
{
public ImageService(IUnitOfWork repo)
: base(repo)
{
}
}
At the moment my controllers aren't really working and aren't using the service layer so i decided not to include any of those. I plan to map the DTOs to ViewModels using auto mapper once i've sorted this issue out.
So now, please anyone knowledgeable enough to give me that idea I'm missing so that i can figure this out?
Your service should receive DTOs, map them to business entities and send them to the repository. It should also retrieve business entities from the repository, map them to DTOs and return the DTOs as reponses. So your business entities never get out from the business layer, only the DTOs do.
Then your UI\Weblayer should be unaware of the business entities. The web layer should only know about the DTOs. To enforce this rule is very important that your UI layer does not uses the service implementation classes (which should be private), just the interfaces. And the service interfaces shouldn´t depend on the business entities, just the DTOs.
So you need service interfaces based on DTOs, and your base service class needs another generic argument for the DTO. I like to have a base class for entities and DTOs so they can be declared as:
//Your UI\presentation layer will work with the interfaces (The inheriting ones)
//so it is very important that there is no dependency
//on the business entities in the interface, just on the DTOs!
protected interface IRepoService<TDto>
where TDto: DTOBase
{
//I'm just adding a couple of methods but you get the idea
TDto GetByID(object id);
void Update(TDto entityToUpdateDto)
}
//This is the interface that will be used by your UI layer
public IImageService: IRepoService<ImageDTO>
{
}
//This class and the ones inheriting should never be used by your
//presentation\UI layer because they depend on the business entities!
//(And it is a best practice to depend on interfaces, anyway)
protected abstract class RepoService<TEntity, TDto> : IRepoService<TDto>
where TEntity : EntityBase
where TDto: DTOBase
{
...
}
//This class should never be used by your service layer.
//Your UI layer should always use IImageService
//You could have a different namespace like Service.Implementation and make sure
//it is not included by your UI layer
public class ImageService : RepoService<ImageModel, ImageDto>, IImageService
{
...
}
You then need a way of adding the mapping between entities and DTO to that base service without actually implementing the mapping (as it depends on each concrete entity and DTO classes). You could declare abstract methods that perform the mapping and will need to be implemented on each specific service (like ImageService). The implemantion of the base RepoService would look like:
public TDto GetByID(object id)
{
//I'm writing it this way so its clear what the method is doing
var entity = repo.GetByID(id);
var dto = this.EntityToDto(entity);
return dto;
}
public void Update(TDto entityToUpdateDto)
{
var entity = this.DtoToEntity(entityToUpdateDto)
repo.Update(entity);
}
//These methods will need to be implemented by every service like ImageService
protected abstract TEntity DtoToEntity(TDto dto);
protected abstract TDto EntityToDto(TEntity entity);
Or you could declare mapping services, adding a dependency with an appropiated mapping service that should be provided by your IOC (This makes more sense if you need the same mapping on different services). The implementation of RepoService would look like:
private IRepository<TEntity> _repo;
private IDtoMappingService<TEntity, TDto> _mappingService;
public RepoService(IUnitOfWork repo, IDtoMappingService<TEntity, TDto> mapping)
{
_repo = repo.GetRepository<TEntity>();
_mappingService = mapping;
}
public TDto GetByID(object id)
{
//I'm writing it this way so its clear what the method is doing
var entity = repo.GetByID(id);
var dto = _mappingService.EntityToDto(entity);
return dto;
}
public void Update(TDto entityToUpdateDto)
{
var entity = _mappingService.DtoToEntity(entityToUpdateDto)
repo.Update(entity);
}
//You will need to create implementations of this interface for each
//TEntity-TDto combination
//Then include them in your dependency injection configuration
public interface IDtoMappingService<TEntity, TDto>
where TEntity : EntityBase
where TDto: DTOBase
{
public TEntity DtoToEntity(TDto dto);
public TDto EntityToDto(TEntity entity);
}
In both cases (abstract methods or mapping services), you can implement the mapping between the entities and DTOs manually or using a tool like Automapper. But you should be very careful when using the AutoMapper and entity framework, although that is another topic! (Google a bit about that and collect some information on the topic. As a first advice pay attention to the queries being executed against the database when loading data so you don´t load more than needed or send many queries. When saving data pay attention to your collections and relationships)
Long post maybe, but I hope it helps!
I have an InvoiceInputModel with a ProjectId property which is a reference to a Project entity. Ideally, I want AutoMapper to be able to map an entire Invoice entity from an InvoiceInputModel, which looks like this:
public class InvoiceInputModel
{
public Guid Id { get; set; }
public DateTime Date { get; set; }
public string Reference { get; set; }
public Guid ProjectId { get; set; }
}
Obviously the following is bad:
Mapper.CreateMap<InvoiceInputModel, Invoice>()
.ForMember(src => src.Project, opt => opt.MapFrom(
dest => _unitOfWork.CurrentSession.Get<Project>(dest.ProjectId)
)
);
How do I tell AutoMapper that invoice.Project should be mapped to a Project entity based off of the ProjectId property in InvoiceInputModel while preserving loose coupling?
Invoice/Edit in my InvoiceController:
[HttpPost]
[Authorize]
public ActionResult Edit(InvoiceInputModel invoiceInputModel)
{
var invoice = _unitOfWork.CurrentSession.Get<Invoice>(invoiceInputModel.Id);
Mapper.Map<InvoiceInputModel, Invoice>(invoiceInputModel, invoice);
invoice.Project = _unitOfWork.CurrentSession.Get<Project>(invoiceInputModel.ProjectId);
// I want AutoMapper to do the above.
_unitOfWork.CurrentSession.SaveOrUpdate(invoice);
_unitOfWork.Commit();
return View(invoice);
}
I spotted something about "Resolvers" and ResolveUsing, but I have no experience using it.
How do I tell AutoMapper to do this while preserving loose coupling between my entity models, input models and view models? Or is there a better way?
How do I tell AutoMapper that invoice.Project should be mapped to a Project entity based off of the ProjectId property in InvoiceInputModel while preserving loose coupling?
You can't. If AutoMapper is going somewhere else to fetch data, then it's not loose coupled.
You're not modifying the Project in this particular View anyway - why do you need to set the relationship to Project, isn't nHibernate smart enough to see that property hasn't changed, and not do anything?
I personally have entities in viewmodels instead of IDs, so that binding happens automatically.
http://sprokhorenko.blogspot.com/2011/03/bind-to-entity-id.html
I want a way to separate the loading of reference data into a view model from the controller. At the moment I have a view model with a property for the selected value and the reference data:
public IEnumerable<SelectListItem> DayTypes { get; set; }
public int DayTypeId { get; set; }
and the data is populated from the relevant repository in the controller action:
model.DayTypes = _dayTypeRepository.GetAll().ToSelectList(d => d.Description, d => d.Identifier.ToString());
I would like to change this because it pollutes the controller with lots of repositories and code that is not core to its concerns. All of these dependencies make unit testing the controller a pain.
One possible approach to solving this would be to make the view model class do the loading which would require a custom model binder to instantiate them using the IoC container to provide the repository dependency. Is this a good option?
Another approach that I think would be good is hinted at in CodeCampServer but is incomplete and commented out involving attributes on the field in the view model:
[SelectListProvided(typeof(AllDaysSelectListProvider))]
public IEnumerable<SelectListItem> DayTypes { get; set; }
however I am struggling to figure out how this could be implemented in a way that would not require some major replumbing of the MVC framework.
How do you solve this problem?
EDIT: I want to keep with strongly typed views and avoid stuffing the data into view data.
FURTHER EDIT: I would also like a solution that is ideally model independent, by which I mean if the same reference data is needed by multiple view models this can be achieved with a single piece of code. Matt's approach is interesting but is tightly coupled to the view model.
I would use a service layer which would return me a POCO object that I would map to a view model. So my controller action would look like this:
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
var viewModel = Mapper.Map<Model, ViewModel>(model);
return View();
}
I also like using action filters to avoid the mapping code all over again so:
[AutoMap(typeof(Model), typeof(ViewModel))]
public ActionResult Index(int id)
{
var model = _service.GetModel(id);
return View(model);
}
This way only the service talks with the CRUD repositories and the controller talks to the service and the mapping layer.
You could write a new ActionFilter that you can decorate an action method with; this action filter will load the reference data into the viewdata, which you can access from your view.
There is more on action filters here.
EDIT: Based on the users comments, this now includes a strongly typed option.
Firstly, you need to create the SharedViewModel to contain the shared data.
public class SharedViewModel
{
public List<string> Days { get; set; }
public List<string> Months { get; set; }
public List<string> Years { get; set; }
}
Next, we create the view model to be used by the Index view, which uses this shared view model.
public class HomeViewModel
{
public string ViewName { get; set; }
public SharedViewModel SharedViewModel { get; set; }
}
The next step is important, it implements an action filter called SharedData(), which will apply the shared data.
public class SharedDataActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var currentModel = ((HomeViewModel) filterContext.Controller.ViewData.Model);
currentModel.SharedViewModel = new SharedViewModel
{
Days = new List<string> {"Mon"},
Months = new List<string> {"Jan"},
Years = new List<string> {"2011"}
};
base.OnActionExecuted(filterContext);
}
}
At the moment, it just applies the whole shared data, but you can added parameters into the method to be selective.
When the action has been executed, this method takes the current model and adds the shared data.
Here is the controller action.
[SharedDataActionFilter]
public ActionResult Index()
{
return View("Index", new HomeViewModel { ViewName = "HomePage" });
}
You can access the data like any other strongly typed view, and the shared data wont affect the data already in the model (in this case "ViewName"). You can also use action filters across controllers, and globally across the site with mvc 3.
Hope this helps, Matt.