How can I write a class that is both inheritable and generic? - c#

I have several classes in my project that are mapped to table rows in a database.
Most of the classes share a lot of the boiler plate code to fetch data from database FetchAll, and to return certain object/row based on a given id FetchById().
I am thinking of writing a single class that can perform FetchAll and FetchById() and then all of my other classes can inherit from this object. However, I am struggling to define the base class to be generic. Other classes can't inherit of it and at the same time be of the generic type associated with what they are inheriting from.
To better explain my question, I will start simplifying two of my common classes that share similar code:
Person.cs
class Person {
public int Id { get; set; }
public string Name { get; set; }
public static List<Person> All {
get { /* fetch all rows and */ return List<Person>; }
}
public static Person FetchById(int id) {
return All.Where(p => p.Id == id).SingleOrDefault();
}
}
Department.cs
class Department {
public int Id { get; set; }
public string Name { get; set; }
public string CostCenter { get; set; }
public static List<Department> All { get { /* same logic as with Person */ } }
public static Department FetchById(int id) { /* same logic as with Person */ }
}
Now here is what I tried to do to reduce code duplication:
IIdentifiable.cs
public interface IIdentifiable {
// make sure all objects have an Id property
int Id { get; set; }
}
DbObject.cs
class DbObject<T> where T: IIdentifiable {
public static List<T> All { get { /* return List<T>; */ } }
public static T FetchById(int id) {
return All.Where(object => object.Id == id).FirstOrDefault();
}
}
Now I went back to my Person and Department classes to take advantage of DbObject:
Person.cs (modified)
class Person : DbObject<Person>, IIdentifiable // won't compile: The type cannot be used as type parameter T
{
public int Id { get; set; }
public string Name { get; set; }
// public static List<Person> All {get{ ... }}; // should be inherited
// public static Person FetchById(int id) { ... } // should be inherited
}
I did the same with Department. You get the idea.
I feel I am doing something wrong. I know it should be much simpler than this.
How can I make DbObject generic and inheritable at the same time?

You can do this by adopting the Curiously recurring template pattern.
Effectively you define DbObject<T> such that T : DbObject<T> then it works.
public class DbObject<T> where T : DbObject<T>, IIdentifiable
{
public List<T> All { get { return new List<T>(); } }
public T FetchById(int id)
{
return All.Where(x => x.Id == id).FirstOrDefault();
}
}
public class Person : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}
Now this code works:
Person p = new Person();
List<Person> all = p.All;
In your original code you were using static methods. I assume that is so you could write List<Person> all = Person.All;, but you can't inherit static methods.
You might want to consider making a repository object that is responsible for returning the people.
Do keep in mind that the downside to this approach is that you need to be careful. This code compiles, but would probably not be what you want:
public class Department : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}

You can use recursive generics and static methods.
public abstract class Identified<T> where T : Identified<T>{
public static List<T> GetAll(){...}
}
public class Person:Identified<Person>{...}
var allPeople = Person.GetAll();
You shouldn't need to redefine GetAll in each subtype. But I would recommend that FetchById performs a separate query. Do you really want to load the entire table to view one record?
But I would actually recommend a separate generic factory class. It's difficult to configure and test static methods.

Related

How to use interface default implementation in C#

