generics - Implementing .ToViewModel() using AutoMapper -
i'm trying ease use of automapper in project, implementing extension method .toviewmodel()
. basically, wrapper around standard call, find myself annoyed how must type each time want map something. compare two:
var viewmodel = mapper.map<domainentitytype, viewmodeltype>(entity); // or... var viewmodel = entity.toviewmodel();
i feel number 2 sweet =) i've let entities extend ientity
, , viewmodels (that correspond entity) extend iviewmodel<ientity>
, , written following extension method:
public static iviewmodel<tentity> toviewmodel<tentity>(this tentity entity) tentity : ientity { return mapper.map<tentity, iviewmodel<tentity>>(entity); }
however, i'm unable make fly.
the following nunit test makes attempt @ testing (although i'm unsure if assert.areequal
tests want - require reference equality? if so, how test "are equivalent"?). test fails message
expected: <castle.proxies.iviewmodel`1proxy> was: <castle.proxies.iviewmodel`1proxy>
[test] public void domainentitytypemapsviewmodeltype() { var entity = new domainentitytype(); var oldskool = mapper.map<domainentitytype, iviewmodel<entity>>(entity); var extension = inspectionobject.toviewmodel(); assert.areequal(oldskool, extension); }
except fact test might testing wrong thing, missing fundamental how automapper works? automapper ever able correctly map interface? (i have class, entityviewmodel
, implements viewmodel<entity>
, haven't told automapper it...)
i managed in generic way. required tweaking of objects, here's did:
since before, of entities inherit entity<tid>
(usually entity<int>
, id potentially data type, example guid), in turn implements interface ientity<tid>
(with single property public tid id {get;}
). few entities don't inherit entity<tid>
, @ least implement ientity<tid>
.
i created new class, overrides equals
, gethashcode
:
public class viewmodel<tentity, tid> : ientity<tid> tentity : ientity<tid> { public tid id { get; set; } public override bool equals(object obj) { var viewmodel = obj viewmodel<tentity, tid>; return viewmodel != null && equals(viewmodel); } public bool equals(viewmodel<tentity, tid> other) { return id.equals(other.id); } public override int gethashcode() { // not returning id.gethashcode() in case want add more // properties later... var hash = 7; hash = (hash * 17) + id.gethashcode(); return hash; } }
now, viewmodels (and editmodels) inherit class:
public class entityviewmodel : viewmodel<entitytype, int> { // data properties }
i extend entities , viewmodels following extension methods:
public static tviewmodel to<tviewmodel>(this ientity entity) tviewmodel : class { return mapper.map(entity, entity.gettype(), typeof(tviewmodel)) tviewmodel; } public static tentity toentity<tentity, tid>(this viewmodel<tentity, tid> viewmodel) tentity : class, ientity<tid> { return mapper.map(viewmodel, viewmodel.gettype(), typeof(tentity)) tentity; }
a key concept make work abandon generic overloads of .map
, since didn't know exact types wanted map , @ compile time.
i can move , forth between types using following syntax:
var entity = new entitytype(); var viewmodel = entity.to<entityviewmodel>(); var backagain = viewmodel.toentity();
which quite content with, since original goal of abstracting away automapper implementation in controllers have been achieved.
you'll notice there non-generic version of ientity - that's empty interface, ientity<t>
inherits from. might considered dull, there's simple reason not use generic version: if do, you'll have specify type argument extension method. you'll end entity.to<entityviewmodel,int>()
instead of above syntax, since inferring type arguments all-or-nothing affair.
Comments
Post a Comment