Initialize some property of view object without foreach statment - c#

I have two Collection from Item & ItemView:
Item
{ Id, Name, DisplayName, Code, Price }
ItemView
{ DisplayName, Price }
In .net is it possible Initialize ItemViews with Items without foreach statement:
foreach (var item in Items)
itemViews.Add(new ItemView
{
DispalyName = item.DisplayName,
Price = item.Price
});
It mean something like cast, so just exist propery(DisplanyName,Price) fill in itemviews;
ItemViews = (ItemViews) Items;
Possible? how? i think i need operator overloading.
Edit:
Actually problem is my classes change during time, and this change has redunduncy in codes
I find Omu.ValueInjecter package, it works for one object but how use it for a collection?
foreach (var item in this._service.GetAll())
viewItemCats.Add((ItemCatInput)new ItemCatInput().InjectFrom(item));

Item and ItemView have common properties so you can define the common properties in a base class and use inheritance:
public class ItemView
{
public string DisplayName { get; set; }
public decimal Price { get; set; }
}
public class Item : ItemView
{
public int Id { get; set; }
public string Name { get; set; }
...etc...
}
With this approach you can treat any Item as an ItemView, here are two examples:
var itemViews = myListOfItems.Cast<ItemView>();
var myItemView = (ItemView) myItem;

The problem you described is due to your design.
It certainly looks like a Model-View design problem. I would approach it this way: Have ItemView "wrap" Item and expose properties used to view the item.
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Code { get; set; }
public decimal Price { get; set; }
}
public class ItemView
{
public ItemView(Item item)
{
this.item = item;
}
public string DisplayName
{
get
{
// TODO: make pretty name
return this.Item.Name + " (" + this.item.Code + ")";
}
}
public decimal Price
{
get { return this.item.Price; }
set { this.item.Price = value; } // TODO: add validation
}
private Item item;
}

Related

Opinions on mapping fields of nested/hierarchical objects to flat list?

