switch/case on viewmodel in controller, any refactoring advice? - c#

I could use some advice on refactoring. In my application users are able to dynamically add new form fields; customfield. For each type (text, dropdown, checkbox, etc.) a ViewModel (TextBoxViewModel, DropDownViewModel, CheckboxViewModel, etc.) is defined.
When I post a form, the appropriate Edit action is executed and I read each customfield to store their values.
Currently the implementation works but is ugly; I switch/case/if/else through all ViewModel types and based on the type I execute the required logic.
This is the the current implementation:
private static void MapToModel(Ticket ticket, TicketViewModel model)
{
ticket.Id = model.Id;
ticket.Name = model.Name;
ticket.Attributes.Clear();
foreach (var cvm in model.Controls)
{
var attribute = new TicketAttribute
{
Id = cvm.Id,
Name = cvm.Name,
};
if (cvm is TextBoxViewModel)
{
attribute.Value = ((TextBoxViewModel) cvm).Value;
}else if (cvm is DropDownListViewModel)
{
attribute.Value = ((DropDownListViewModel)cvm).Values;
}
ticket.Attributes.Add(attribute);
}
}
And I would like to refactor this to something like this, but without putting all logic in the ViewModel. Best I could come up with is the visitor pattern where I would add a Accept method to the ViewModel class, and use visitors to execute the logic required:
This would still require the same switching logic on types in the AddAttribute method:
foreach (var cvm in model.Controls)
{
ticket.Attributes.AddAttribute(cvm);
}
This would require logic in the ViewModel class
foreach (var cvm in model.Controls)
{
ticket.Attributes.Add(cvm.AddAttribute);
}
I want to refactor this to create a more generic approach, so that in future when new types of fields are added I don't have to update all codes with new constructions to check for types.
[solution after the provided help]
I had to cast the object, I cannot use different returntypes in different implementations of IControlViewModel so that is one part I have to work around, but overall this is beautiful.
ticket.Attributes = model.Controls
.OfType<IControlViewModel>()
.Select(cvm => new TicketAttribute {
Id = cvm.Id,
Name = cvm.Name,
Value = (string)cvm.OutputValue
})
.ToList();
public interface IControlViewModel
{
string Id { get; }
string Name { get; }
object OutputValue { get; }
}
public abstract class ControlViewModel : IControlViewModel
{
public string Id { get; set; }
public abstract string Type { get; }
public string Label { get; set; }
public string Name { get; set; }
public bool Visible { get; set; }
public abstract object OutputValue { get; }
}
public class TextBoxViewModel : ControlViewModel
{
public override string Type
{
get { return "textbox"; }
}
public override object OutputValue
{
get
{
return Value;
}
}
public string Value {set; }
}

1) Create an interface which defines that you will have output value property on each of your view models
public interface IControlViewModel
{
object OutputValue{get;}
}
2) Implement interface in each of your viewmodels:
public TextBoxViewModel: IControlViewModel
{
...
public object OutputValue
{
get
{
//return whatever is your expected output value from control
return Value;
}
}
...
}
3) Then you can get all attributes with this single LINQ statement:
ticket.Attributes = model.Controls
.OfType<IControlViewModel>()
.Select(cvm => new TicketAttribute {
Id = cvm.Id,
Name = cvm.Name,
Value = cvm.OutputValue
})
.ToList();
4) This code will work fine even if you create new control types, just make sure to implement interface in your new viewmodels.

Related

What pattern can be used to create an object of one class, but fill its properties in different ways?

