Obtain DB Data in Polymorphism - c#

I have base class, and multiple derived classes, like that:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Parent : Person
{
public int Age { get; set; }
}
public class Child : Person
{
public int Grade { get; set; }
public Parent Father { get; set; }
public Parent Mother { get; set; }
}
Each class has a table in the DB, with an appropirate field.
When I want to obtain data from the DB, and initalize an Person's instance, I need to set value for each property, like that:
Name = reader["Name"].ToString();
Age = int.Parse(reader["Age"].ToString());
Now, I don't want to do that on each derived class, but do that generally in the base class.
What is the best way to do that? Thank you.

First of all, the best answer you can find here is: adopt an OR/M like Entity Framework, NHibernate or Dapper (or any other) and focus your efforts in your business instead of wasting your time in infrastructure details!
In the other hand, if you want an approach to solve your issue using your own code, I would say you need to create the concept of data mapper. It would be a class which should define mapping between relational and object-oriented counterparts.
This way, when you try to obtain a derived class, you instantiate base class and derived class data mappers, and you call them to obtain the entire column data:
DerivedClass derivedClassInstance = new DerivedClass();
DerivedClassDataMapper derivedMapper = new DerivedClassDataMapper(derivedClassInstance, dataReader);
derivedMapper.Fetch();
// Now your derivedClassInstance reference will contain an object which
// has both data from base and derived class...
Finally, I would say that both data mappers should inherit from a DataMapper base class where Fetch method should be marked as virtual (or abstract) in order to let derived data mappers add more mapping code (this way, base mapper will map properties to columns defined in the base class, derived class will override Fetch to add more mappings and so on...).
Anyway, you shouldn't reinvent the wheel because there're solid object-relational mappers.

Related

How can I cast IEnumerable<DerivedClass> to IEnumerable<BaseClass> if it is a property of another class, in C# [duplicate]