I'm writing a tool which accesses a word document to prefill it with data. The document has a subset of custom document properties, each identified by a name, whose values are used to update fields in the document.
My ViewModel should both be able to initiate/update its instances from data of those document properties, aswell as write its values back and update the fields of the document.
Something like this:
class PersonVM : INotifyPropertyChanged
{
// properties
string Name { get; set; }
string PhoneNumber { get; set; }
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
// …
}
void updateMyPropertiesFromWord()
{
// …
}
}
class ProjectVM : INotifyPropertyChanged
{
int ProjectNumber { get; set; }
PersonVM Manager { get; set; }
PersonVM Mechanic1 { get; set; }
PersonVM Mechanic2 { get; set; }
void saveMyPropertyValuesToWord()
{
Manager.saveMyPropertyValuesToWord();
Mechanic1.saveMyPropertyValuesToWord();
Mechanic2.saveMyPropertyValuesToWord();
// handle ProjectNumber etc.
}
void updateMyPropertiesFromWord()
{
Manager.updateMyPropertiesFromWord();
Mechanic1.updateMyPropertiesFromWord();
Mechanic2.updateMyPropertiesFromWord();
// handle ProjectNumber etc.
}
class CompanyVM : INotifyPropertyChanged
{
string Name { get; set; }
PersonVM Owner { get; set; }
ProjectVM Project1 { get; set; }
ProjectVM Project2 { get; set; }
// …
}
// …
}
Right now I have a class with static string properties for each document property that might be present in a word document from which I would like to load the data accordingly:
class WordUtils
{
// Company
static string CompanyName = "dp_CompanyName";
// Company.Owner
static string CompanyOwnerName = "dp_CompanyOwnerName";
static string CompanyOwnerPhone = "dp_CompanyOwnerPhone";
// Company.Project1
static string CompanyProject1Number = "dp_CompanyProject1Number";
// Company.Project1.Manager
static string CompanyProject1ManagerName = "dp_CompanyProject1ManagerName";
static string CompanyProject1ManagerPhone = "dp_CompanyProject1ManagerPhone";
// Company.Project1.Mechanic1
// … etc
}
Now back to implementing those PersonVM.saveMyPropertyValuesToWord() - I thought of something like this:
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(WordUtils.OwnerName);
}
but here I need to know on class Level exactly what instance of it this is called from (i.e. what PersonVM am I, Company.Owner or Project1.Manager or ?) in order to decide which WordUtils.Name I need to provide.
I'm not sure how this should be done, maybe make PersonVM abstract and make a new class for each role (which would again only have one instance of itself, not very pretty in my eyes)? I have also taken a short look at Attributes and expect those might be helpfull in this scenario. Maybe I am missing something obvious, but extensive search for a robust way to tackle this problem have been fruitless so far.
How about something like this:
class Property
{
public string Key { get; }
public string Value { get; set; }
public Property(string key) => Key = key;
}
interface IPropertyTree
{
IEnumerable<IPropertyTree> ChildNodes { get; }
IEnumerable<Property> Properties { get; }
}
class PersonVM : IPropertyTree
{
private readonly string prefix;
public PersonVM(string prefix)
{
Name = new Property(prefix + "Name" );
PhoneNumber = new Property(prefix + "PhoneNumber");
}
public Property Name { get; }
public Property PhoneNumber { get; }
public IEnumerable<IPropertyTree> ChildNodes => Enumerable.Empty<IPropertyTree>();
public IEnumerable<Property> Properties => new[] {Name, PhoneNumber};
}
static class PropertyTreeExtensions
{
public static void Update(this IPropertyTree self)
{
foreach (var property in self.Flatten().SelectMany(tree => tree.Properties))
{
property.Value = MyApp.MyWordDocument.GetCustomProperty(property.Key);
}
}
public static IEnumerable<IPropertyTree> Flatten(this IPropertyTree self)
{
var stack = new Stack<IPropertyTree>();
stack.Push(self);
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in current.ChildNodes)
{
stack.Push(child);
}
}
}
}
This should allow each property to have a unique key, and keep the key and property value tightly coupled. It should also allow you to move the save/update logic to a centralized place.
Of course you can implement a concrete class of IPerson for each type and hard code the individual implementations.
Since you know the person type the moment you are creating an instance of PersonVMM, you could add an attribute PersonTypeId and set it from the constructor,
void SomeMethod()
{
var personVm = new PersonVM(WordUtils.OwnerName);
}
class PersonVM : INotifyPropertyChanged
{
// properties
string PersonTypeId { get; set; }
string Name { get; set; }
string PhoneNumber { get; set; }
public PersonVM()
{}
public PersonVM(string personTypeId)
{
PersonTypeId = personTypeId;
}
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(PersonTypeId);
}
}

Check if T implements an interface and return T after populating interface properties

I have a generic method that takes a list of T as a parameter. I am trying to get certain data based on the type of interface and populate only those properties of each item in the list, that are defined in the interface.
So my base interface is:
public interface IStatusResult
{
int Status { get; set; }
string StatusName { get; set; }
}
I have a number of other interfaces that implement this interface, which i want to use to figure out what Status/Status Names key/value pairs to retrieve:
public interface IROQStatusResult: IStatusResult { }
public interface IOrderStatusResult: IStatusResult { }
public interface IActivityStatusResult: IStatusResult { }
Finally, i implement this interface in my search result class:
public class RecommendedOrderQuantitySearchResult:IROQStatusResult {
public int Id { get; set; }
public DateTime TargetDate { get; set; }
public int Status { get; set; }
public string StatusName { get; set; }
}
So once a get a List from the Db i want to set the status names in the Generic method:
public static List<T> PopulateStatusNames<T>(List<T> items) where T:class
{
Dictionary<int, string> statuses = new Dictionary<int, string>();
if (typeof(IROQStatusResult).IsAssignableFrom(typeof(T))) {
statuses = GlobalConstants.GetROQStatuses();
}
if (typeof(IOrderStatusResult).IsAssignableFrom(typeof(T))){
statuses = GlobalConstants.GetOrderStatuses();
}
foreach (var item in items)
{
item.StatusName = statuses[item.Status];
}
return items;
}
Currently, item.StatusName is giving me an error: Item does not have property StatusName
How can i implement this?
Use OfType, it will return only those that can be safely cast into the type:
foreach (var item in items.OfType<IStatusResult>())
{
item.StatusName = statuses[item.Status];
}

c# Set Parent class property in child setter

