WPF - Instance subclass from superclass - c#

I'm doing and WPF application and I have a ViewModel that I use in several Views and in DataGrids.
Now I have another View that requires an extended or decorated version of that ViewModel. So I decided to go for inheritance in this way:
public class StandardViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class ExtendedViewModel : StandardViewModel
{
public string Email { get; set; }
}
However, I want to decorate and existing instance of the StandardViewModel. Specifically the selected object in the DataGrid so it can be passed into the other View.
The new View needs access to the properties of both classes (the Email and the FirtsName and LastName)
So I'm thinking of ways to creating a constructor for my ExtendedViewModel.
My idea is to copy the base instance directly.
Is this correct?
And efficient?
Is there any other way of doing it?
public class ExtendedViewModel : StandardViewModel
{
public string Email { get; set; }
public ExtendedViewModel(StandardViewModel base)
{
this = base
}
}
Edit
I'm doing this for not only one but several classes. And they do not have only two properties so I'm trying to avoid copying the values one by one.

Finally I'll avoid using inheritance and I'll create a new class, expose the base class and subscribe to INotifyPropertyChanged as described here and here.
This way I'll be able to have properties that depend on the Base ViewModel updated as done with FullName below.
The resulting ViewModel will look like:
public class ExtendedViewModel
{
public StandardViewModel Base { get; set; }
public string Email { get; set; }
public string FullName {
get => Base.FirstName + Base.LastName;
}
public ExtendedViewModel(StandardViewModel base)
{
Base = base
Base.PropertyChanged += BaseChanged
}
private void BaseChanged(object sender, PropertyChangedEventArgs e)
{
// Here check if FirstName or LastName changed and
RaisePropertyChanged("FullName");
}
}
In the view I will bind directly to Email or to Base.FirstName.

In oversimplified terms a decorator owns an instance of the class it decorates and delegates functionality existing in the owned class to that class, while adding new functionality uncoupled from the owned class instance.
public class StandardViewModel
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class ExtendedViewModel : StandardViewModel
{
private StandardViewModel _standard;
public ExtendedViewModel (StandardViewModel standard)
{
if (standard.GetType() != typeof(StandardViewModel )) {
throw new ArgumentException ("Expected a non derived standard view model", nameof(standard));
}
_standard = standard;
}
public string Email { get; set; }
public override string FirstName {
get => _standard.Firstname;
set => _standard.Firstname = value;
}
public override string LastName {
get => _standard.LastName;
set => _standard.LastName = value;
}
}

Related

Overriding a base virtual property with a derived type is null when passing to JsonResult

