Is it okay to have a constructor or other non database accessing methods on POCO classes. For example when passing a view model to a controller.
Controller:
public ActionResult SomeMethod(SomeViewModel model)
{
var entity = new SomePocoClasse(model);
// ... then save entity to database
return SomeActionResult
}
Entity:
public SomeClass() {}
public SomeClass(SomeViewModel model)
{
// create itself based on values in model
}
public void Update(SomeViewModel model)
{
// Update itself base on values in model
}
The first entity constructor is for entity framework or regular creation
var entity = new entity
{
// set up properties
};
The second is for creation from SomeViewModel
var entity = new entity(SomeViewModel);
The method is for updating itself from SomeViewModel
var entity = SomeIRepository.Get(id);
entity.Update(SomeViewModel);
Or is the above bad practice which should go some where else.
Yes and no. In general, it's not necessarily bad practice to have a constructor on a POCO. There's any number of reasons why you might want or need that. However, you need to ensure that you maintain a parameterless constructor as well, or you'll cause issues with things like EF which won't know how to properly initialize your POCO class otherwise.
That said, what you're doing here is not good practice. You haven't provided a ton of code, but it appears that what you're doing is passing in the view model to the POCO constructor to set the properties on the POCO with those values. Rather, what you should be doing is pulling the entity fresh from the database and then mapping over any relevant properties on your view model to that entity instance. I supposed what you're doing could be fine solely when creating a new entity, but that means having two separate ways of populating your POCO class with values depending on whether you're creating or editing. That increases complexity and complexity means higher maintenance costs.
Instead, you should either use a library like AutoMapper or create a utility class to handle the mapping:
public static class SomePocoMapper
{
public static SomePoco Map(SomeViewModel model)
{
return Map(model, null);
}
public static SomePoco Map(SomeViewModel model, SomePoco entity)
{
entity = entity ?? new SomePoco();
// map over property values;
return entity;
}
}
Then in your create action:
var entity = SomePocoMapper.Map(model);
And in your edit action:
var entity = // get entity from database
SomePocoMapper.Map(model, entity);
Related
This question already has answers here:
Where to convert business model to view model?
(3 answers)
Closed 5 years ago.
I'm using MVC 4 with the repository pattern and unit testing also. I have a typical controller that has simple CRUD functionality. I've separated my View Models from my DTOs and I would like to know the best way to convert between the 2:
Models:
I have Admin.Models.Product which is my view model and AdminAssembly.Models.Product which is my DTO.
Controller:
//repo that handles product operations
AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> db;
//default constructor
public ProductController() { db = new AdminAssembly.Repositories.EntityRepo<AdminAssembly.Models.Product>(new AdminAssembly.Models.EntitiesContext()); }
//unit testing constructor
public ProductController(AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> context) { db = context; }
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Admin.Models.Product product) {
if (ModelState.IsValid) {
//COMPILE-ERROR: how to convert to DTO?
db.Add(product);
}
return View();
}
//
// GET: /Product/Edit/5
public ActionResult Edit(int id) {
//COMPILE-ERROR: how to convert to view model?
Admin.Models.Product product = db.GetAll().Where(p => p.ID == id);
return View(product);
}
How do I convert between the 2?
Do I reference my DTO assembly in my view model and do something like: (won't this break my unit testing?)
//convert to AdminAssembly.Models.Product
db.Add(product.ToDTO());
//convert back to Admin.Models.Product via constructor
Admin.Models.Product product = Admin.Models.new Product(db.GetAll().Where(p => p.ID == id));
Do I need some sort of object conversion black box?
Converter.ToViewProduct(product);
Some sort of interface?
or something else?
Update 1:
public static class Product {
public static Admin.Models.Product ToView(AdminAssembly.Models.Product dto) {
Admin.Models.Product viewProduct = new Admin.Models.Product();
//straight copy
viewProduct.Property1 = dto.Property1;
viewProduct.Property2 = dto.Property2;
return viewProduct;
}
public static AdminAssembly.Models.Product ToDTO(Admin.Models.Product viewModel) {
AdminAssembly.Models.Product dtoProduct = new AdminAssembly.Models.Product();
//straight copy
dtoProduct.Property1 = viewModel.Property1;
dtoProduct.Property2 = viewModel.Property2;
//perhaps a bit of wizza-majig
dtoProduct.Property1 = viewModel.Property1 + viewModel.Property2;
return dtoProduct;
}
}
The long-hand response
[HttpPost]
public ActionResult Create(Admin.Models.Product product)
{
if (ModelState.IsValid)
{
//COMPILE-ERROR: how to convert to DTO?
var dtoProduct = new AdminAssembly.Models.Product();
dtoProduct.Property1 = product.Property1;
dtoProduct.Property2 = product.Property2;
//...and so on
db.Add(dtoProduct);
}
return View();
}
While this looks verbose and tedious (and it is) it has to happen eventually, somewhere.
You can hide this mapping either in another class or extension method, or you can use a third party like AutoMapper, as Charlino points out.
As a side note, having two classes with the same name in two different namespaces will eventually get confusing (if not for you, then for the next person who has to maintain your code.) Implement friendlier and more descriptive names wherever possible. For example, put all your view models in a folder called ViewModels, not Models. And append all your view models with ViewModel, or VM. It's also a good convention, imo, to name your view models based on the view that they are for, not so much the domain model that they will be mapped to, as not all view models will map directly to a domain model. Sometimes you'll want parts of more than one domain model, for a single view, and that will blow up your naming convention.
So in this particular case I would suggest changing Admin.Models to Admin.ViewModels and then rename the view model version of Product to CreateViewModel. Your code will be much more readable and will not be littered with namespaces throughout your methods.
All of that would result in a method that would look more like this:
[HttpPost]
public ActionResult Create(CreateViewModel viewModel)
{
if (ModelState.IsValid)
{
var product = new Product();
product.Property1 = viewModel.Property1;
product.Property2 = viewModel.Property2;
//...and so on
db.Add(product);
}
return View();
}
Check out a library called AutoMapper.
From their wiki:
What is AutoMapper?
AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. This type of code is rather dreary and boring to write, so why not invent a tool to do it for us?
If you dont want to use AutoMapper you may use extensions, as suggested by #Forty-Two. If the number of things to map is no very great, I would go with this approach, just because then, AutoMapper == YAGNI
public static class Extensions
{
public static ViewModel ToViewModel(this Model )
{
var vm = new ViewModel()
{
//map
};
return vm;
}
public static Model ToModel(this ViewModel viewModel)
{
var model = new Model()
{
//map
};
return model;
}
}
Similar to your code in UPDATE, but using extensions instead.
Attempting to create an MVC project using EF and the Model first approach.
In order to implement it in a way that the Web and data portions are loosely coupled I'm attempting to implement the repository pattern, but, after reading many articles I'm still trying to grasp what objects my Repository interface should return or how they should bind/map to the 'M' model in my MVC project.
Here's a very simplistic example of what I'm asking.
//Repository Interface
public interface IMemberRepository
{
Member GetById(int id);
IEnumerable<Member> FindByName(string name);
}
//Repository Interface Implementation
public class MemberRepository : IMemberRepository
{
//My DB Context object created by EF
private MyContainer context;
public MemberRepository(MyContainer context)
{
this.context = context;
}
public Member GetById(int id)
{
return context.Members.SingleOrDefault(x => x.Id == id);
}
public IEnumerable<Member> FindByName(string name)
{
return context.Members.Find(x => x.name == name);
}
}
So using Ninject as my DI framework I could call this from my controller as follows:
public class GroupsController : Controller
{
public ViewResult Find(string name)
{
IMemberRepository repo =
ObjectFactory.Instance.CreateInstance<IMemberRepository>();
return repo.FindByName(name);
}
}
At this point I'm just not understanding how my Member object (from the EF model) is supposed to bind/map to my 'MVC' member model object. It seems I must be missing some sort of mapping layer or I'm just completely off track. If I were to reference my EF generated class directly it feels like I'm negating the point of using an interface and DI framework. Any advice or direction at this point would be appreciated.
Your IMemberRepository interface and its implementation MemberRepository look right to me. That is how I structure my database code as well.
Moving to the MVC world, I would create view models which you populate from your data model. This will give you the flexibility of adding any attributes or additional properties that you need in your view.
This would be the workflow:
Fetch object(s) from repository
Populate view model objects with all the data from your repository object(s)
Return the view model to your view from the controller
I want to create a repository class to separate out my data logic from my controllers. I am using a ViewModel to represent some data that will be filled with data from different tables.
Here are some questions I have:
For a method like GetAll(), do I return an IQueryable<MyViewModel> or IQueryable<Entity>? If I return viewmodels, how do I cope with a GetAll() that pulls thousands of records?
Do I create a constructor for my custom ViewModel class that takes the Entity as a parameter to do the mapping? (I'm still unfamiliar with automapper so just need an understanding on how to do this from a design point of view)
Again, my main concern is a method like GetAll() which would pull many records. If I did a foreach loop to translate each Entity into a ViewModel seems like a lot of overhead. My thought was to put a reference inside the custom ViewModel class to the IQueryable<Entity> to access from the collection, and have the ListViewModel just have indexers or something like that which reference the collection property.
1) For a method like GetAll(), do I return an IQueryable or IQueryable? If I return viewmodels, how do I cope with a GetAll() that pulls thousands of records?
IQueryable<Entity>. The repository doesn't deal with view models. Think of the repository as something that is defined in a separate class library that doesn't reference your ASP.NET MVC application which is where your view models live. It is the ASP.NET MVC application that references this library.
2) Do I create a constructor for my custom ViewModel class that takes the Entity as a parameter to do the mapping? (I'm still unfamiliar with automapper so just need an understanding on how to do this from a design point of view)
No. Don't create constructors in your view models especially if you want your controller actions to take those view models as action parameters (think of a POST action). The reason for this is that the default model binder will no longer know how to instantiate your view model and you will have to write custom model binders.
So AutoMapper or manually map.
Example with manual mapping which is what you could start with:
public ActionResult SomeAction()
{
IEnumerable<Entity> entities = Repository.GetAll();
IEnumerable<MyViewModel> model = entities.Select(x => new MyViewModel
{
Prop1 = x.Prop1,
Prop2 = x.Prop2,
...
});
return View(model);
}
And once you get sick of writing this code move to AutoMapper:
public ActionResult SomeAction()
{
IEnumerable<Entity> entities = Repository.GetAll();
IEnumerable<MyViewModel> model = Mapper.Map<IEnumerable<Entity>, IEnumerable<MyViewModel>>(entities);
return View(model);
}
or if you write a custom action filter that uses the OnActionExecuted event to pull the domain model that was passed to the view, map it to the view model using AutoMapper and substitute the model with the view model for the view, you could further simplify the repetitive code:
[AutoMap(typeof(IEnumerable<Entity>), typeof(IEnumerable<MyViewModel>))]
public ActionResult SomeAction()
{
IEnumerable<Entity> entities = Repository.GetAll();
return View(entities);
}
Again, my main concern is a method like GetAll() which would pull many
records. If I did a foreach loop to translate each Entity into a
ViewModel seems like a lot of overhead.
Don't be concerned about that. Pulling your records will be a magnitude slower than looping and mapping to the view model.
There are many different ways to do this, but to start simply, I would return an IEnumerable<T> for your GetAll() method. However, you'll probably want to implement paging in some fashion. You might want to setup a generic repository that does your basic data access for most scenarios and returns an Enumerable. You could reserve a single method that should be reserved for more complicated queries and returns an IQueryable<T>. The basic stripped down implementation might look like below.
public class Repository<T> : IRepository<T> where T : class
{
internal ObjectContext _objectContext;
internal ObjectSet<T> _objectSet;
public Repository(ObjectContext objectContext)
{
_objectContext = objectContext;
_objectSet = objectContext.CreateObjectSet<T>();
}
public IQueryable<T> GetQuery()
{
return _objectSet;
}
public IEnumerable<T> GetAll()
{
return GetQuery().ToList();
}
public IEnumerable<T> Find(Func<T, bool> where)
{
return _objectSet.Where<T>(where);
}
public T Single(Func<T, bool> where)
{
return _objectSet.SingleOrDefault<T>(where);
}
public List<T> Page<TKey>(Expression<Func<T, bool>> where, int page, int pagesize, Expression<Func<T, TKey>> orderBySelector, bool ascending)
{
return ascending
? GetQuery().Where(where).OrderBy(orderBySelector).Skip((page - 1) * pagesize).Take(pagesize).ToList()
: GetQuery().Where(where).OrderByDescending(orderBySelector).Skip((page - 1) * pagesize).Take(pagesize).ToList();
}
public void Delete(T entity)
{
_objectSet.DeleteObject(entity);
}
public void Add(T entity)
{
_objectSet.AddObject(entity);
}
}
And the Interface would look like
public interface IRepository<T> where T : class
{
IQueryable<T> GetQuery();
IEnumerable<T> GetAll();
IEnumerable<T> Find(Func<T, bool> where);
T Single(Func<T, bool> where);
List<T> Page<TKey>(Expression<Func<T, bool>> where, int page, int pagesize, Expression<Func<T, TKey>> orderBySelector, bool ascending);
void Delete(T entity);
void Add(T entity);
}
The above can function as the beginning of a simple generic repository. Once you have your entities, you don't need AutoMapper, it just makes life easier as many ViewModels have the same properties as your entities. You can simply define a new ViewModel or List of ViewModels and map the properties on your own.
List<ViewModel> vm = new List<ViewModel>();
foreach (var e in entities)
{
ViewModel v = new ViewModel();
v.something = e.something;
// perform the rest
vm.Add(v);
}
*That was quite a bit to type, sorry about any typos :)
I think you may have a misunderstanding of the view model and it's purpose. You don't need to create a view model for every entity in your database, as it seems you want to do; you just create a view model for each view you want to render. Hence the term "view model"--it organizes the data in the form of a model that your view can be strongly typed to.
You wouldn't, for example, want to create a separate view model for every entity returned by a GetAll(). In a simple scenario of displaying a gridview of all records you would probably just need a single viewmodel with one property:
public class MyViewModel
{
public List<MyRecord> AllRecords {get;set;}
}
You would populate this view model in the controller
public ActionResult SomeAction()
{
var viewmodel = new MyViewModel{AllRecords = GetAll()};
return View(viewModel);
}
Have a look at this blog post by Rachael Appel for a really concise discussion.
I've a simple class
[Serializable]
public class MyClass
{
public String FirstName { get; set: }
public String LastName { get; set: }
//Bellow is what I would like to do
//But, it's not working
//I get an exception
ContactDataContext db = new ContactDataContext();
public void Save()
{
Contact contact = new Contact();
contact.FirstName = FirstName;
contact.LastName = LastName;
db.Contacts.InsertOnSubmit(contact);
db.SubmitChanges();
}
}
I wanted to attach a Save method to the class so that I could call it on each object. When I introduced the above statement which contains ContactDataContext, I got the following error "In assembly ... PublicKeyToken=null' is not marked as serializable"
It's clear that the DataContext class is generated by the framework (). I checked and did not see where that class was marked serialize.
What can I do to overcome that? What's the rule when I'm not the author of a class? Just go ahead and mark the DataContext class as serializable, and pretend that everything will work?
Thanks for helping
It might be worth taking a step back and seeing if what you want to achieve is really valid.
Generally, a serializable class is used for data transport between two layers. It is more likely to be a simple class that only holds data.
It seems a little out of place for it to hold the ability to persist to a database. It is not likely that both ends of the pipe actually have access to the database, and it seems very unlikely that they would both have the ability to persist data.
I wonder if it's worth factoring the save out to a repository. So have a repository class that will accept the data transfer object, construct the database object and save it.
This will simplify your code and completely avoid the problem you're having. It will also greatly enhance testability.
The problem is that the db field gets serialized, while clearly it doesn't need to be serialized (it's instantiated once the object is created).
Therefore, you should decorate it with the NonSerialized attribute:
[NonSerialized]
ContactDataContext db = new ContactDataContext();
[Update]
To make sure the db field is accesable after object initialization, you should use a lazy loading property and use this property instead of the field:
[NonSerialized]
ContactDataContext db = null;
[NonSerialized]
private ContactDataContext {
get {
if (db == null) {
db = new ContactDataContext();
}
return db;
}
set {
db = value;
}
}
public void Save()
{
Contact contact = new Contact();
contact.FirstName = FirstName;
contact.LastName = LastName;
Db.Contacts.InsertOnSubmit(contact);
Db.SubmitChanges();
}
[Update2]
You can serialize most objects, as long as it has a public parameterless constructor (or no constructor at all) and no properties/fields that cannot be serialized but require serializing. If the class itself is not marked as [Serializable], then you can do this yourself using a partial class. If the class has properties/fields that cannot be serialized, then you might achieve this by inheriting the class and overriding these properties/fields to decorate them as [NonSerialized].
You can create a surrogate that knows how to serialize the dodgy classes - see here for an example
I think you do need to decorate the base class, however, the DataContext auto generated classes are marked as partial. Have you tried doing something like:
[Serializable]
public partial class ContactDataContext
{
}
Not sure if it would work but its worth a try.
I'm having trouble trying to think what the best way is to recreate a database object in a controller Action.
I want to make use of ModelBinders so in my action I have access to the object via a parameter, rather than having to repeat code to get an object from the database based on an identifier parameter. So I was thinking of having a ModelBinder that performs a call to the dataaccess layer to obtain the original object (or creates a new one if it doesn't exist in the database), then binds any properties to the database object to update it. However I've read that the ModelBinders shouldn't make database queries (first comment of this article).
If the ModelBinder shouldn't perform a database query (so just using the DefaultModelBinder) then what about database objects that have properties that are other db objects? These would never get assigned.
Saving an object after the user has edited it (1 or 2 properties are editable in the view) the ModelBinded object would be missing data, so saving it as it is would result in data in the database being overwritten with invalid values, or NOT-NULL constraints failing.
So, whats the best way to get an object in a controller action from the database bound with the form data posted back from the view?
Note im using NHibernate.
I get the model object from the database, then use UpdateModel (or TryUpdateModel) on the object to update values from the form parameters.
public ActionResult Update( int id )
{
DataContext dc = new DataContext();
MyModel model = dc.MyModels.Where( m => m.ID == id ).SingleOrDefault();
string[] whitelist = new string[] { "Name", "Property1", "Property2" };
if (!TryUpdateModel( model, whitelist )) {
... model error handling...
return View("Edit");
}
ViewData.Model = model;
return View("Show");
}
Unfortunately you don't have control over the construction of the model binder, so you can't inject any repository implementation.
You can reach out directly into a service locator to pull in your repository & fetch the item:
public class ProductBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
if(modelType != typeof(Product))
return null;
var form = controllerContext.HttpContext.Request.Form;
int id = Int32.Parse(form["Id"]);
if(id == 0)
return base.CreateModel(controllerContext, bindingContext, modelType);
IProductRepository repository = ServiceLocator.Resolve<IProductRepository>();
return repository.Fetch(id);
}
}
You might even make this work for all of your entities if you can use a base class or interface that provides the Id of the class.
You'll have to set this up in Global.asax:
ModelBinders.Binders.Add(typeof(Product), new ProductBinder());
and then you can do this:
public ActionResult Save([Bind] Product product)
{
....
_repository.Save(product);
}
Let me first state that I don't recommend to access database from ModelBinders, as from perspective of Separation Of Concern ModelBinders should only be responsible of interpretting client request, obviously database is not.
If you dont want to repeat your self (DRY), use repositories/services
However if u really want to do it like that, then
In global.asax.cs Register a custom MyModelBinderProvider to MVC
ModelBinderProviders.BinderProviders.Add(new EntityModelBinderProvider
{
ConnectionString = "my connection string"
));
Cunstruct the custom ModelBinderProvider to contain database settings
public class EntityBinderProvider: IModelBinderProvider
{
public string ConnectionString { get; set; }
public IModelBinder GetBinder(Type modelType)
{
if (Is known entity)
return new EntityBinder(ConnectionString);
else
return null;
}
}
Follow further instructions from Ben Scheirman
You don't actually have to hit the database. Simply setting the Id of the objects will be enough to set the relationship up, but watch your cascades. Make sure your cascde settings won't update the related object as it will clear the values.