I have a class like this
public class OwnerWithholding
{
private decimal ManagementFeePct;
private decimal TotalManagementFee;
private decimal OperationalFeesPct;
private decimal TotalOperationalFees;
}
And I have calculation method that create object of this class, fill it with some arithmetic operations, and return this object.
public OwnerWithholding CalculationMethod1(Reservation r, SqlConnection conn)
{
OwnerWithholding result = new OwnerWithholding();
// result.ManagementFeePct = some data from Fees table in DB + value
//from another db - constant value..
// Also with other properties - with some operations on data
//result.TotalManagementFee = ..
// result.OperationalFeesPct = ..
// result. TotalOperationalFees = ..
return result;
}
And now it works fine.
But this calculation method is just one option for populating data.
There is another calculation method, implemented in a completely different way, but filling exactly the same properties of the object. And I may have more of them.
I need a pattern that would allow me to create objects of the same class, just indicating the calculation method that is needed.
I like the strategy pattern , where the algorithms will be the methods that fill the objects that called them.
But it doesn’t look very good.
Maybe a factory method would be more appropriate here, but I don’t know how to implement it.
Edit: Going by the OPs comments now, it looks like just ONE method in the class needs to be set in multiple ways.
The Template pattern (or the Builder) are better fits in this case, not the Factory.
Template Pattern.
a. Abstract Base class that set default properties, but leaves out one property (Get Ingredients) to be populated by the concrete classes.
public abstract class PizzaCreator
{
public abstract string GetIngredients { get; }
public string Bake { get; set; } = "Bake at 400F for 30 minutes";
public string Deliver { get; set; } = "Deliver in custom box";
}
b. Two Pizza classes, for now just overriding the abstract property
public class CheesePizza : PizzaCreator
{
public override string GetIngredients
{
get { return GetMyIngredients(); }
}
string GetMyIngredients()
{
return "Lots of Cheese!";
}
}
public class PepperoniPizza : PizzaCreator
{
public override string GetIngredients
{
get { return GetMyIngredients(); }
}
string GetMyIngredients()
{
return "Lots of Meats!";
}
}
Here I'm creating instances of the Pizza
var pepPizza = new PepperoniPizza();
var chessePizza = new CheesePizza();
You could even have these creations routed through a Factory class/method.
Original answer:
Here is the Abstract Factory Pattern.
This code goes into the Factory class library.
a.ICar interface
public interface ICar
{
string Name { get; set; }
int Doors { get; set; }
string EngineCapacity { get; set; }
}
b.Abstract Car Factory
public abstract class AbstractCarFactory
{
public abstract ICar CreateCar(CarType type);
}
c.Two Concrete Cars -
internal class NissanPickUpTruck : ICar
{
public string Name { get; set; }
public int Doors { get; set ; }
public string EngineCapacity { get ; set ; }
}
internal class NissanSportsCar: ICar
{
public string Name { get; set; }
public int Doors { get; set; }
public string EngineCapacity { get; set; }
}
d.Concrete Factory
public class NissanFactory : AbstractCarFactory
{
public override ICar CreateCar(CarType type)
{
switch (type)
{
case CarType.PickupTruck:
return new NissanPickUpTruck{Name = "Titan", Doors = 6, EngineCapacity = "V12"};
case CarType.SportsCar:
return new NissanSportsCar{Name = "350Z", Doors = 2, EngineCapacity = "V6"};
default:
throw new Exception();
}
}
}
Finally the calls from an external project
var nissanFactory = new NissanFactory();
var sportsCar = nissanFactory.CreateCar(CarType.SportsCar);
var pickUpTruck = nissanFactory.CreateCar(CarType.PickupTruck);
But like the other comment, the Builder is something worth checking out as well.

Implement multiple times the same generic interface that includes properties with different type parameters

