Show properties in view from derived classes - c#

I'm trying to get my head around inheritance and polymorphism and run into maybe a simple problem.
I got an abstract base class and two derived classes. I created a viewmodel containing a list with type of the base class, which I pass to the view.
My question is how can I show the properties of the derived classes in the view?
Models
public abstract class Card
{
public string Name { get; set; }
public int Manacost { get; set; }
}
public class Spell : Card
{
public string Description { get; set; }
}
public class Minion: Card
{
public int AttackPower { get; set; }
public int Health { get; set; }
}
Viewmodel passed to view
public class DeckbuilderVM
{
public List<Card> CardLibary { get; set; }
}
View
#model.DeckbuilderVM
#foreach (var item in Model.CardLibary)
{
//Can acces these
<p>Name: #item.Name</p>
<p>Manacost: #item.Manacost</p>
//Can't acces these
<p>Attactpower: #item.AttackPower </p>
<p>Health: #item.Health </p>
//How to acces property of the derived classes?
}

Related

How to access data of a domain class in another model class?

I have a domain class like this,
public class DomainClassExample
{
public int ClientAssessmentId { get; set; }
public int IfaId { get; set; }
}
I want to get the data in this domain class in another model class as a list.
Is there any way by which this can be done?
You can add the model to any other class as a proprty.
public class AnotherModelClassExample
{
//...other properties
public IEnumerable<DomainClassExample> DomainClassExampleList { get; set; }
public AnotherModelClassExample(){
DomainClassExampleList = dbcontext.DomainClassExamples.ToList();
}
}

Cast concrete type to nested generic base type

Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.

What's the best way to include nested objects of derived types in Entity Framework?

