This is probably my naivety showing through, but anyway...
I have a generic interface which defines a set of standard methods (implemented differently) across implementations.
I pass the interface into a method as a parameter, this method being responsible for persisting to a database. E.g. I have some implementations called bug, incident, etc, defined from the generic interface (called IEntry). These concerete implementations also make use of IEnumerable
Because a bug is different to an incident, there are different fields. When I pass the interface into a method as a parameter, is there any way to inference the type? So if I pass in the Bug object, I can use its fields, which are not the same fields as in those of Incident. These fields are useful for the persistance to the database. I'm assuming no because there is no way to know what the type to be passed in will be (obviously), but I know people here have more wisdom. In that case, is there a better way of doing things? Because of the similarity, I would like to stick to interfaces.
EDIT: I guess the other way is to make use of some flow control to generate the sql statement on the fly and then pass it in as a parameter.
Thanks
The thing about passing objects and interfaces around is that you really shouldn't be concerned with the actual type, as long as it inherits from/implements the particular base class/interface you're interested in.
So building logic into that method to figure out that it's a bug, and then accessing things that are only present for bugs, that's basically not the OOP way, although it might be the "best" way in your particular case.
I would, however, advise against it, and instead try to build a proper OOP way with polymorphism to handle the differences, instead of building it into the method as special cases.
You mention persistence, is this method responsible for storing the data somewhere? Perhaps you could separate the part that gathers the information to store from the part that stores the information, that way you could ask the object itself to provide you with all the pertinent information, which could vary from one class to another.
Bad Design (as I think was described in the question):
public interface IEntry
{
string Description { get; set; }
}
public class Bug : IEntry
{
public int ID { get; set; }
public string Description { get; set; }
public string UserName { get; set; }
}
public class Incident : IEntry
{
public Guid ID { get; set; }
public string Description { get; set; }
}
public class Persister
{
public void Save(IEnumerable<IEntry> values)
{
foreach (IEntry value in values) { Save(value); }
}
public void Save(IEntry value)
{
if (value is Bug) { /* Bug save logic */ }
else if (value is Incident) { /* Incident save logic */ }
}
}
Improved design (smart entity approach):
public interface IEntry
{
string Description { get; set; }
void Save(IPersister gateway);
}
public class Bug : IEntry
{
public int ID { get; set; }
public string Description { get; set; }
public string UserName { get; set; }
public void Save(IPersister gateway)
{
gateway.SaveBug(this);
}
}
public class Incident : IEntry
{
public Guid ID { get; set; }
public string Description { get; set; }
public void Save(IPersister gateway)
{
gateway.SaveIncident(this);
}
}
public interface IPersister
{
void SaveBug(Bug value);
void SaveIncident(Incident value);
}
public class Persister : IPersister
{
public void Save(IEnumerable<IEntry> values)
{
foreach (IEntry value in values) { Save(value); }
}
public void Save(IEntry value)
{
value.Save(this);
}
public void SaveBug(Bug value)
{
// Bug save logic
}
public void SaveIncident(Incident value)
{
// Incident save logic
}
}
The improved design is only caters for the need to shift the need for change of Persister.Save(IEntry). I just wanted to demonstrate a first step to make the code less brittle. In reality and production code you would want to have a BugPersister and IncidentPersister class in order to conform to the Single Responsibility principle.
Hope this more code-centric example is a help.
The persistance thing is just a method in a class to upload details to a database.
I guess I could write an abstract class with a function for the persistance requirement and that could be based on parameters for it to work. I can use this in each of my interface implementations. Because the way the update to db will happen (pretty much the same but a few words in a sql query change), I can generate this based on method parameters.
Related
I am thinking about the best practice in OOP for the following problem:
We have a program that is working with an external API.
The API has an object of type Element which is basically a geometric element.
Our application is a validation application that runs on a geometric model
The application takes a collection of those elements and performs some geometric tests on them.
We wrap this API element with our own class called "ValidationElement" and save some additional information to this wrapper element that can not be obtained directly from the API Element but is required by our application.
So far so good, but now the application should expand and support other types of models (basically we can say that the app is running in a different environment).
Specifically for this environment (and it does not apply to the previous cases), we want to save an additional parameter that obtaining it results in low performance.
What is the best practice option to implement it?
On one hand, I would like to avoid adding extra parameters that are not relevant to a specific(the first) part of the program.
And on the second hand, I am not sure that I want to use inheritance and split this object just for this small additional property.
public class ValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
}
The first and easy option is that the same class will have the additional property and calculation method:
public class ValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
The second option that I mentioned is the inheritance
public class SecondTypeValidationElement : ValidationElement
{
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
What do you think is the best practice for this? Is there any other way or design pattern that should help me achieve the goal?
I would like to avoid adding extra parameters that are not relevant to a specific(the first) part of the program.
It looks like it is a sign that an inheritance shoulbe be avoided here. As there is a strong possibility that this behaviour is not applicable for other classes.
And this is the second reason to avoid of creation some abstraction:
Element which is basically a geometric element
Because:
all derived elements will have these additional properties.
there are many articles which show how Liskov substitution principle can be violated in geometry figures
So let's prefer composition over inheritance.
So, in my view, it would be really good if we move all heavy, tightly coupled logic of calculating of additional property to separate class:
public class ValidationElement
{
public string Element { get; set; }
public SomeExtra AdditionalProperty { get; set; }
}
public class SomeExtra
{
public string Location { get; set; }//The extra property
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = string.Empty;
}
}
Why have we created separate class SomeExtra and put logic here:
if we want to edit logic HardProcessingCalcOfAdditionalProperty, then we will edit just one class SomeExtra. By doing this we are satisfying Single Responsibility Principle of SOLID principles.
we can easily create some base abstract class for SomeExtra and then at runtime we can decide what concrete implementation should be injected. By doing this we are satisfying Open Closed Principle of SOLID principles.
UPDATE:
I really like this answer about whether inheritance or composition should be chosen:
My acid test for the above is:
Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is
expected? Indicates Inheritance.
e.g. A Cessna biplane will expose the complete interface of an airplane, if not more. So that makes it fit to derive from Airplane.
Does TypeB want only some/part of the behavior exposed by TypeA? Indicates need for Composition.
e.g. A Bird may need only the fly behavior of an Airplane. In this case, it makes sense to extract it out as an interface / class /
both and make it a member of both classes.
Update: Just came back to my answer and it seems now that it is incomplete without a specific mention of Barbara Liskov's Liskov
Substitution Principle as a test for 'Should I be inheriting from
this type?'
OOP and SOLID best practice is to use abstractions (interfaces or abstract classes), wich is closer to your second approach.
Dependency Inversion Principle:
The Dependency Inversion principle
states that our classes should depend upon interfaces or abstract
classes instead of concrete classes and functions.
Your first approach to edit the ValidationElement class is generally a bad idea, given that there are several environments for the project to be run onto.
In addition, maintaining and developing the project on this approach is not scalable and will be a headache in the long run.
Open-Closed Principle: The Open-Closed Principle requires that classes should be open for extension and closed to modification.
I suggest below designing:
public interface IValidationElement
{
Element Element { get; set; }
XYZ Location {get; set;}//The extra property
// declare other base properties and methods
}
public class ValidationElement: IValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
// define other base properties and methods
}
public interface ISecondTypeValidationElement: IValidationElement
{
string AdditionalProperty { get; set; }
void HardProcessingCalcOfAdditionalProperty();
}
public class SecondTypeValidationElement: ISecondTypeValidationElement
{
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
public interface IThirdEnvironmentValidationElement: IValidationElement
{
string ThirdProperty { get; set; }
void RequiredProcessing();
}
public class ThirdEnvironmentValidationElement: IThirdEnvironmentValidationElement
{
public string ThirdProperty { get; set; }
public void RequiredProcessing()
{
//related operations
}
}
I am not going to repeat Open-close, DI, or other principals. It is already discussed. I would look at something like this, or even alternatively use Extensions to setup the value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// old updated
public class Element
{
public Element(string msg) { Message = msg; }
public string Message;
}
public class XYZ { }
public class ABC { }
// new
public interface IDoesSomething
{
void SetResult();
}
// create 2 different wrappers
public class ValidationElementWrapper : IDoesSomething
{
public ValidationElementWrapper(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public void SetResult()
{
Console.WriteLine("This is " + Element.Message);
// Do nothing
}
}
public class ValidationElementWrapper2 : IDoesSomething
{
public ValidationElementWrapper2(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public string AdditionalProperty { get; set; }
public void SetResult()
{
AdditionalProperty = "Set additional property on wrapper 2";
Console.WriteLine("This is " + Element.Message + " and it has additional property - " + AdditionalProperty);
}
}
// run your program
public class Program
{
public static void Main()
{
var list = new List<IDoesSomething>();
list.Add(new ValidationElementWrapper(new Element("Element 1")));
list.Add(new ValidationElementWrapper2(new Element("Element 2")));
list.ForEach(item => item.SetResult());
}
}
Output
This is Element 1
This is Element 2 and it has additional property - Set additional property on wrapper 2
Alternatively, you can start with more basic class and then keep extending it
public class ValidationElementWrapper : IDoesSomething
{
public ValidationElementWrapper(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public virtual void SetResult() // <--- virtual
{
// Do nothing
Console.WriteLine("This is " + Element.Message);
}
}
public class ValidationElementWrapper2 : ValidationElementWrapper // <-- inheritnce
{
public ValidationElementWrapper2(Element el) : base(el)
{
}
public XYZ Location {get; set;}
public string AdditionalProperty { get; set; }
public override void SetResult() // <--- override
{
AdditionalProperty = "Set additional property on wrapper 2";
Console.WriteLine("This is " + Element.Message + " and it has additional property - " + AdditionalProperty);
}
}
Result will be the same
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.
I'm trying to get my head around a polymorphism/inheritance situation in C#.
What I have right now is these classes:
Lease (the base class containing the general data)
PrivateLease (inheriting from the Lease class)
BusinessLease (inheriting from the Lease class)
What I want to achieve is this:
Lease lease = new PrivateLease();
This works at the moment, but I am not able to access the properties on the PrivateLease object when doing this. At least not without casting the Lease object to a PrivateLease object first.
I'd like the Lease object to be the general object of either a PrivateLease or BusinessLease object which holds all the data for one of the objects. Then when inserting/updating/deleting to the database I'm going to ask which type it is first to dertermine which tables to insert the data into.
I've got a strange feeling that the above is not the right approach to solve this problem. Does anyone have any hints on this? :-) I've searched on google and read in my programming books and everyone suggests this approach of having a base class and then inherit from it to the other classes.
Any help/hint is greatly appreciated!
Thanks in advance.
EDIT
Should've elaborated a bit on this from the beginning, I'm sorry for that!
The above mentioned classes are merely just holding data from the UI of my ASP.NET solution to perform CRUD operations against the database via a Data Access Layer. So bascially these classes only contains a bunch of properties to hold data. I.e:
public class Lease
{
public int Id { get; set; }
public bool IsActive { get; set; }
public string TypeOfRental { get; set; }
public string RentalPeriod { get; set; }
public DateTime TakeoverDate { get; set; }
}
public class PrivateLease : Lease
{
public string Floor { get; set; }
public string Side { get; set; }
public int FloorSize { get; set; }
public int NumberOfRooms { get; set; }
}
etc..
The PrivateLease and BusinessLease classes are different because of the different leaseing-variables that exists in the real world :-)
Basically I could just go with the two separate PrivateLease and BusinessLease objects, but since the model dictates that an Address object can hold one or more Leases, this is not an option.
To me it seems like I'm going to go through a major casting hell both on the ASP.NET frontend and on the DAL? :-/
Don't decide (choose a logic) on the layer of consumer, but let to decide by the classes themselves:
// or you ILease interface if a parent class will not contain any shared logic
abstract class Lease
{
public abstract void Do();
// example of shared logic
protected void Save(Lease l) { }
}
class PrivateLease : Lease
{
public override void Do() { // private logic here }
}
class BusinessLease : Lease
{
public override void Do() { // business logic here }
}
Usage:
Lease l = ...
l.Do(); // execute the logic
You may want to create a factory for objects creation:
static class LeaseFactory<T> where T : Lease, new() // constraint to require default constructor existence
{
public static Leas Create()
{
return new T();
}
}
You're right in the basic approach of having a base class.
What you need to do is to put any common properties in the base class. Then if you have different business rules, those can be implemented with virtual functions, being called polymorphically.
abstract class Lease
{
public int MonthlyCost {get;set;}
public string CustomerName {get;set;}
// Declare that all Leases have to have an IncreaseCost method.
public abstract void IncreaseCost();
}
class PrivateLease : Lease
{
// Private leases are incremented by an absolute number (10).
public override void IncreaseCost()
{
MonthlyCost += 10;
}
}
class BusinessLease : Lease
{
// Business leases are incremented by 10%.
public override void IncreaseCost()
{
MonthlyCost *= 1.10;
}
}
// Somewhere in your code...
Lease lease = new PrivateLease();
// This call is polymorphic. It will use the actual type of the lease object.
lease.IncreaseCost();
In the modern OOD you can use interfaces, for this situation.
Edit:
In my opinion, to avoid casting, you can have multiple interfaces for multiple purposes. then PrivateLease and BusinessLease can implement the appropriate ones.
interface IWrite
{
string Data { get; set; }
void Write();
}
interface IRead
{
string Data { get; set; }
void Read();
}
public class Lease
{
//..
}
public class PrivateLease : Lease, IWrite, IRead
{
// other implementations
public string Data { get; set; }
public void Read()
{
//..
}
public void Write()
{
//..
}
}
public class BusinessLease : Lease, IRead
{
// other implementations
public string Data { get; set; }
public void Read()
{
//..
}
}
In Lease class add virtual method called DBUpdate and override it in both the derived classes.
Let's say some Utility class has LeaseDBOperation Method looks like this :
public static void LeaseDBOperation (Lease anylease)
{
anyleaase.DBUpdate();
}
you can call this method as :
var pl = new PrivateLease();
..set all the properties of **pl**
//call this for db operations :
Utility.LeaseDBOperation(pl)
Here in LeaseDBOperation method , if based on the type send , DBUpdate method of required class will be called.
Lease l = (Lease)sth;
if (l is PrivateLease)
{
PrivateLease p = (PrivateLease)l;
//do private logic here
}
else if (l if BussinessLease)
{
BussinessLease b = (BunessinessLease)l;
//do bussiness logic here
}
I have a class Voucher:
public abstract class Voucher
{
public int Id { get; set; }
public decimal Value { get; protected set; }
public const string SuccessMessage = "Applied";
}
and a subclass GiftVoucher
public class GiftVoucher : Voucher
{
}
and another subclass DiscountVoucher
public class DiscountVoucher : Voucher
{
public decimal Threshold { get; private set; }
public string FailureMessage { get { return "Please spend £{0} to use this discount"; } }
}
You can see that DiscountVoucher has a couple of specific properties Threshold and FailureMessage that respectively represent the amount of money you need to spend to get the discount and the failure message to display if the user has not spent that money.
My question is this. I have a collection of Voucher objects and what I don't want to do in my code is something like this
if (voucher is DiscountVoucher)
{
// cast voucher to a DiscountVoucher and then call the specific methods on it
}
because this is not at all maintainable. At the same time I did not want to put those specific methods in the Voucher abstract class because they are not applicable to all types of Vouchers. Does anyone know how to design this functionality?
In the general case: No!
Handling specialized scenarios in a general code flow without any code handling the special cases does not work.
However in some cases you can cheat a little bit. You can implement virtual methods in the abstract base class that provides a default "nothing" implementation.
Could be a method that returns null, 0 or just does nothing.
In this case
public virtual string FailureMessage { get { return string.Empty; } }
might be a reasonable implementation.
I guess that your implementation looks a lot like the template method pattern. Then it is perfectly normal to have void implementations for steps not applicable to certain implementations.
Well what you've got here is a version of the strategy pattern. I don't think there's any getting away from eventually having to decide if you have one type of voucher or another but you can limit the number of variations - voucher categories if you will - using interfaces.
For instance you might end up with five vouchers which implement interfaces called 'StandardVoucher' and three called 'DiscountVoucher' but instead of having to handle eight cases you now just have two.
The interfaces can cover a range of vouchers showing the available methods without worrying about the details of each vouchers implementation.
No, you cannot, because iterating over more general objects and then calling specific methods would require using polymorphism to have dedicated functionality in each subclass. Without a method in the superclass to override, you have no way to obtain what you want.
I think you're right to be suspicious of the code you describe.
My first thought is that if members of DiscountVoucher aren't broad enough to exist as virtual or abstract in Voucher, then a function that takes a Voucher as a parameter should not touch them.
So, to solve the problem, I'd say you could do one of two things:
First, you could add virtual methods or properties to Voucher, e.g.
public abstract class Voucher
{
public int Id { get; set; }
public decimal Value { get; protected set; }
public const string SuccessMessage = "Applied";
public decimal Threshold { get { return 0.0; } }
public string FailureMessage { get { return ""; } }
}
Second, you can add methods that do what you expect for each Voucher. You've grouped them together as vouchers, so think about what they have in common. If, say, GiftVoucher and DiscountVoucher are both doing their own calculations to determine if they apply to the current ShoppingCart, then you could have a Voucher method called isValid() to detect this. For example,
public abstract class Voucher
{
public bool isValid(ShoppingCart sc);
public string FailureMessage { get { return "This voucher does not apply"; } }
// ...
}
public class DiscountVoucher : Voucher
{
private decimal Threshold;
public override bool isValid(ShoppingCart sc)
{
return (sc.total >= Threshold);
}
public override string FailureMessage
{
get { return FormatString("Please spend £{0} to use this discount", Threshold); }
}
There are just cases where you will have to cast. Here I would implement a general error checking mechanism:
public abstract class Voucher
{
public int Id { get; set; }
public decimal Value { get; protected set; }
public virtual string SuccessMessage { get { return "Applied"; } }
public virtual string FailureMessage { get { return String.Empty; } }
public virtual bool Ok { get { return true; } }
}
public class GiftVoucher : Voucher { }
public class DiscountVoucher : Voucher
{
public decimal Threshold { get; private set; }
public override string FailureMessage { get { return "Please spend £{0} to use this discount"; } }
public override bool Ok { get { return Value >= Threshold; } }
}
You can then test the integrity of a voucher of any type without casting:
if (voucher.Ok) {
Console.WriteLine(voucher.SuccessMessage);
} else {
Console.WriteLine(voucher.FailureMessage);
}
As a general rule, try to let objects do their own stuff (here to test if they are OK) instead of doing it from the "outside". Even the fact, that no error can occur in a GiftVoucher needs not to be known by the "outer world".
I have a scenario where I have a bunch of jobs that I am scheduling to run at various times, the jobs themselves are being handled generically already which is great. And I have an abstract BaseJob class that they all inherit from that I use for common things (like the jobPK, startTime, exception logging, reporting, etc). But beyond that the jobs are very different, they have different properties and data associated with them that is entriely specific to them (I call these proprties JobDetails). So for example:
JobDetails for Job1
-customerId int
-cost double
-sku string
-userDefinedProperties SomeCustomObjectType
JobDetails for Job2
-name string
-executionDate DateTime
-otherProperties SomeOtherCustomObjectType
In the base class I would like to be able to store a reference to these JobDetails in as generic a fashion as possible (so in other words I don't want to just store it as object) to minimize the overhead for boxing/unboxing. Then I want to have the BaseJob class handle a lot of the common functionality that is needed for the app, so for example, if a job fails, I want to save its JobDetails to the database so that it can be restarted, I also want to log any errors that may have occured to a given job. For this I need to be able to extract those JobDetails and make use of them.
It seems like I need to make use of .NET generics and have a class of generic properties that I can stuff anything into and not have to worry about typing. What's the best way to handle this and make it efficient and flexible?
I hope that is clear, thanks for the help
You can make the JobDetails implement an interface and let have BaseJob have an abstract reference to it. Then in the actual jobs you implement the abstract JobDetails with the implementation you want. Then let the JobDetails interface define the methods BaseJob needs to work with. This is a slight variation on the Template Method design pattern. It would look something like this:
public interface IJobDetails {
void DoSomeWork();
}
public abstract BaseJob {
protected abstract IJobDetails JobDetails { get; set; }
public ExecuteJob {
//execute the job
JobDetails.DoSomeWork();
}
}
public Job1 : BaseJob {
public Job1() {
JobDetails = new ConcreteJobDetails();
}
protected override IJobDetails JobDetails { get; set; }
}
How about something like...
public abstract class JobBase<TDetails>
{
private TDetails details;
protected TDetails Details
{
get
{
if (details == null)
{
this.details = this.LoadDetails();
}
return this.details;
}
}
protected virtual TDetails LoadDetails()
{
// Some kind of deserialization of TDetails from your DB storage.
}
}
public class ExampleJob : JobBase<ExampleJob.ExampleJobDetails>
{
public class ExampleJobDetails
{
public string ExampleProperty { get; set; }
public int AnotherProperty { get; set; }
}
}
You'd either want to have tables for each type used as TDetails or one big Key/Value based table for all of them. There are pros/cons to both. If you are super paranoid about boxing, there's no reason why TDetails can't be constrained to be a struct, too.
Edit: Got it backwards, you want to save the details on a failure. How about...
public abstract class JobBase<TDetails>
{
protected TDetails Details { get; private set; }
public JobBase()
{
this.Details = this.CreateDetails();
}
protected abstract TDetails CreateDetails();
protected void SaveDetails()
{
// Generic save to database.
}
}
public class ExampleJob : JobBase<ExampleJob.ExampleJobDetails>
{
public class ExampleJobDetails
{
public string ExampleProperty { get; set; }
public int AnotherProperty { get; set; }
}
protected override ExampleJobDetails CreateDetails()
{
return new ExampleJobDetails() { ExampleProperty = "Hi.", AnotherProperty = 1 };
}
}