I have classes which are inherits from base class.
public class Car : BaseEntity
{
public string Name { get; private set; }
public Car(string name)
{
Name = name;
// update method goes here
}
}
public abstract class BaseEntity
{
public Guid Id { get; } = Guid.NewGuid();
}
I want to have an interface or something else where I can audit this entity changes. I made an interface and added default implementation like below :
public interface IAuditable
{
// userNameProvider goes here
DateTime CreatedTime { get; set; }
Guid CreatedBy { get; set; }
void Audit()
{
CreatedTime = DateTime.UtcNow;
// CreatedBy = userNameProvider.GetUserId();
}
}
But the issue is that I need to cast my Car into this interface in order to call Audit method. As it's not forced to implement in the Car class, I guess I can just forget it or something else. I want a solution which could be reusable for all entities deriving from this interface just by calling one method and not to be afraid to forget Audit method. I'd highly appreciate your suggestions. Thanks
Example code what is working right now but need to simplify :
var car = new Car("bmw");
Console.WriteLine(car.CreatedTime);
public class Car : BaseEntity, IAuditable
{
public string Name { get; private set; }
public DateTime CreatedTime { get; set; }
public Car(string name)
{
Name = name;
(this as IAuditable).Audit();
// update method goes here
}
}
I mentioned above what I did and my expectation, please take a look :)
You can override your DbContext SaveChangesAsync method
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
foreach (var e in ChangeTracker.Entries()
.Where(i => i.State == EntityState.Added
|| i.State == EntityState.Modified)
.Select(i => i.Entity)
.OfType<IAuditable>())
{
e.Audit();
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
This will ensure that all IAuditable entities modified through your DB Context will have their Audit method called.
You cannot use default interface methods directly if you don't implement them in the concrete class. However, you can add an extension method to make calling the default interface method easier:
public static class AuditableExtensions
{
public static void Audit(this IAuditable auditable)
{
auditable.Audit();
}
}
// usage:
Car car = new Car();
car.Audit();
// or
public class Car : BaseEntity, IAuditable
{
public Car()
{
this.Audit();
}
}
You can use an external processing class like this:
public class Car : BaseEntity, IAuditable
{
public string Name { get; private set; }
public Car(string name)
{
Name = name;
Auditor.Audit(this);
}
}
public static class Auditor
{
public static void Audit(IAuditable entity)
{
entity.Audit();
}
}

Refactoring classes into multi-layered generic classes in C#

I have a problem with C# generics, and I'm not sure about the most elegant solution. I've been programming a while but am new to the C# ecosystem so don't know common terminology for searching.
I'm trying to refactor code to reduce existing copy-paste duplication of classes. It is easy to resolve with one level of generics, but I can't get it working with two.
A very simplified example is below. The core issue is that BaseProfile cannot use any implementation details relating to DetailsA or DetailsB as it does not know the type. So UpdateDetailsId() has to be duplicated in 2 derived classes, instead of having a single Profile class handle it. Keep in mind this is a toy example just to express the relationships. The real classes have tens of fields, but a common subset which we are using in the class in question, so even if DetailsA and DetailsB look identical assume we need both.
public abstract class BaseProfile<TypeOfPerson>
{
public TypeOfPerson Person { get; set; }
}
public class Profile1 : BaseProfile<PersonA>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class Profile2 : BaseProfile<PersonB>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class PersonA
{
public DetailsA Details { get; set; }
}
public class PersonB
{
public DetailsB Details { get; set; }
}
public class DetailsA
{
public int Id { get; set; }
}
public class DetailsB
{
public int Id { get; set; }
}
I can add interfaces as it is referring to all the same fields for each type. However, C# will not allow an interface to include another interface and automatically resolve it in the implementation, because the member has to exactly match i.e. I thought I could just add IDetails Details to the IPerson interface but the fields now need to be type IDetails instead of DetailsA which implements IDetails. If I do that then I lose compiler type safety and can put the wrong Details on the wrong Person.
I have had success doing a public/private field pair like below, but this only validates and throws at runtime when casting value to DetailsA. I'd prefer something safer but I don't know if this is the best option. The goal of this example is a single Profile class, handling multiple Person classes, each with their own Details type that has an int Id field.
public class PersonA : IPerson
{
public IDetails Details
{
get { return _details; }
set { _details = (DetailsA)value; }
}
private DetailsA _details { get; set; }
}
One way of achieving this is by defining the type relationship between PersonA to DetailsA in a generic way, and specify a second generic type on BaseProfile.
Profile1 : BaseProfile<PersonA, DetailsA>
Consider the following code (note that I'm using Net6, so I have all these nullable reference type operators):
public abstract class BaseProfile<TPerson, TDetails>
where TDetails : IDetails, new()
where TPerson : PersonDetails<TDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
}
public class Profile1 : BaseProfile<PersonA, DetailsA>
{
}
public class Profile2 : BaseProfile<PersonB, DetailsB>
{
}
public abstract class PersonDetails<TDetails>
where TDetails : IDetails, new()
{
public virtual TDetails? Details { get; set; } = new TDetails();
}
public class PersonA : PersonDetails<DetailsA>
{
}
public class PersonB : PersonDetails<DetailsB>
{
}
public interface IDetails
{
int Id { get; set; }
}
public class DetailsA : IDetails
{
public int Id { get; set; }
public string? FirstName { get; set; }
}
public class DetailsB : IDetails
{
public int Id { get; set; }
public string? LastName { get; set; }
}
Testing with the following snippet
var profile1 = new Profile1();
var profile2 = new Profile2();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine();
Update:
Because you included explicit casting in your snippet for Details property getters and setter, I also want to show a pattern using a concrete type inheriting on these generic types -- then demonstrate implicit/explicit operator user-defined conversion patterns.
Add the following declarations:
public abstract class BaseProfile<TPerson>
where TPerson : PersonDetails<GenericDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
public static explicit operator Profile1(BaseProfile<TPerson> details)
{
var profile = new Profile1();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
public static explicit operator Profile2(BaseProfile<TPerson> details)
{
var profile = new Profile2();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
}
public class GenericProfile : BaseProfile<GenericPerson>
{
}
public abstract class GenericPersonDetails : PersonDetails<GenericDetails>
{
}
public class GenericPerson : GenericPersonDetails
{
}
public class GenericDetails : IDetails
{
public int Id { get; set; }
public static implicit operator DetailsA(GenericDetails details)
{
return new DetailsA() { Id = details.Id };
}
public static implicit operator DetailsB(GenericDetails details)
{
return new DetailsB() { Id = details.Id };
}
}
and, update the testing functional scope:
var profile1 = new Profile1();
var profile2 = new Profile2();
var genericProfile = new GenericProfile();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
genericProfile.UpdateDetailsId(20);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile1.Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine(genericProfile.Person!.Details!.Id);
Console.WriteLine(((Profile1)genericProfile).Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(((Profile2)genericProfile).Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine();

Creating one helper method for different types via interface c#

I have a requirement to order several lists by the same value. But, for whatever reason, these lists contain objects of different types which share this value. Let's call it ChildID.
The simplified model code would look something like this:
public class Child
{
public string ChildID { get; set; }
}
public class Parent
{
public Child Child { get; set; }
}
public class OtherClassID
{
public int ID { get; set; }
public string ChildID { get; set; }
}
public class SomeOtherClass
{
public OtherClassID ID { get; set; }
}
So, in order to avoid code duplication, I tried this:
public interface IHasChildID
{
string GetChildID();
}
public class Child : IHasChildID
{
public string ChildID { get; set; }
public string GetChildID()
{
return ChildID;
}
}
public class Parent : IHasChildID
{
public Child Child { get; set; }
public string GetChildID()
{
return Child.ChildID;
}
}
public class OtherClassID
{
public int ID { get; set; }
public string ChildID { get; set; }
}
public class SomeOtherClass : IHasChildID
{
public OtherClassID ID { get; set; }
public string GetChildID()
{
return ID.ChildID;
}
}
And when I created a helper class with a helper method which takes an interface as a parameter, I expected it to work:
public static class ChildOrderHelper
{
public static IEnumerable<IHasChildID> OrderChildren(IEnumerable<IHasChildID> children)
{
var childrenList = children.ToList();
//do some splitting, ordering and conatenation of lists
return orderedList;
}
}
But, on every helper call I get an error:
List<Child> originalList = GetChildren(); // whatever
// some lines of code
var orderedList = ChildOrderHelper.OrderChildren(originalList).ToList(); // error
Error CS1503 Argument 1: cannot convert from
'System.Collections.Generic.List<NamespaceOne.Child>' to
'System.Collections.Generic.List<NamespaceTwo.IHasChildID>'
And so for every helper call, no matter the type.
One thing to note is that I've given an example with three distinct types that have this value and need to be ordered by it. In the project, there is probably 10 or more.
I guess there is something fundamental I don't yet understand about interface usage, but any help would be appreciated on this matter.
I'm not entirely sure what your overall use case is, but maybe it would be beneficial to make the OrderChildren method generic, as follows:
public static class ChildOrderHelper
{
public static IEnumerable<T> OrderChildren<T>(IEnumerable<T> children) where T : IHasChildID
{
var childrenList = children.ToList();
//just a simple example of what I'm guessing the method could do...
return childrenList.OrderBy(c => c.GetChildID()).ToList();
}
}
And call it as follows:
List<Child> originalList = GetChildren();
List<Child> orderedList = ChildOrderHelper.OrderChildren<Child>(originalList).ToList();
The approach can be taken like defining an interface and then implemenint that one in all the required classes or a base class that can lookup the child id.
Below is a sample of the source code.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
public class Program
{
public static void Main()
{
var parents = new List<Parent>();
parents.Add(new Parent{ChildId = "123"});
parents.Add(new Parent{ChildId = "321"});
parents.Add(new Parent{ChildId = "456"});
var result = ChildHelpers.OrderChildren(parents);
foreach(var res in result) {
Console.WriteLine(res.ChildId);
}
Console.WriteLine("Hello World");
}
}
public interface IChild {
string ChildId {get;set;}
}
public class Child : IChild {
public string Name {get;set;}
public string ChildId {get;set;}
}
public class Parent : IChild {
public Parent() {
child = new Child();
}
public Child child {get;set;}
public string ChildId {
get{
return child.ChildId;
}
set{
child.ChildId = value;
}
}
}
public class AnotherChild : IChild {
public string Description{get;set;}
public string ChildId {get;set;}
}
public static class ChildHelpers {
public static IEnumerable<IChild> OrderChildren(IEnumerable<IChild> children)
{
return children.OrderBy(c=>c.ChildId).AsEnumerable();
}
}
If you would like to playaround with this sample and see other options if required, please refer this link.

Generic Attributes in MVC models

I created the GenericAttribute.cs file in my Models
public class GenericAttributes<T>
{
public T Id { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
}
Now I want to add 'int id' field in my User Model
public class User
{
//here I want to add 'Id' field
public string UserId { get; set; }
public string password { get; set; }
public string UserType { get; set; }
public int EmployeeId { get; set; }
public virtual Employee employee { get; set; }
}
How should I do this? Please help
You can make GenericAttributes an interface so you can implement it where ever.
Such as;
public interface IGenericAttributes<T>
{
//properties
}
And use in your class declaration;
public class User : IGenericAttributes<int>
{
//properties
}
This will force your concrete type User to implement the properties of the interface.
You are getting some conflicting answers due to your naming convention. Any class of the form xxxAttribute is expected to be a subclass of the Attribute class. Attributes are metadata that you can attach to classes, fields, etc. Using reflection you can read these attributes, which is a powerful way to inform various APIs about how to interact with your custom classes - without inheritance or an interface.
If this sort of metadata is your intent, then Barr J's answer is correct. However, if your intent is for the GenericAttributes class to serve as a base class that you can inherit these properties from, then Tom Johnson is correct (although he did change GenericAttributes into an interface instead of a base class, but same result if all you have are properties like this). The latter is most likely what you are looking for.
I would suggest renaming GenericAttributes to something more descriptive, like BaseRecord or IRecord (as an interface), since User looks like data coming from or going to a database.
It would also be handy to have a non-generic version of the class/interface so that you can non-generically reference such records.
public class BaseRecord {
public Type IdType { get; }
private Object _id = null;
public Object Id {
get {
return _id;
}
set {
if(value != null) {
if(!IdType.IsAssignableFrom(value.GetType()))
throw new Exception("IdType mismatch");
}
_id = value;
}
}
public bool IsActive { get; set; }
public DateTime CreatedTime { get; set; }
public BaseRecord(Type idType)
{
if(idType == null) throw new ArgumentNullException("idType");
this.IdType = idType;
}
}
namespace Generic {
public class BaseRecord<T> : BaseRecord
{
new public T Id {
get { return (T)base.Id; }
set { base.Id = value; }
}
public BaseRecord() : base(typeof(T))
{
}
}
}
public class User : Generic.BaseRecord<int>
{}
public class OtherRecord : Generic.BaseRecord<string>
{}
// This inheritence scheme gives you the flexibility to non-generically reference record objects
// which can't be done if you only have generic base classes
BaseRecord r = new User();
r = new OtherRecord();
BaseRecord records[] = { new User(), new OtherRecord() };
To access the id for GenericAttributes class, you'll have to cast User object as base class type.
namespace SampleApp
{
class SampleProgram
{
static void Main(string[] args)
{
User User = new User() { Id = 1 };
var genericAttribute = (User as GenericAttributes<int>);
genericAttribute.Id = 2;
var genericAttributeId = genericAttribute.Id;
var classId = User.Id;
}
}
public class GenericAttributes<T>
{
public T Id { get; set; }
}
public class User : GenericAttributes<int>
{
public new int Id { get; set; }
}
}

How to make two similar functions into one function?

I have two functions that do the basically same thing on two different classes.... each class has different properties.
For example:
public class ClassA
{
public int ColorID {get;set;}
public string ColorDescription {get;set;}
}
public class ClassB
{
public int TypeID {get;set;}
public string TypeDescription {get;set;}
}
public void ExFunctionSaveA(ClassA aClass)
{
aClass.ColorID=1;
aClass.ColorDescription="My Color";
Save();
}
public void ExFunctionSaveB(ClassB bClass)
{
bClass.TypeID=2;
bClass.TypeDescription="My Type";
Save();
}
As you can see the classes and the functions have the same type structure, just the property names are different... but I feel like I am repeating code doing this
Is there a way to make ExFunctionA and ExFunctionB into one function, so that I could use this for all classes that have similar structure
I know I could do some sort of generic thing like
public void ExFunctionSave<T>() // T is either ClassA or ClassB
{
.
.
.
.
Save();
}
but how would I handle the properties of each
Rather than using a generic, why not use inheritance to solve this?
public class theBase
{
string ID;
string Description;
}
public class theColor : theBase
{
}
public class theType : theBase
{
}
public void ExFunctionSaveA(theBase base)
{
base.ID=1;
base.Description="My Color";
Save();
}
If you can alter the definitions of your classes, then the best approach would be to make them implement a common interface that contains the properties you want to access:
public interface IDescribable
{
int ID { get; set; }
string Description { get; set; }
}
public class ClassA
{
public int ID { get; set; }
public string Description { get; set; }
public int ColorID
{
get { return ID; }
set { ID = value; }
}
public string ColorDescription
{
get { return Description; }
set { Description = value; }
}
}
public class ClassB
{
public int ID { get; set; }
public string Description { get; set; }
public int TypeID
{
get { return ID; }
set { ID = value; }
}
public string TypeDescription
{
get { return Description; }
set { Description = value; }
}
}
public void ExFunctionSave(IDescribable d, int id, string desc)
{
d.ID = id;
d.Description = desc;
Save();
}
Nothing more you can do unless the the 2 classes implement the same interface which has the function. In your case, even the function signatures are different.
You could define an Interface with attributes id and description.
The clases that has this structure could implement that interface.
And your method receive as parameter the interface and execute the moethods ...
Take a look at Reflection.
Reflection will let your code receive a ClassA, and discover that it has a ColourID and a ColorDescription. Likewise, when you receive a ClassB, you can discover its TypeID and TypeDescription. It's cool.
I would probably recommend a common interface, at least for your example, but if you're trying to something more complex and more generic, Reflection is the way to go.

Categories