I need to include nested objects in an entity. The problem is that also need nested objects that are nested in the nested objects and on top of it these second level nested objects are of different types. I will show an example of something similar to my case, don't worry whether the schema makes sense to you, I'm just trying to give an easier example. So:
public class Garage
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Driver> Drivers { get; set; }
}
public abstract class Vehicle<TDriver, TMechanic>
where TDriver : Driver
where TMechanic : Mechanic
{
public virtual ICollection<TDriver> Drivers { get; set; }
public virtual ICollection<TMechanic> Mechanics { get; set; }
}
public class Car : Vehicle<CarDriver, CarMechanic>
{
// Some properties
}
public class Truck : Vehicle<TruckDriver, TruckMechanic>
{
// Some properties
}
public abstract class Driver
{
public int Id { get; set; }
public string Name { get; set; }
public int GarageId { get; set; }
public virtual Garage Garage { get; set; }
}
public class CarDriver : Driver
{
public int CarId { get; set; }
public virtual Car Car { get; set; }
}
public class TruckDriver : Driver
{
public int TruckId { get; set; }
public virtual Truck Truck { get; set; }
}
public abstract class Mechanic
{
public int Id { get; set; }
}
public class CarMechanic : Mechanic
{
public int CarId { get; set; }
public virtual Car Car { get; set; }
}
public class TruckMechanic : Mechanic
{
public int TruckId { get; set; }
public virtual Truck Truck { get; set; }
}
Let's say that for some reason a Driver can have either a Car or a Truck and that's the reason for this class structure.
So, now, when I create a new Garage entity and create new Drivers in it I provide just the Ids of their Car or Truck entities. After the new Garage entity with its nested Driver entities is created, everything is okay, except for the fact that the Driver entities (CarDriver or TruckDriver) have just their CarId and TruckId fields loaded. The thing is that I need also the real Car and Truck entities. So, how can I include them?
The following line doesn't do the work.
context.Garages.Include(g => g.Drivers)
The only thing that I came up with (and I know that it's the stupidest thing that I can do) is to create a new instance of the DbContext object and get the Garage entity by Id.
Does anybody know how to cope with this situation without doing this stupid workaround?
Edit:
I added Vehicle, Mechanic, CarMechanic and TruckMechanic classes so that my example can be more similar to my real code. Now, from this example it doesn't make a lot of sense for a Car object to be a Vehicle of CarDrivers and CarMechanics. But in my case it's relevant.
Let's say that for some reason a Driver can have either a Car or a
Truck and that's the reason for this class structure.
...
The thing is that I need also the real Car and Truck entities. So, how
can I include them?
You seem to want to be able to access each driver's vehicle in a Polymorphic way. But apparently, you didn't expose such a vehicle on the base Driver class, and since you're accessing your data using Garage.Drivers (which is using that base Driver class), you're ending up with the problem of inaccessible vehicle.
Now, in an ideal world you would simply do:
public abstract class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
}
public class Car : Vehicle { public string CarSpecificProperty { get; set; } }
public class Truck : Vehicle { public string TruckSpecificProperty { get; set; } }
public abstract class Driver<TVehicle> where TVehicle : Vehicle
{
public virtual TVehicle Vehicle { get; set; }
}
public class CarDriver : Driver<Car> {}
public class TruckDriver : Driver<Truck> {}
The problem is that Entity Framework will refuse to map open generic Entities and you won't be able to create the appropriate DbSet<Driver<?>> in your context.
That will require us to come up with a less-cleaner approach:
public abstract class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
}
public class Car : Vehicle { public string CarSpecificProperty { get; set; } }
public class Truck : Vehicle { public string TruckSpecificProperty { get; set; } }
public abstract class Driver
{
public int Id { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class CarDriver : Driver
{
public Car Car
{
get { return this.Vehicle as Car; }
set { this.Vehicle = value as Car; }
}
}
public class TruckDriver : Driver
{
public Truck Truck
{
get { return this.Vehicle as Truck; }
set { this.Vehicle = value as Truck; }
}
}
And in your DbContext:
public IDbSet<Driver> Drivers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CarDriver>().Ignore(x => x.Car);
modelBuilder.Entity<TruckDriver>().Ignore(x => x.Truck);
base.OnModelCreating(modelBuilder);
}
And the actual usage would be:
var driversWithVehicles = context.Drivers.Where(x => x.Vehicle != null);
var driversWithToyotaVehicles = context.Drivers.Where(x => x.Vehicle.Make == "Toyota");
var carDrivers = context.Drivers.OfType<CarDriver>();
var carDriversWithCriteria = context.Drivers.OfType<CarDriver>().Where(x => (x.Vehicle as Car).CarSpecificProperty == "SomeValue");
var truckDrivers = context.Drivers.OfType<TruckDriver>();
var truckDriversWithCriteria = context.Drivers.OfType<TruckDriver>().Where(x => (x.Vehicle as Truck).TruckSpecificProperty == "SomeValue");
(Notice that we don't access Car/Truck specific data using the .Car/.Truck property, since they are ignored on-purpose and EF will throw when trying to access them)
Now, to address the last piece which is your original Garage with Include problem:
context.Garages.Include(g => g.Drivers.Select(d => d.Vehicle));
Or
context.Garages.Include("Drivers.Vehicle");
Of course, you can now also query those Drivers data directly:
var allGaragesWithCars = context.Garages.Where(g => g.Drivers.OfType<CarDriver>().Any());

Retrieving values from Model C# MVC

I have a C# MVC Model as follows;
public class MyModel
{
...
public IEnumerable<MyModel> allDetails { get; set; }
public int age { set; get; }
public string gender { set; get; }
public int schoolid { set; get; }
...
}
Now want to retrieve data from allDetails. How can i do it ?
In the controller;
model.allDetails = MyDetails.getAllDetails(); // Saves all details in the Model
Now how can i retrieve age, gender and school from this model; My approach as follows (but its doesn't work)
model.allDetails.age; // This doesn't work
In your code model.allDetails is a list (or smth else that implements IEnumerable), so you have to use foreach or another loop, or just First() to get first value from it: model.allDetails.First().age
I think you got your desing for class MyModel wrong, You should probably have two classes like:
public class MyClass
{
...
public IEnumerable<MyModel> allDetails { get; set; }
public int age { set; get; }
public string gender { set; get; }
public int schoolid { set; get; }
...
}
public class MyModel
{
...
public IEnumerable<MyClass> allDetails { get; set; }
...
}
Where your MyModel class will contain an IEnumerable of your other class which contains properties. Later you can fill your model through controller.
In your current MyModel class, you are keeping IEnumerable of the class itself, so each object for that class will have another IEnumerable and so on.
You may also see: C# Coding Conventions (C# Programming Guide) for your properties names.

How do I organize C# classes that inherit from one another, but also have properties that inherit from one another?

I have an application that has a concept of a Venue, a place where events happen. A Venue has many VenueParts. So, it looks like this:
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
A Venue can be a GolfCourseVenue, which is a Venue that has a Slope and a specific kind of VenuePart called a HoleVenuePart:
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public virtual ICollection<HoleVenuePart> Holes { get; set; }
}
In the future, there may also be other kinds of Venues that all inherit from Venue. They might add their own fields, and will always have VenueParts of their own specific type.
Here are the VenuePart classes:
public abstract class VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get { return "Hole"; } }
public int Yardage { get; set; }
}
My declarations above seem wrong, because now I have a GolfCourseVenue with two collections, when really it should just have the one. I can't override it, because the type is different, right? When I run reports, I would like to refer to the classes generically, where I just spit out Venues and VenueParts. But, when I render forms and such, I would like to be specific.
I have a lot of relationships like this and am wondering what I am doing wrong. For example, I have an Order that has OrderItems, but also specific kinds of Orders that have specific kinds of OrderItems.
Update: I should note that these classes are Entity Framework Code-First entities. I was hoping this wouldn't matter, but I guess it might. I need to structure the classes in a way that Code-First can properly create tables. It doesn't look like Code-First can handle generics. Sorry this implementation detail is getting in the way of an elegant solution :/
Update 2: Someone linked to a search that pointed at Covariance and Contravariance, which seemed to be a way to constrain lists within subtypes to be of a given subtype themselves. That seems really promising, but the person deleted their answer! Does anyone have any information on how I may leverage these concepts?
Update 3: Removed the navigation properties that were in child objects, because it was confusing people and not helping to describe the problem.
Here's one possible option using generics:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
}
Here GolfCourseVenue has the collection VenueParts, which can contain HoleVenueParts or super classes HoleVenueParts. Other specializations of Venue would restrict VenueParts to containing VenueParts specific to that venue.
A second possibility is pretty much as you had it
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
}
Now GolfCourseVenue has the collection VenueParts, which can contain VenueParts or super classes VenueParts. Here all specializations of Venue can contain any type of VenuePart which may or may not be appropriate.
In answer to your comment about covariance, I would propose something like this:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get{return "I'm a hole venue"; } }
}
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public abstract ICollection<VenuePart> VenueParts { get; }
}
public class GolfCourseVenue : Venue
{
private ICollection<HoleVenuePart> _holeVenueParts;
public GolfCourseVenue(ICollection<HoleVenuePart> parts)
{
_holeVenueParts = parts;
}
public override ICollection<VenuePart> VenueParts
{
get
{
// Here we need to prevent clients adding
// new VenuePart to the VenueParts collection.
// They have to use Add(HoleVenuePart part).
// Unfortunately only interfaces are covariant not types.
return new ReadOnlyCollection<VenuePart>(
_holeVenueParts.OfType<VenuePart>().ToList());
}
}
public void Add(HoleVenuePart part) { _holeVenueParts.Add(part); }
}
I look forward to the advice of others - but my approach is to use generics in this case. With generics, your GolfCourseVenue's "parts" are strong typed!
...and as I type this everyone else is saying generics too. HOW DO YOU overstackers type so dang fast?!
Anyways, pretending I'm still first -
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public abstract class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
}
Also, as a 2nd option, you could use an interface too, so in case you didn't like the name Parts, you could call it Holes when the derived type is known to be a GolfCourse
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public interface IPartCollection<T> where T : VenuePart
{
ICollection<T> Parts { get; set; }
}
public abstract class Venue<T> : IPartCollection<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
ICollection<HoleVenuePart> IPartCollection<HoleVenuePart>.Parts { get { return base.Parts; } set { base.Parts = value; }}
public virtual ICollection<HoleVenuePart> Holes { get { return base.Parts; } set { base.Parts = value;}}
}
You can use Covariance
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual IEnumerable<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public GolfCourseVenue()
{
List<HoleVenuePart> HoleVenueParts = new List<HoleVenuePart>();
HoleVenueParts.Add(new HoleVenuePart());
VenueParts = HoleVenueParts;
}
}
Assuming HoleVenuePart is inherited from VenuePart
If you remove "set" portions of both collections than it will make more sense: base class provides "all parts" collection, while derived classes have filtered view in addition to base class one.
Note: Depending on your needs making GolfVenue to be specialization generic of Venue<VenuePart> may not work as Venue<Type1> and Venue<Type2> will not have any good base class to work with.
Consider using interfaces instead of base classes as it would allow more flexibility in implementation.
public interface IVenue
{
public int Id { get; }
public string Name { get; }
public virtual IEnumerabe<VenuePart> VenueParts { get; }
}
public interface IGolfCourse : IVenue
{
public virtual IEnumerabe<HoleVenuePart> Holes { get; }
}
Now you can use GolfCourse:Venue from other samples but since it implements interface you can handle it in gnereic way too:
class GolfCourse:Venue<HoleVenuePart>, IGolfCourse {
public virtual IEnumerabe<VenuePart> Holes{ get
{
return VenueParts.OfType<HoleVenuePart>();
}
}
}
class OtherPlace:Venue<VenuePart>, IVenue {...}
List<IVenue> = new List<IVenue> { new GolfCourse(), new OtherPlace() };
Nothe that GolfCourse and OtherPlace don't have common parent class (except object), so without interface you can't use them interchangebly.

Categories