As you know, C# 9.0 (.Net 5) now allows Covariant Returns. I need help applying this to a set of classes having Auto-Implemented properties.
I have two abstract classes that represent financial bank accounts and transactions. I made them abstract since I will pull data from various data sources and while the main properties will be common across all sources, each source may have additional fields I want to keep. A 1 to Many relationship exists between both classes (1 account has many transactions AND 1 transaction belongs to only 1 account).
public abstract class BankAccount
{
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; } = new List<Transaction>();
...
}
public abstract class Transaction
{
public string Name { get; set; }
public virtual BankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
And here is an example of the concrete implementations
public class PlaidBankAccount : BankAccount
{
public string PlaidId { get; set; }
...
}
public class PlaidTransaction : Transaction
{
public string PlaidId { get; set; }
public override PlaidBankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
What I want to do is to override the base class getters and setters so that they use derived classes. For example:
If I create an instance of the concrete transaction and call the BankAccount getter, I want to get an instance of the derived PlaidBankAccount not the base BankAccount.
What I've found is that when I only define virtual getter in the base class and override it in the derived class, it works. But just as I add both properties {get;set;}, I get the same error as in previous C# versions:
error CS1715: 'PlaidTransaction.BankAccount': type must be 'BankAccount' to match overridden member 'Transaction.BankAccount'
How could I fix this?
In C# 9 properties are only able to have co-variant returns when they are readonly, so unfortunately, no set; is possible.
An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property. Beginning with C# 9.0, read-only overriding properties support covariant return types. The overridden property must be virtual, abstract, or override.
From the Microsoft Docs - Override keyword

Cannot access public properties in derived class

I have an abstract class which is inherited by another class as following:
public abstract class Employee
{
public string name{ get; set; }
public string age { get; set; }
}
public class OtherEmployee : Employee
{
public OtherEmployee()
{
}
public string specialField{ get; set; }
}
This is what I am doing and somehow, not getting it done:
Employee otherEmployee= new OtherEmployee();
otherEmployee.specialField = "somevalue";
I am not provided with the access to specialField, whereas all the attributes of Employee are accessible. I know it is a trivial problem, but I have hit a roadblock here..
Employee doesn't know about specialField, only OtherEmployee does. You need to cast it to the right class to use specialField.
Employee otherEmployee = new OtherEmployee();
This doesn't give you an OtherEmployee but an Employee. To be able to use specialField do something like this:
(otherEmployee as OtherEmployee).specialField = yourValue;
You will not have access to specialField because the object is created with the base class reference. It will not have access to the Child class properties.
Though the object is instantiated with a child Class, the reference still points to base class. So the properties/skeleton will be of base class and it would not have memory allocated for child class structure.
You have to write in this way beacause you daeclare otheremployee variable as Employee Type, so it doesn't know about special field. So:
(otherEmployee as OtherEmployee).specialField = "somevalue";

Can C# constraints be used without a base type?

I have some classes with common properties, however, I cannot make them derive from a base type (LINQ-to-SQL limitations).
I would like to treat them as if they had a base type, but not by using Reflection (performance is critical).
For example:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
}
public class Vehicle
{
public int Id { get; set; }
public string Label { get; set; }
}
In this case I would be happy if I had the Id property available, regardless of the type I'm holding.
Is there any way in C# to to something similar to this:
public static int GetId<T>(T entity) where T // has an int property 'Id'
{
return entity.Id;
}
I guess I could have used dynamic, however, I'm looking for a way to restrict the code in compile time from using this method for an object that has no Id property.
You can use interfaces:
public interface IHasId
{
int Id { get; }
}
public class User : IHasId { ... }
public class Vehicle : IHasId { ... }
public static int GetId<T>(T entity) where T : IHasId
{
return entity.Id;
}
However, if you are not able to modify the classes to add the interface, you won't be able to do this. No compile-time checks will verify that a property exists on T. You'd have to use reflection - which is slow and obviously not ideal.
There is no way to guarantee a type has a given member without constraining to a common base type or interface. One way to work around this limitation is to use a lambda to access the value
public static int Use<T>(T value, Func<T, int> getIdFunc) {
int id = getIdFunc(value);
...
}
Use(new User(), u => u.Id);
Use(new Vehicle(), v => v.Id);
You can create an interface with the common properties and make your classes implement it:
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
}
public class Vehicle : IEntity
{
public int Id { get; set; }
public string Label { get; set; }
}
public static int GetId<T>(T entity) where T : IEntity
{
return entity.Id;
}
You could simplify GetId like this:
public static int GetId(IEntity entity)
{
return entity.Id;
}
The other answers mentioning the interface approach are certainly good, but I want to tailor the response to your situation involving Linq-to-SQL.
But first, to address the question title as asked
Can C# constraints be used without a base type?
Generally, the answer is no. Specifically, you can use struct, class, or new() as constraints, and those are not technically base types, and they do give some guidance on how the type can be used. That doesn't quite rise to the level of what you wish to do, which is to limit a method to types that have a certain property. For that, you will need to constrain to a specific interface or base class.
For your specific use case, you mention Linq-to-SQL. If you are working from models that are generated for you, then you should have options to modify those classes without modifying the generated model class files directly.
You probably have something like
// code generated by tool
// Customer.cs
public partial class Customer // : EntityBaseClasses, interfaces, etc
{
public int ID
{
get { /* implementation */ }
set { /* implementation */ }
}
}
And other similar files for things such as Accounts or Orders or things of that nature. If you are writing code that wishes to take advantage of the commonly available ID property, you can take utilize the partial in the partial class to define a second class file to introduce a common interface type to these models.
public interface IIdentifiableEntity
{
int ID { get; }
}
And the beauty here is that using it is easy, because the implementation already exists in your generated models. You just have to declare it, and you can declare it in another file.
public partial class Customer : IIdentifiableEntity { }
public partial class Account : IIdentifiableEntity { }
// etc.
This approach has proven valuable for me when using a repository pattern, and wishing to define a general GetById method without having to repeat the same boilerplate in repository after repository. I can constrain the method/class to the interface, and get GetById for "free."
Either you need to make both classes implement an interface with the properties you need, and use that in the generic constraint, or you write separate methods for each type. That's the only way you'll get compile-time safety.

implementing similar classes (data entity objects)