Im trying to set a parent class property in child property setter.
I have one main class :User, which has a child class LIST ArrayPositions, which in turn has a child class list of ExpressionMember.
Whenever the property ExpressionMemValue in ExpressionMember class is set, i want to update it's parent class ArrayPosition aswell.
However the current solution does not update the corresponding parent.
Here's the code:
public List<User> users = new List<User>();
public class User
{
public string ImageName { get; set; }
private string _PartName = "";
public string PartName
{
get
{
return this._PartName;
}
set {
_PartName=value;
}
}
public List <ArrayPosition> ArrayPositions { get; set; }
public override string ToString()
{
return this.PartName.ToString();
}
}
public class ArrayPosition:User
{
public string myArrayPos = "";
public string PartId { get; set; }
public string ArrayPos
{
get
{
return this.myArrayPos;
}
set
{
this.myArrayPos = value;
}
}
public List<ExpressionMember> ExpressionMembers { get; set; }
}
public class ExpressionMember : ArrayPosition
{
public string ExpressionMem { get; set; }
public string MyExpressionMemValye="";
public string ExpressionMemValue
{
get
{
return MyExpressionMemValye;
}
set
{
MyExpressionMemValye = value;
// this.ArrayPos = value; //set parent value, this will not update it
}
}
}
It would appear that you need to not use inheritance and instead use composition which you are kind of already doing. Try doing this instead. It's not perfect by any means but I'm trying not to change your general strategy too much.
public class User
{
public string ImageName { get; set; }
private string _PartName = "";
public string PartName
{
get
{
return this._PartName;
}
set {
_PartName=value;
}
}
public List <ArrayPosition> ArrayPositions { get; set; }
public override string ToString()
{
return this.PartName.ToString();
}
}
public class ArrayPosition
{
public string myArrayPos = "";
public string PartId { get; set; }
public string ArrayPos
{
get
{
return this.myArrayPos;
}
set
{
this.myArrayPos = value;
}
}
public List<ExpressionMember> ExpressionMembers { get; set; }
}
public class ExpressionMember
{
private ArrayPosition _parentArrayPosition;
public string ExpressionMem { get; set; }
public string MyExpressionMemValye="";
public string ExpressionMemValue
{
get
{
return MyExpressionMemValye;
}
set
{
MyExpressionMemValye = value;
this._parentArrayPosition.ArrayPos = value;
}
public ExpressionMember(ArrayPosition parent) {
_parentArrayPosition = parent;
}
}
}
You are definitely not using inheritance and composition correctly. You are looking to build a tree of objects where the object itself has child objects. Something that might clarify things in your mind is instead of calling them child/parent classes, refer to them as sub/super classes in the case of inheritance and parent/child objects in the case of composition. A parent object is an instance of a class that contains another instance of a class (child object). A subclass inherits the members of another class.
Your inheritance is very strange. The exact responsibilities of your classes are not clear to me.
Apart from that, you could protect the property ExpressionMembers by making it read-only. Implement a new method to add or remove elements. Add an event (e.g. ExpressionMemValueChanged) to ExpressionMember . This event is triggered when an item is added. Whenever an element is added or removed you register/deregister ArrayPosition to/from this event. Inside the event handler you can then set your ArrayPos value.
(You can use an ObservableCollection for your ExpressionMembers and react to the CollectionChanged event instead of writing a getter/setter.)

How do I get an item from an icollection via index when in the class contrusctor I set the icollection as a list?

