c# - How to seperate commands out of big viewmodel -
my viewmodel contains lots of commands, makes viewmodel big. want seperate command out of viewmodel. currently, soluton create class each command below,
public class testcommand : commandbase { private mainviewmodel vm; public testcommand(mainviewmodel vm) { this.vm = vm; } public override bool canexecute(object parameter) { return true; } public override void executecommand(object parameter) { vm.logger.log(...); ... } }
since need use method or properties in viewmodel, have pass viewmodel parameter command. solution, there're 2 disadvantages: 1. there're lots of command files in project, if average count of command in 1 view 15, 10 views have 150 command files in project; 2. passing viewmodel parameter command needs properties or methods should private have been change public; it's strange pass viewmodel command.
is there other solution seperate commands?
tl;dr:
viewmodels presentation logic expressed in commands, it's not unusual commands take big chunk of viewmodel's code. don't try hard make viewmodels plain data holders (like common in asp.net mvc when using viewmodels) inotifypropertychanged
.
long version
with lack of more details it's hard give concrete tips, here general guidelines. may update question more details kind of commands using , i'll try update question.
presentation logic
main concern of viewmodels presentation. there no place business logic in viewmodels.
business logic has extracted either business/domain models (if follow rich domain model) or services (in anemic domain model). in rich domain model service layers quite thin , there coordinate actions between multiple models.
so if viewmodel/commands doing kind of logic not related presentation (if click button a, disable buttons b, c , d or hide groupboxa or "disable button if data missing (
canexecute
oficommand
)) it's doing much.separation of concerns:
it may possible viewmodel tries more it's intended do. logger in example such hint. logging not viewmodel concern.
viewmodel presentation , presentation logic , logging application concern (as not belong domain/business logic).
often viewmodel can split 2 or more viewmodels (i.e. viewmodel manages list of customers , allow edit selected customer, can split 2 or 3 viewmodels:
customersviewmodel
(display list),customerdetailviewmodel
orcustomerviewmodel
(detail customer) ,customereditviewmodel
(to edit customer in question)concerns logging , caching should done using i.e. decorator pattern. requires services and/or repositories use , implement interfaces, can create decorators caching or logging , rather inject original instance of service, implement decorator.
dependency injection (di) , inversion of control (ioc) containers this. having manually wire (aka poor mans di) pain in butt. concrete examples out of scope of answer.
business logic in commands
commands shouldn't contain business logic. when commands contain code (usually more 5-20 lines of code) clue, commands may much.
commands should wire multiple calls services , assign data properties and/or rise events/messages (that relevant presentation layer. not confused domain events, shouldn't raised inside commands). similar "actions" in mvc (like used frameworks in asp.net mvc example).
commands should this
var customer = new customer { name = this.customername, mail = this.customermail }; try { this.customerservice.addcustomer(customer); // add observable<customer> list ui gets updated this.customers.add(customer); // service should have populated id field of customer when persisting // notify other viewmodels new customer has been added this.messagebus.publish(new customercreated() { customerid = customer.id } ); } catch (somespecificexception e) { // handle exception }
or
this.customers = this.customerrepository.getall(); // or async commands this.customers = await this.customerrepository.getallasync();
encapsulation
many commands tightly coupled viewmodel , need access internal state of the viewmodel or model (the model shouldn't directly exposed view, couples model view , change in model breaks view , bindings).
moving these
icommands
out viewmodels may difficult without breaking encapsulation.
of course can implement multiple commands in 1 class
public class myviewmodelcommandhandler { private readonly imyrepository myrepository; public myviewmodelcommandhandler(/* pass dependencies here*/) { // assign , guard dependencies mycommand = new relaycommand(mycommand, canexecutemycommand); myothercommand = new relaycommand(myothercommand, canexecutemyothercommand); } public icommand mycommand { get; protected set; } public icommand myothercommand { get; protected set; } private void mycommand() { // } private void canexecutemycommand() { // validate } private void myothercommand() { // else } private void canexecutemyothercommand() { // validate } }
and in viewmodel assign these commands
public class myviewmodel : viewmodelbase { public myviewmodel() { var commandhandler = new mycommandhandler(this); onecommand = commandhandler.mycommand; othercommand = commandhandler.myothercommand; } public icommand onecommand { get; private set; } public icommand othercommand { get; private set; } }
you can inject mycommandhandler
view using ioc container, require remodeling command handler class bit, create icommand
on demand. may use
public class myviewmodel : viewmodelbase { public myviewmodel(mycommandhandler commandhandler) { onecommand = commandhandler.createmycommand(this); othercommand = commandhandler.createmyothercommand(this); } public icommand onecommand { get; private set; } public icommand othercommand { get; private set; } }
but shifts problem, won't solve points 1. 5. though. i'd suggest try suggestions aboves list first , if commands still contain "too many lines of code", try other solution.
i don't creates unnecessary abstractions little gain.
it's not uncommon viewmodels consist of presentation logic, that's purpose , presentation logic typically inside commands. except that, have properties , constructor. properties shouldn't have else other checking if value changed, assign , 1 or more onpropertychanged
calls.
so 50-80% of viewmodel code commands.
Comments
Post a Comment