Well from the beginning, I've got a simple generic interface:
public interface IItemContainer<T> where T : Item
{
T ChosenItem { get; set; }
}
And an class that implements it multiple times:
public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{
public FabulousItem ChosenItem { get; set; }
NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }
GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }
}
I can't make the ChosenItems of types NiceItem and GreedyItem public, and also I can't access it like this:
ChosenItem<GreedyItem> = new GreedyItem();
cuz' I've got an error:
'GreedyItem' is a type, which is not valid in the current context
Is there anyway to use those props in this manner or I've got it all wrong and should do It with Dictionary or other way?
When you like to keep your generic IItemContainer you can implement a GetChosenItem and SetChosenItem method like this.
public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{
FabulousItem IItemContainer<FabulousItem>.ChosenItem { get; set; }
NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }
GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }
public T GetChosenItem<T>()
where T : Item
{
return ((IItemContainer<T>)this).ChosenItem;
}
public void SetChosenItem<T>(T value)
where T : Item
{
((IItemContainer<T>)this).ChosenItem = value;
}
}
Which comes very close to what you were trying to do.
container.SetChosenItem<NiceItem>(new NiceItem());
Yours is a case explicit interface implementations were made for. You give your conflicting items a unique name and forward the items to the interface. That also avoids any naming confusions:
public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem> {
public FabulousItem ChosenFabulousItem { get; set; }
public NiceItem ChosenNiceItem { get; set; }
public GreedyItem ChosenGreedyItem { get; set; }
FabulousItem IItemContainer<FabulousItem>.ChosenItem {
get {
return ChosenFabulousItem;
}
set {
ChosenFabulousItem = value;
}
}
NiceItem IItemContainer<NiceItem>.ChosenItem {
get {
return ChosenNiceItem;
}
set {
ChosenNiceItem = value;
}
}
GreedyItem IItemContainer<GreedyItem>.ChosenItem {
get {
return ChosenGreedyItem;
}
set {
ChosenGreedyItem = value;
}
}
}
Assignment is simple:
container.ChosenFabulousItem = new FabulousItem();
container.ChosenNiceItem = new NiceItem();
container.ChosenGreedyItem = new GreedyItem();
If you have more complex conversion logic in the background (e.g. you assign a FaboulousItem and need to convert it into a NiceItem), you can do so by providing getters and/or setters for your public properties.
It's completely wrong. As you should know you cannot make same names for member items (properties, fields etc. ). It will confuse compiler.
I would suggest to modify your interface a bit like :
public interface IItemContainer
{
List<Item> ChosenItems { get; set; }
T ChosenItem<T>() where T : Item;
}
Now in your implementation :
public class ItemContainer : IItemContainer
{
IItemContainer.ChosenItems
{
get { // your implementation
}
set { // your implementation
}
}
T IItemContainer.ChosenItem<T>()
{
return ((IItemContainer)this).ChosenItems.OfType<T>().FirstOrDefault();
}
}
This method will let you store different objects that derive from Item and return desired one using ChosenItem<T>() method.
EDIT:
I've got another interface which operates on List of Items, cuz' some submodules works only on one Item, and some only on set. I need also to store an instance of each implemented type independently.
You can always use something like a factory collection ( dont know if the name is correct ).
public class ChosenItemCollection
{
Dictionary<Type, Item> _fac = new Dictionary<Type, Item>();
public T Add<T>(T item) where T : Item
{
if(!_fac.ContainsKey(typeof(T))
{
_fac.Add(typeof(T), item);
}
else
{
_fac[typeof(T)] = item;
}
}
public T GetChosenItem<T>() where T : Item
{
if(_fac.ContainsKey(typeof(T))
return _fac[typeof(T)];
return null;
}
}
Then in your interface instead of List<Item> ChosenItems you can do ChosenItemCollection ChosenItems.
Using this in your example :
GreedyItem item = // ...
ItemContainer.ChosenItems.Add(item);
ItemContainer.ChosenItem.ChosenItem<GreedyItem>();
I think Pidon has a nice solution. But could result in a runtime error when using not implemented Item derives.
Another solution could be adding properties which will do the casts to the implemented types:
public class ChosenItemsContainer : IItemContainer<FabulousItem>, IItemContainer<NiceItem>, IItemContainer<GreedyItem>
{
// these properties are only visible when casting to the correct
// interface. Which the public properties below will do.
FabulousItem IItemContainer<FabulousItem>.ChosenItem { get; set; }
GreedyItem IItemContainer<GreedyItem>.ChosenItem { get; set; }
NiceItem IItemContainer<NiceItem>.ChosenItem { get; set; }
// return this as IItemContainer<FabulousItem>
public IItemContainer<FabulousItem> AsFabulous
{
get
{
return (IItemContainer<FabulousItem>)this;
}
}
// return this as IItemContainer<NiceItem>
public IItemContainer<NiceItem> AsNice
{
get
{
return (IItemContainer<NiceItem>)this;
}
}
// return this as IItemContainer<GreedyItem>
public IItemContainer<GreedyItem> AsGreedy
{
get
{
return (IItemContainer<GreedyItem>)this;
}
}
}
ChosenItemsContainer container = new ChosenItemsContainer();
container.AsFabulous.ChosenItem = new FabulousItem();
container.AsNice.ChosenItem = new NiceItem();
container.AsGreedy.ChosenItem = new GreedyItem();
This way each implemented type has it's own ChosenItem instance. I think this is a clean solution without cluttering of generic <T> in code.

AutoMapping objects

I have a weird situation where I have objects and Lists of objects as part of my entities and contracts to interface with a third-party service. I'm going to try to see if I can replace the actual object class with something more specific in the entities and contracts to get around this, but I am curious if there is a way to get AutoMapper to handle this as is.
Here are some dummy classes:
public class From
{
public object Item { get; set; }
}
public class FromObject
{
public string Value { get; set; }
}
public class To
{
public object Item { get; set; }
}
public class ToObject
{
public string Value { get; set; }
}
And the quick replication:
Mapper.CreateMap<From, To>();
Mapper.CreateMap<FromObject, ToObject>();
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // FromObject
Basically, the question is this: Is there a way to get AutoMapper to understand that from.Item is a FromObject and apply the mapping to ToObject? I'm thinking there's probably not a way to make it automatic, since there's nothing that would indicate that to.Item has to be a ToObject, but is there a way to specify during the CreateMap or Map calls that this should be taken into account?
I don't think there is an "automatic" way of doing it, since AutoMapper won't be able to figure out that From.Item is FromObject and To.Item is ToObject.
But, while creating mapping, you can specify that
Mapper.CreateMap<FromObject, ToObject>();
Mapper.CreateMap<From, To>()
.ForMember(dest => dest.Item, opt => opt.MapFrom(src => Mapper.Map<ToObject>(src.Item)));
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // ToObject
If you're willing to use an additional interface, this can be accomplished using Include. You can't just map object to object in this fashion, though.
public class From
{
public IItem Item { get; set; }
}
public class FromObject : IItem
{
public string Value { get; set; }
}
public class To
{
public object Item { get; set; }
}
public class ToObject
{
public string Value { get; set; }
}
public interface IItem
{
// Nothing; just for grouping.
}
Mapper.CreateMap<From, To>();
Mapper.CreateMap<IItem, object>()
.Include<FromObject, ToObject>();
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // ToObject

Programmatically type an object

Programmatically type an object
C# mvc4 Project
I have two similar ViewModels, that contain over a dozen complex objects, that I want to call a common method from my Create and Edit Actions to populate the ViewModels.
Something along the lines of this
private void loadMdlDtl(CreateViewModel cvM, EditViewModel evM)
{
If (vM1 != null) { var vM = vM1}
If (vM2 != null) { var vM = vM2}
// about two dozen complex objects need to be populated
vM.property1 = …;
vM.property2 = …;
…
}
This doesn’t work because vM isn’t in scope.
Is there any way to Programmatically type the vM object so that I don't have to create two loadModel methods or otherwise duplicate a lot of code ?
SOLUTION:
Create an Interface:
public interface IViewModels
{
string property1 { get; set; }
int property2 { get; set; }
IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
}
Have View Models inherit from interface:
public class CreateViewModel : IViewModels, IValidatableObject
{
string property1 { get; set; }
int property2 { get; set; }
IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
{
// implementation
}
}
public class EditViewModel : IViewModels, IValidatableObject
{
string property1 { get; set; }
int property2 { get; set; }
IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
{
// implementation
}
}
Call the method from Actions passing the View Model:
public ActionResult Create()
{
var vM = new CreateViewModel();
...
loadMdlDtl(vM);
...
}
But now accept the interface rather than the View Model into the method:
private void loadMdlDtl(IViewModel vM)
{
// implementation
}
Since you want to access properties and/or methods that are the same across all objects, you can define an interface with such properties and methods. Have each object implement that interface.
public interface IMyCommonStuff
{
string property1 { get; set; }
int property2 { get; set; }
int SomeMethod();
}
UPDATE
If some of the methods and/or properties have identical implementations, that implementation can be done in a common base type. I would suggest still using an interface definition when acting on your objects. Example:
public class MyCommonImplementation : IMyCommonStuff
{
public virtual int SomeMethod()
{
// Implementation goes here.
}
public string property1 { get; set; }
public int property2 { get; set; }
}
public class MyConcreteSubclass : MyCommonImplementation, IMyCommonStuff
{
// Add only the things that make this concrete subclass special. Everything
// else is inherited from the base class
}
Eric's answer is the standard way of doing it, but if you want to save time, you could use dynamic keyword to define vM, such as:
dynamic vM;
if (vM1 != null) vM = vM1;
if (vM2 != null) vM = vM2;
//about two dozen complex objects need to be populated
vM.property1 = …;
vM.property2 = …;
…

Binding to interface and displaying properties in base interface

This question (along with its answer) explains why you can't easily bind a DataGridView to an interface type and get columns for properties inherited from a base interface.
The suggested solution is to implement a custom TypeConverter. My attempt is below. However, creating a DataSource and DataGridView bound to ICamel still only results in one column (Humps). I don't think that my converter is being used by .NET to decide which properties it can see for ICamel. What am I doing wrong?
[TypeConverter(typeof(MyConverter))]
public interface IAnimal
{
string Name { get; set; }
int Legs { get; set; }
}
[TypeConverter(typeof(MyConverter))]
public interface ICamel : IAnimal
{
int Humps { get; set; }
}
public class MyConverter : TypeConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
if(value is Type && (Type)value == typeof(ICamel))
{
List<PropertyDescriptor> propertyDescriptors = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(ICamel)))
{
propertyDescriptors.Add(pd);
}
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(IAnimal)))
{
propertyDescriptors.Add(pd);
}
return new PropertyDescriptorCollection(propertyDescriptors.ToArray());
}
return base.GetProperties(context, value, attributes);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
DataGridView does not use TypeConverter; PropertyGrid uses TypeConverter.
If it relates to list-controls like DataGridView, then the other answer is wrong.
To provide custom properties on a list, you need one of:
ITypedList on the data-source
TypeDescriptionProvider on the type
Both are non-trivial.
My Workaround happens in the binding of the dgv.
I do need that the base interfaces and the inheriting interfaces remain in the same structure, just because i do other things width the final concerete class, not only show the data on a DataGridView. So, for example:
interface IGenericPerson
{
int ID { get; set; }
string Name { get; set; }
}
interface IOperator : IGenericPerson
{
bool IsAdmin { get; set; }
}
the concrete class:
class Operator : IOperator
{
public Operator(){}
public Operator(int id, string name, bool isAdmin)
{
this.ID = id;
this.Name = name;
thsi.IsAdmin = isAdmin;
}
public int ID { get; set; }
public string name { get; set; }
public bool IsAdmin { get; set; }
}
and in a Gateway Class:
public IList<IOperator> GetOperators()
{
IList<IOperator> list = new List<IOperator>();
list.add(new Operator(112, "Mark Twain", false);
list.add(new Operator(112, "Charles Manson", false);
list.add(new Operator(112, "Richard Nixon", true);
return list;
}
Now, if i try to bind a datagridView like this:
Gateway gt = new Gateway();
dgv.DataSource = gt.GetOperators();
I get a DataGridView with the only bool IsAdmin column from the IOperator Interface, not the ID, neither the Name propertys from its base interface.
but if I do this:
Gateway gt = new Gateway();
IList<IOperator> list = gt.GetOperators();
IList<Operator> ds = new List<Operator>();
foreach(IOperator op in list)
ds.add((Operator)op);
dgv.DataSource = ds;
Everything works in the right way.
In this way i don't need to change the structure of the intarfaces chain, useful for other purposes, and only qhen displaying data i just insert the snippet above.
My Suggestion would be to create a Interface that "reimplements" the propertys you want:
Let's say you have two interfaces:
public interface IHasName1
{
String Name1 { get; set; }
}
public interface IHasName2 : IHasName1
{
String Name2 { get; set; }
}
And a class that implements IHasName2:
public class HasTwoNames : IHasName2
{
#region IHasName1 Member
public string Name1 { get; set; }
#endregion
#region IHasName2 Member
public string Name2 {get; set; }
#endregion
}
Now, thx for figuring that out btw., if you have a List with objects of concrete type HasTwoNames and you bind that list to a dgv, it only displays the member (Name2) of IHasName2.
A "workaround" is to create a new interface "IHasEverything" that inherits from IHasName2 and therefore from IHasName1 and reimplements the Propertys you need in your binding (you can do that with the new statement
public interface IHasEverything : IHasName2
{
new String Name1 { get; set; }
new String Name2 { get; set; }
}
Now your concrete class "HasTwoNames" needs to implement IHasEverything, too:
public class HasTwoNames : IHasName2, IHasEverything
{
...
}
You can bind this List to a datagridview:
public List<IHasEverything> elements = new List<IHasEverything> {
new HasTwoNames { Name1 = "Name1", Name2 = "Name2"},
new HasTwoNames { Name1 = "Name3", Name2 = "Name4"},
};
I know that this is just a workaround and only possible if you can modify the implementing class. But it works.
(If you remove a property from IHasName2, the code will still compile but you get a warning that IHasEverything does not need the new keyword.

Categories

Resources