I am working on an application which currently creates data entity objects from the results of a sql query. In the database 3 of the tables are very similar but have several different properties.
My initial plan was to create 3 different classes, even though each class is very similar. However when I came to create the method which returns a list of objects, I have hit a stumbling block as the return type will be different depending on which mode the application is in.
e.g.
public class A
{
public int Id {get;}
public string Name {get;}
}
public class B
{
public int Id {get;}
public string Name {get;}
public string ExtraInfo {get;}
}
public class MainScreen
{
...
this.resultsGrid.DataSource = LoadData();
}
I would prefer not to write one method to load a list of each data type.
What should the return type of LoadData() be, to allow it to be generic as possible.
What is the most elegant way of dealing with this scenario?
Thanks,
Sean
You should have inheritance to allow polymorphism, so you would have a base class that all entities included in the data binding derive from it.
Then, you can have a mid-base class to have some shared properties like Name and ID.
Base class:
public abstract class Entity
{
}
Entity with Name and ID:
public class NameAndIDEntity : Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
Entity with Name, ID and ExtraInfo:
public class NameIDAndExtraEntity : NameAndIDEntity
{
public string ExtraInfo { get; set; }
}
Entity with other information (can't be derived from NameAndIDEntity), derives from Entity so it can be included in the data binding:
public class OtherInformationEntity : Entity
{
public int Age { get; set; }
}
Finally, you can make the LoadData return type Entity.
Simples!
Create a class ListItem (with properties Id and Name, I presume). In your factory class/method, make instances of that class from the records into a List and bind the datasource to the list.
Don't be scared to create specialised classes for your UI.
UPDATE: Forgot to mention. Avoid inheritance as much as possible.
First you can create an inheitance tree in your project, where base class holds a shared/common properties among set of dfferent types
Second you can retrieve from the query anonymous type and after map it to a known type by mapping them to a real type, like from Jon Skeet's blog Horrible grotty hack: returning an anonymous type instance
That means that you need by the way know which query what type returns (can not avoid that), but this can reduce amount of fraction you need to add to your code, like from example:
static class GrottyHacks
{
internal static T Cast<T>(object target, T example) //CAST TO SPECIFIED TYPE
{
return (T) target;
}
}
class CheesecakeFactory
{
static object CreateCheesecake()
{
return new { Fruit="Strawberry", Topping="Chocolate" };
}
static void Main()
{
object weaklyTyped = CreateCheesecake(); //ANONYMOUS TYPE GENERATION
var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
new { Fruit="", Topping="" }); //"MAPPING"
Console.WriteLine("Cheesecake: {0} ({1})",
stronglyTyped.Fruit, stronglyTyped.Topping);
}
}

EF 4.0 generics based inheritance

I have a class like this
public abstract class BaseType<T>
{
public string Name {};
public T TypedValue {
get {
return GetTypedValue(PersistedValue);
}
};
public string PersistedValue {}
public abstract T GetTypedValue(PersistedValue);
}
then many derived classes like
public class IntegerType:BaseType<int>
{
...
}
is it possible to map this hierarchy using EF 4.0 using Table per inheritance scheme ?
Currently the generated code creates has an error because it generates a property like
public <T> ObjectSet<TypedAttribute<T>> TypedAttributes
{
get
{
return _typedAttributes ?? (_typedAttributes = CreateObjectSet<TypedAttribute<T>>("TypedAttributes")); }
}
private ObjectSet<TypedAttribute> _typedAttributes;
I don't think so because:
Inheritance mapping requires the base class to be entity in EDMX.
When inheritance is used the ObjectSet is for base type. What generic argument would you use to create an instance of ObjectSet when it has to be used to retrieve any subtype?
It can be partially achieved without inheritance (at least for POCOs). Simply model your subtypes in EDMX without base type. Then manually create POCO classes and derive them from generic base types. The only rule you have to follow is that POCO class must have the same name as entity in EDMX and it must have all its properties with accessibility set in EDMX. If you want to use change tracking properties must be marked as virtual. If you want to use lazy loading navigation properties must be virtual as well.
Example:
Suppose that I have two entities in EDMX: IntegerValue and DoubleValue. Now I defined these POCOs as follows:
public abstract class BaseType<T>
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual T Value { get; set; }
}
public class IntegerValue : BaseType<int>
{ }
public class DoubleValue : BaseType<double>
{ }
It will result in single table per sub type.

Categories

Resources