I have a project written in C# MVC 5. There are several Controllers/Views that perform a similar set of functions based on <input ... fields in the Razor View.
There's currently logic in each controller to save input field values to a database table and later allow these saved input values to be retrieved and the View's input fields are populated by them.
These Views have many similarities and so several of their fields are duplicated across these Models.
It's become difficult to maintain code in each controller that saves its own fields and I want to use generics to create a single, common routine that saves the values for any of these controllers.
For example, 2 simple ViewModels:
public class ViewModel1
{
public string Name { get; set; }
public int Unique1 { get; set; }
...
}
public class ViewModel2
{
public string Name { get; set; }
public int Unique2 { get; set; }
...
}
Database model used:
public class SavedInputs
{
[Key] public int Id { get; set; }
public string Name { get; set; }
public int? Unique1 { get; set; }
public int? Unique2 { get; set; }
}
Now I want to create a function that can save either ViewModel's fields. Something like:
public bool SaveToDb(T model)
{
var inputs = new SavedInputs()
{
Name = model.Name,
Unique1 = model.Unique1,
Unique2 = model.Unique2
}
_db.SavedInputs.Add(inputs);
...
}
Then from each controller's Save action:
public ActionResult SaveInputs(ViewModel1 model)
{
var success = SaveToDb(model);
}
Of course ... SaveToDb() above isn't going to work but hopefully it shows what I'm wanting to accomplish.
There's many ways to do this.
Either you could have a common interface or abstract class that all your view models share which have the properties you want to save. Then SaveToDb just takes the interface instead of the generic type. Then in SaveToDb you can access all the properties of the interface properties from the model passed in and do whatever you want with them. I guess if you want to just have some properties null then abstract class makes sense, since you can override whatever you want to use
e.g.
public abstract class SavedInputsBase
{
public virtual string Name { get; set; }
public virtual int Unique1 { get; set; }
public virtual int Unique2 { get; set; }
}
public class ViewModel1 : SavedInputsBase
{
public override string Name { get; set; }
public override int Unique1 { get; set; }
...
}
public class ViewModel2 : SavedInputsBase
{
public override string Name { get; set; }
public override int Unique2 { get; set; }
...
}
public bool SaveToDb(SavedInputsBase model)
{
var inputs = new SavedInputs()
{
Name = model.Name,
Unique1 = model.Unique1,
Unique2 = model.Unique2
}
_db.SavedInputs.Add(inputs);
}
Alternatively you could use something like Automapper nuget package and have SaveToDb take SavedInputs and then just map your viewmodel to SavedInputs. Or just manually map it everywhere you want to call SaveToDb.
Related
I am currently working on making viewmodels capable of parsing data extracted from database to the UI and vice versa, and to do so I do a lot of manual mapping between my two viewmodels.
Currently I try to pass some values that determines an attribute, but since each attributetype requires specifying a lot specific parameter, and 90% of the variables will be redundant in all cases since attributes only have one type..
Thus i have create a placeholder base class, which just contains an Id, that each atttribute have,
and each of the specific attribute type parameter will then use this placeholder as base class..
example:
public class BooleanViewSpecification : AttributeTypeSpecification
{
public string TrueOptionText { get; set; }
public string FalseOptionText { get; set; }
}
public class DateTimeTypeViewSpecification : AttributeTypeSpecification
{
public DateTime EarliestDataTime { get; set; }
public DateTime LatestDataTime { get; set; }
}
and my Attribute class is just an
public class AttributeView
{
public DataType Type { get; set; }
public AttributeTypeSpecification AttributeTypeViewSpecification { get; set; }
}
And the same Goes for my DB view model
public class BooleanSpecification : AttributeTypeSpecification
{
public string TrueOptionText { get; set; }
public string FalseOptionText { get; set; }
}
public class DateTimeTypeSpecification : AttributeTypeSpecification
{
public DateTime EarliestDataTime { get; set; }
public DateTime LatestDataTime { get; set; }
}
and my Attribute class is just an
public class Attribute
{
public DataType Type { get; set; }
public AttributeTypeSpecification AttributeTypeSpecification { get; set; }
}
Problem is then mapping from one class to another class
public static IEnumerable<AttributeView> MapToViewModel(this IEnumerable<Attribute> attributes)
{
return attributes.Select(z => new AttributeView()
{
Type = z.Type,
AttributeTypeViewSpecification = z.AttributeTypeSpecification
});
}
Which does not seem to work?
I use entity framework and migrate using Code-First what I receive is the Id of the location, and not the actual values?
I cant seem to understand why I cant be given the values - if it during the mapping does have the value?
So why cant they be mapped over?
I retrieve the value
Context.Include(Attribute).ThenInclude(AttributeTypeSpecification)
The only thing I receive is the actual Id rather than the specified entries?
Given are the following classes:
public class Rule
{
public long Id { get; set; }
public string Filter { get; set; }
public RuleAction Action { get; set; }
}
public abstract class RuleAction
{
}
public class RuleAction1 : RuleAction
{
public string Value { get; set; }
}
public class RuleAction2 : RuleAction
{
public decimal Percent { get; set; }
}
I want to map these classes to the following table layout. I use Entity Framework Core Preview 2.
Table "Rule"
- Id
- Filter
- ActionDiscriminator
- Value // only set if the object in Action is typeof(RuleAction1)
- Percent // only set if the object in Action is typeof(RuleAction2)
The important part is that "Action" is not mapped to a separate table. I know i can map the property as a "Owned property" like described in this article (OwnsOne): https://blogs.msdn.microsoft.com/dotnet/2017/06/28/announcing-ef-core-2-0-preview-2/ but this dosn't seem to work in combination with inheritance, at least i couldn't find an example.
Anybody knows how to combine owned properties with inheritance?
Could you just do something like this:
public class RuleAction1 : RuleAction
{
public string Value { get; set; }
public decimal Percent { get; set; } = null;
}
public class RuleAction2 : RuleAction
{
public decimal Percent { get; set; }
public string Value { get; set; } = null;
}
That way those value match the table schema but just default to null values. Or you could do something like this:
public abstract class RuleAction
{
public string Value { get; set; } = null;
public decimal Percent { get; set; } = null;
}
public class RuleAction1 : RuleAction
{
}
public class RuleAction2 : RuleAction
{
}
I could be way off, sorry if this is only slowing you down.
My program is starting to get pretty big. and i have found that its starting to do the same thing in multiple area's.
Im trying to figure out how i can make it more efficient.
So i have an object that looks like this
public class TreeViewNode
{
public TreeViewNode()
{
Children = new ObservableCollection<TreeViewNode>();
}
public String Name { get; set; }
public string Description { get; set; }
public ObservableCollection<TreeViewNode> Children { get; set; }
}
i also have another object that looks like this;
public class ComputerObject
{
public String Name { get; set; }
public Int32 UUID { get; set; }
public DateTime Created { get; set; }
public ObservableCollection<Object> Children { get; set; }
}
Both these items need to have some of the same properties..
at the moment they both have the Children Property and the Name Property. but they both need to have some other common properties added to them.
so i have tried something like this.
public class BaseObject
{
public String Name { get; set; }
public ObservableCollection<Object> Children { get; set; }
public string Description { get; set; }
public BaseObject()
{
Children = new ObservableCollection<object>();
}
}
public class ComputerObject: BaseObject
{
public Int32 UUID { get; set; }
public DateTime Created { get; set; }
}
public class TreeViewNode: BaseObject
{
public String IconPath { get; set; }
}
Now this is just a cut down version of what i am implementing, i have alot of objects that share the same properties. and some that dont and mix and match. and i cannot figure out the best implimentation for this.
My Objects are becoming very cluttered, and when i rename a property i find that i have to rename it in several area's and this isnt the way its ment to be.
can someone please advise how i would implement multiple objects that share the same property names?
In my opinion you should not let classes inherit from one baseclass when these childclasses are not related to each other (like #Sriram Sakthivel asked Animal < Dog,Cat) just to share the same properties.
You should determine which classes are related (cat, dog are animals; car, motorcycle are vehicles) and then create baseclasses based on these "groupings".
I would look into decorator pattern. In short, you dont share common properties via inheritance. You make classes that contain common properties, and use these classes as properties in your end classes.
EDIT: Example is actually just a standard composition, it should work nevertheless
E.G.
public class Decorator1
{
public String Name { get; set; }
public ObservableCollection<Object> Children { get; set; }
public string Description { get; set; }
}
public class Decorator2
{
public long Id { get; set; }
}
public class ClassA
{
public Decorator1 TreeNodeImpl;
}
public class ClassB
{
public Decorator1 TreeNodeImpl;
public Decorator2 LongIdImpl;
}
Hi I have a situation in witch I have to create some custom validation attributes because the way my model is created.The model looks something like this:
public class EvaluationFormDataContract
{
public int StudentAssignmentInstanceId { get; set; }
public int EvaluationType { get; set; }
public List<CategoriesOnEvaluationDataContract> Categories { get; set; }
}
public class CategoriesOnEvaluationDataContract
{
public string Memo { get; set; }
public int CategoryId { get; set; }
public List<QuestionsOnEvalCategoryDataContract> Questions { get; set; }
// Fields needed for validation
public bool? HasMemo { get; set; }
public bool MemoIsMandatory { get; set; }
}
public class QuestionsOnEvalCategoryDataContract
{
public string Memo { get; set; }
public string Grade { get; set; }
public int QuestionId { get; set; }
// Fields needed for validation
public bool HasGrade { get; set; }
public bool HasMemo { get; set; }
public bool ShowOnlyMemo { get; set; }
}
As it can be seem the model is composed two levels deep.
And I will have to validate starting from the second level , where I will check if the model HasMemo and if MemoIsMandatory.
The third validation should be done at the 3rd level where I have to check if it HasGrade and HasMemo.
Normaly if it were up to me I would split this in three separate calls to the server but we are depending on an legacy project and for the moment I have to make this work.
The post action will be called via an ajax call and will have all this data into it.
Now my question is where should I add the validation attribute?
Should it be added at the top on Categories , making it directly responsible for all the levels of the model?
Or I should place it on each model and find a way to make the data binder aware of it? If so how can I do this?
You can do both. If you implement System.ComponentModel.DataAnnotations.IValidatableObject interface at the top-most level, you can do whatever you want with the properties in the entire graph and return the errors.
public class EvaluationFormDataContract : IValidatableObject
{
// All properties go here
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (// do what you want)
yield return new ValidationResult("message");
}
}
Or, you can apply attributes at the lower levels and automatically binding takes care of validating the properties in the graph. You don't need to do anything special.
I have four MVC model layer domain classes.
namespace MvcMobile.Models.BusinessObject
{
public class Speaker
{
public int SpeakerID { get; set; }
public string SpeakerName { get; set; }
}
public class Tag
{
public int TagID { get; set; }
public string TagName { get; set; }
}
public class Seminar
{
public string Seminar_Code { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Room { get; set; }
}
public class Seminar_Detail
{
public string Seminar_Code { get; set; }
public int SpeakerID { get; set; }
public int TagID { get; set; }
public string DateAndTime { get; set; }
}
}
I would like to make CRUD operation by using these classes. So I create two VeiwModel Classes.
namespace MvcMobile.ViewModel
{
public class Seminar_Root_ViewModel
{
public Seminar_Subsidiary_ViewModel Seminars { get; set; }
public List<Speaker> Speakers { get; set; }
public List<Tag> Tags { get; set; }
}
public class Seminar_Subsidiary_ViewModel
{
public Seminar Seminar { get; set; }
public List<Seminar_Detail> Seminar_Detail { get; set; }
}
}
For Controller layer, I consider that I will use Seminar_Root_ViewModel to make the whole CRUD operation processes.
What I would like to ask is that Is this proper way or correct way?
If you have more elegant way to make model layer and ViewModel layer, Please let me get suggestion.
Every suggestion will be appreciated.
[updated]
Let's assume that I make master-Detail form design.
Speaker and Tag are just look-up tables for dropdownlist or some controls like that.
Seminar is Master Data and Seminar_Detail will be Item Grid Data.
So As for this scenario, all of this classes are needed for this program.
Please let me know if my thinking is wrong.
The only thing I can see is if you are not going to re-use your Seminar_Subsidiary_ViewModel view model you could skip it.
If you are going to need those two properties Seminar and Seminar_Detail on another view or ajax call, it's perfectly fine to have that kind of separation.
Personally I'm not a huge fan of _ on class name, but that have nothing to do with the question.