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