I have a set of messages each defined by:
namespace DatabaseDesign.Models
{
public class PrivateMessageDetail
{
public int PrivateMessageDetailId { get; set; }
public DateTime MessageDate { get; set; }
public string FromUser { get; set; } // Don't need ToUser, it's already in header
public string Message { get; set; }
public int PrivateMessageHeaderId { get; set; }
public virtual PrivateMessageHeader PrivateMessageHeader { get; set; }
}
}
The collection of messages between two users are held within the Messages iCollection, which is redefined (I think? ) As a list in the contructor of this class:
namespace DatabaseDesign.Models
{
public class PrivateMessageHeader
{
public int PrivateMessageHeaderId { get; set; }
public PrivateMessageHeader() { this.Messages = new List<PrivateMessageDetail>(); }
public DateTime TimeStamp { get; set; } // Date of the start of thread
public string User1 { get; set; }
public string User2 { get; set; } // this could be made to a list to allow multiples
public int numberOfMessages { get; set; }
public virtual ICollection<PrivateMessageDetail> Messages { get; set; }
}
}
In my Details controller, I want to viewBag return the private message history (i.e. an instance of PrivateMessageHeader )
public ActionResult Details()//string userTo, string userFrom)
{
string userTo = "mike", userFrom = "tim";
var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
string MsgStr = "";
foreach (var msg in MsgHistory.Messages)
{
//how do I get the message & user name from each MessageDetail in the Messages List of the MessageHeader class?
//MsgStr += MsgHistory.Messages[msg].
}
return View();
}
The line
var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
returns the correct PrivateMessageHeader (i.e. the message history between 2 users). I can't seem to get the data out though.
Attempt no.1: I passed the whole thing through the viewbag and attempted to take the data out that way. After 5 minutes of frustration I decided this probably wasn't the best way of doing it anyway. Surely it is more efficient to just store the data as a string and viewbag that bad boy?
So, I went on and did what is currently shown in the Details controller, and messed around trying to access the PrivateMessageDetails held within the Messages List/iCollection. It won't let me index them though in the foreach loop, saying that the iCollection can't be referenced in that way.... but in the contructor I define it as a list???
My queries:
These are entity framework 6 classes. If I define the
public virtual ICollection Messages { get; set; }
As public virtual List Messages { get; set; } will it still work correctly?
If so, why do all the MSDN examples use ICollection?
Is there an efficient way to take the messages and "userFrom" variables from the iCollection?
This is what I wanted to do:
var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
string MsgStr = "";
foreach (var msg in MsgHistory.Messages)
{
MsgStr += MsgHistory.Messages[msg].UserFrom +": "+MsgHistory.Messages[msg].Message+"<br>";
}
Viewbag.Message = MsgStr;
return View();
There's no need to try to access MsgHistory.Messages as though it was a list. The iterator variable in a foreach-loop is the current item in the list that you're looping through. So you don't need to get that item from the list. Try this:
foreach (var msg in MsgHistory.Messages)
{
MsgStr += msg.UserFrom + ": " + msg.Message + "<br>";
}
However, I'd strongly recommend not constructing HTML in an MVC controller. This is what views are for. Instead, use your controllers to construct a view model, then pass it to the view, like this:
public ActionResult Details()//string userTo, string userFrom)
{
var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
var viewModel = MsgHistory.Messages.ToList();
return View(viewModel);
}
And construct the HTML in the view like this:
#model IEnumerable<DatabaseDesign.Models.PrivateMessageDetail>
...
#foreach(var msg in Model) {
#:#msg.UserFrom: #msg.Messages<br>
}
Or possibly use an anonymous type, like this:
public ActionResult Details()//string userTo, string userFrom)
{
var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
var viewModel = MsgHistory.Messages.Select(x => new { x.UserFrom, x.Message }).ToList();
return View(viewModel);
}
With a dynamic view model:
#model IEnumerable<dynamic>
...
#foreach(var msg in Model) {
#:#msg.UserFrom: #msg.Messages<br>
}

List by GroupBy from IEnumerable

I am trying to create a list of Queues that are displayed by Queue Category. Each Queue Category is assigned an Enum value as such.
public enum QueueCategory
{
None=0,
Critical=1,
High=2,
Orphaned=3,
Missing=4
}
And for each Category, I want to then display these fields.
public class QueueInformation
{
public string Name { get; set; }
public Decimal PercentOfThreshold { get; set; }
public string Host { get; set; }
public DateTime OldestArrival { get; set; }
public QueueCategory Category { get; set; }
}
}
How would I go about linking these two pages so that QueueInformation is displayed by QueueCategory?
IEnumerable<QueueInformation> infos = ...;
foreach (var categoryGroup in infos.GroupBy(i => i.Category))
{
Console.WriteLine("Current category: {0}", categoryGroup.Key);
foreach (var queueInfo in categoryGroup)
{
Console.WriteLine(queueInfo.Name /*...*/);
}
Console.WriteLine("==========================");
}
I assume you want a source ordered by the QueueCategory:
IEnumerable<QueueInformation> list = new BindingList<QueueInformation>();
var orderedList = from l in list orderby l.Category select l;
Hope this helps

Categories

Resources