I have 2 base classes which 1 for search criteria and other 1 for search results.
I also have 2 derived classes for User object versions of both of those.
When I put a breakpoint in the controller action I can see all properties are populated as I've hardcoded.
When I call this action directly in the browser, each of my derived object properties is null.
I'm guessing the JSON serialization is not able to tell the difference from the base class to the derived one.
Is there a way to solve this?
public class BaseSearchCriteria
{
public int Page { get; set; }
public int RecordsPerPage { get; set; }
}
public class BaseSearchResults
{
public int TotalResults { get; set; }
public virtual BaseSearchCriteria SearchCriteria { get; set; }
}
public class UserSearchCriteria : BaseSearchCriteria
{
public string Username { get; set; }
}
public class UserSearchResults : BaseSearchResults
{
public new UserSearchCriteria SearchCriteria { get; set; }
}
public JsonResult Search(UserSearchCriteria model)
{
var viewModel = new UserSearchResults
{
SearchCriteria = new UserSearchCriteria
{
Page = 1,
RecordsPerPage = 15
}
};
viewModel.TotalResults = 100;
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
Maybe good way to deal with it is to use generics as Daniel A. White propose.
Sample gist here.

error 1061. T doesn't contain a definition for <propertyName>

i am having around 7 models who have same properties(atributes). On view page i am using a model(name = commonModel) which contains all those properties and a extra property to choose in which model's database i want to save that sent data so i created a valuesRelocate Method that will assign all the properties of commonModel to the choosen model (in this case article).
The code i gave below is working but i am getting a error when assigning value of a property of commonModel to a property of article.
What is the better way to do this.
Error is at tempModel.question
public ActionResult Create([Bind(Include =
"Id,question,ans,ruleApplicable,hint,exception,modelSelector")]
commonModel commonModel)
{
if (ModelState.IsValid)
{
if (commonModel.modelSelector == "article")
{
article model2 = new article();
article model1 = valuesRelocate<article>(commonModel,
model2);
db.articleDb.Add(model1);
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(commonModel);
}
private T valuesRelocate<T>(commonModel commonModel, T tempModel) {
tempModel.question = commonModel.question;
return tempModel;
}
I am using a abstract base class named baseGrammar .code for both the class is shown below
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleApplicable { get; set; }
public string hint { get; set; }
public bool exception { get; set; }
}
the one shown above is base class
and those shown below are derived classes
i use different classes because i wanted to have different classes for different grammatical concepts.
public class article : baseGrammar
{
}
public class commonModel : baseGrammar
{
[Required]
public string modelSelector { get; set; }
}
hope this helps.
You just need to constrain the type parameter T to be derived from your base class:
// Names changed to follow .NET naming conventions
private T RelocateValues<T>(BaseGrammar baseModel, T tempModel)
where T : BaseGrammar
{
tempModel.question = baseModel.question;
return tempModel;
}
However, given that you're modifying the incoming model, you could remove the return value and just change the method to:
private void RelocateValues(BaseGrammar from, BaseGrammar to)
{
to.question = from.question;
}
Then in your calling code:
Article model = new Article();
RelocateValues(model);
db.ArticleDb.Add(model);
There's no need to have two separate variables which will refer to the same object anyway...

AutoMapper isn't recognizing profile-specific prefixes

I'm trying to use AutoMapper to take data from a class that has prefixes before property names and map it to a second class that doesn't have those prefixes. However, I don't necessarily want it to always strip out that prefix: I just want it to do it for this particular mapping.
My source class looks like this:
public class AdvancedSearchFilterDataModel
{
// ....
public string ServiceMeterNumber { get; set; }
// ....
}
My destination class looks like this:
[DataContract]
public class ServicesAdvancedSearchFilterData : AdvancedSearchFilterData
{
// ....
[DataMember]
public string MeterNumber { get; set; }
// ....
}
When I try to map values like this, it works:
Mapper.Configuration.RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>();
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
But I only want "Service" to be recognized as a prefix for certain mappings, since it's also used as a normal part of property names in other mappings. I tried to handle this with a profile, but this didn't work -- no data was mapped:
Mapper.CreateProfile("ServicePrefix").RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>()
.WithProfile("ServicePrefix");
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
How can I make it recognize the prefix only when I want it to, either using profiles or some other technique? (I also have other prefixes that I'm going to need it to recognize for other mappings in the same way.)
I achieved this functionality by creating following structure:
I have Person model for my view which is flattened from PersonCombined
public class PersonCombined
{
public Person Person { get; set; }
public Address DefaultAddress { get; set; }
public Contact EmailContact { get; set; }
public Contact PhoneContact { get; set; }
public Contact WebsiteContact { get; set; }
}
public class Person : IWebServiceModel
{
public int ID { get; set; }
public string PersonFirstName { get; set; }
public string PersonSurname { get; set; }
public string PersonDescription { get; set; }
public Nullable<bool> PersonIsActive { get; set; }
}
Then I have separate class for this mapping only that looks like this:
public class PersonCustomMapping : ICustomMapping
{
const string separator = " ";
private static IMappingEngine _MappingEngine;
public IMappingEngine MappingEngine
{
get
{
if (_MappingEngine == null)
{
var configuration = new ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
configuration.RecognizePrefixes("Person");
configuration.RecognizeDestinationPrefixes("Person");
configuration.CreateMap<Person, MCIACRM.Model.Combine.PersonCombined>();
configuration.CreateMap<MCIACRM.Model.Combine.PersonCombined, Person>();
_MappingEngine = new MappingEngine(configuration);
}
return _MappingEngine;
}
}
}
In my generic view I have mappingEngine property like this:
private IMappingEngine mappingEngine
{
get
{
if (_mappingEngine == null)
{
_mappingEngine = AutoMapper.Mapper.Engine;
}
return _mappingEngine;
}
}
Finally in my generic view constructor i have:
public GenericEntityController(IGenericLogic<S> logic, ICustomMapping customMapping)
: base()
{
this._mappingEngine = customMapping.MappingEngine;
this.logic = logic;
}
And that's how I do mapping:
result = items.Project(mappingEngine).To<R>();
or
logic.Update(mappingEngine.Map<S>(wsItem));
Because I use 1 entity per view I can define custom mapping configuration per entity.
Hope this helps

NHibernate inheritance create child object from parent

I'm sorry if it's a dumb question, but i just can't seem to get a grip.
I got 2 classes, Customer and Member.
Customer.cs
public class Customer
{
public virtual string Firstname { get; set; }
public virtual string Middlename { get; set; }
public virtual string Lastname { get; set; }
}
Member.cs
public class Member : Customer
{
public virtual string MemberId { get; set; }
public virtual string MemberRegistrationDate { get; set; }
public virtual string MembershipStatus { get; set; }
public Member()
{
MemberRegistrationDate = DateTime.Now;
MembershipStatus = MembershipStatusEnum.Active;
}
}
I'm pretty sure this has to be an inheritance, in which Member is a Customer, though if I'm being desperate I can resort to composition.
Note that here I'm using NHibernate that forces me to use all that virtuals.
Given a single customer object, how should a new Member from an existing Customer ?
I can think of 2 options here :
1 - Using Member.cs constructors to recreate it's parents properties
Is this a good thing to do? I tried to do this :
public class Member : Customer
{
public virtual string MemberId { get; set; }
public virtual string MemberRegistrationDate { get; set; }
public virtual string MembershipStatus { get; set; }
public Member(Customer customer)
{
Firstname = customer.Firstname;
Middlename = customer.Middlename;
Lastname = customer.Lastname;
MemberRegistrationDate = DateTime.Now;
MembershipStatus = MembershipStatusEnum.Active;
}
}
But Resharper warns me about accessing virtual member in a constructor, which I do agree to avoid, and tells me to make Member a sealed class which cannot have virtual members (no NHibernate compatibility).
It also raises another issue when someday I added a new property to Customer class, and I forgot to do the same to Member constructor.
2 - Using some sort of reflection helper to map between two objects.
Sure it's a viable option, but I'm currently learning about DDD and I'm wondering if it's okay to put such helper in the domain layer?
Need some suggestions, thanks !
Not sure if I get you right, but there's no need to do anything Customer related in your Member class. You only need to tell NHibernate that Member derives from Customer, and you need to provide the correct mapping for both classes. That's it, the rest goes automatically (that's the whole point of inheritance in OOP, anyway).
Regarding your second issue ('Don't call virtuals in c'tor.'): That's theoretically true, but only relevant if there's a chance that the virtual method gets overwritten in a derived class. So you could safely ignore the R# warning here.
But I think it's cleanest here to get rid of the Member c'tor altogether and declare the class like so:
public class Member : Customer
{
private memberRegistrationDate = DateTime.Now;
private membershipStatus = MembershipStatusEnum.Active;
public virtual string MemberId { get; set; }
public virtual string MemberRegistrationDate
{
get { return this.memberRegistrationDate; };
set { this.memberRegistrationDate = value; };
}
public virtual string MembershipStatus
{
get { return this.membershipStatus; };
set { this.membershipStatus = value; };
}
}
EDIT:
If you're looking for an easy way to turn a customer, into a member, you should maybe entirely keep the conversion code away from your classes and put it into an extension method instead (to keep things clean):
public static class CustomerExtensions
{
public static Member ToMember(this Customer customer)
{
var member = new Member();
member.Firstname = customer.Firstname;
member.Middlename = customer.Middlename;
member.Lastname = customer.Lastname;
return member;
}
}
You can call it then like this:
Member member = customer.ToMember();

Propogate property value in class hierachy

I want to propagate the property from child class to parent class,
ie: If MySchool.ModifiedTime is changed it should change the ModifiedTime in Student Class too, like wise LstBook[0].ModifiedTime is changed it should change MySchool.ModifiedTime as well Student.ModifiedTime... (basically ModifiedTime should be in sync),any Idea
I'm looking for a Generic function in BaseClass to achieve this.
public class MyBaseClass
{
public DateTime ModifiedTime{ get; set; }
}
public class Student: MyBaseClass
{
public string Name { get; set; }
public school MySchool {get;set;}
}
public class School : MyBaseClass
{
public string SchoolName { get; set; }
public List<Book> LstBook {get;set;}
}
public class Book:MyBaseClass
{
public string BookName{get;set;}
}
You could make ModifiedTime virtual and then in each child class override it to perform the syncing.
public class MyBaseClass
{
public virtual DateTime ModifiedTime{ get; set; }
}
public class Student: MyBaseClass
{
public string Name { get; set; }
public school MySchool {get;set;}
public virtual DateTime ModifiedTime
{
get {
return MySchool.ModifiedTime;
}
set {
MySchool.ModifiedTime = value;
}
}
}
And so on.
However, I would reconsider your class hierarchy because it seems like the factoring is incorrect. If all the properties need to be in sync across the entire hierarchy then maybe only one class should have that property and other classes should refere to it. For example, only School should have the ModifiedTime property and when you need to get the modified time for a student you would retrieve it through the MySchool property
You seem to be misunderstanding how object hierarchy works.
Implementing this as a class member only links it to the object created, and a static method would of course mean all objects access the same property.
Instead, as I understand it, you want groups of instances (not all) to share a property.
The simplest way to do this is to create a shared object that provides the modified time for all instances in a group.
As the other commenters have pointed out, you can't do this in a straightforward way with a base class simply because that's not how class hierarchies work. What you could do is create another class called "GroupInfo" or something like that. Make ModifiedTime a property on that.
In all your other classes, add a property for a GroupInfo. Then whenever you create a new book or whatever, as part of the constructor pass in a reference to the GroupInfo for the book.
That way all the objects in the group will share a single GroupInfo, and thus a single ModifiedTime.
You can make ModifiedTime static, which will cause it to be shared among all derived instances of MyBaseClass.
public class MyBaseClass
{
public static DateTime ModifiedTime{ get; set; }
}
Update: More complete example; better explanation of methodology
Your base class could be better described as a interface since your enforcing that each class implement a common property and not making common calculations or sweeping generalizations that could group schools, students, and books together.
Pursuing an event driven solution there are a few things you can do such as using BindingList<T> which is basically List<T> on steroids.
Unfortunatly, you'll need to explode your pretty little { get; set; } properties into full fields, but the best way is for each modification of a property to trigger an event. Each subsequent object that is affected by the modification is subscribed to your modified objects Modified event.
public interface IChangeAware
{
event EventHandler<EventArgs> OnChange;
DateTime ModifiedTime { get; set; }
}
public class Student : IChangeAware
{
public event EventHandler<EventArgs> OnChange;
public DateTime ModifiedTime { get; set; }
public string Name { get; set; }
public School School
{
get { return School; }
set
{
School = value;
if (this.OnChange != null)
this.OnChange(this, new EventArgs());
}
}
public Student()
{
if (School != null)
School.OnChange += MySchoolOnChange;
}
void MySchoolOnChange(object sender, EventArgs e)
{
ModifiedTime = DateTime.Now;
}
}
public class School : IChangeAware
{
public event EventHandler<EventArgs> OnChange;
public DateTime ModifiedTime { get; set; }
public string SchoolName { get; set; }
public BindingList<Book> Books { get; set; }
public School()
{
Books = new BindingList<Book>();
Books.ListChanged += BooksListChanged;
}
void BooksListChanged(object sender, ListChangedEventArgs e)
{
ModifiedTime = DateTime.Now;
OnChange(this, new EventArgs());
}
}
public class Book
{
public string BookName { get; set; }
}

Categories

Resources