chain of responsibility and generics - c#

I have a chain of responsibility that applies filters to a collection. I am trying to make a factory to build that chain of responsibility from a configuration. My concrete types for the chain arent generic but their abstraction are, and the genericity makes me struggle to put them in a collection for a mapping between config and correct chain node implementation.
Here is the implementation of the chain :
public interface IFilter<T> where T : IFilterable
{
IFilter<T> SetNext(IFilter<T> next);
IEnumerable<T> Filter(IEnumerable<T> data);
}
public class BaseFilter<T> : IFilter<T> where T : IFilterable
{
protected IFilter<T> Next { get; set; }
public IFilter<T> SetNext(IFilter<T> next)
{
Next = next;
return Next;
}
public virtual IEnumerable<T> Filter(IEnumerable<T> data)
{
return Next == null ? data : Next.Filter(data);
}
}
Here is an example of concrete implementation of the nodes of the chain :
public interface IFilterable {}
public interface ICanFly: IFilterable
{
bool CanFly { get; }
}
public interface ITransport : IFilterable
{
int Passengers { get; }
}
public class Duck : ICanFly
{
public bool CanFly => true;
}
public class Plane : ICanFly, ITransport
{
public bool CanFly => true;
public int Passengers => 5;
}
public class FlyerFilter : BaseFilter<ICanFly>
{
public override IEnumerable<ICanFly> Filter(IEnumerable<ICanFly> data)
{
return base.Filter(data.Where(x => x.CanFly));
}
}
public class SmallTransportFilter : BaseFilter<ITransport>
{
public override IEnumerable<ITransport> Filter(IEnumerable<ITransport> data)
{
return base.Filter(data.Where(x => x.Passengers < 8));
}
}
My problems start when I want to make a factory that map the configuration to my concrete types (FlyerFilter and SmallTransportFilter in my example)
public interface IFilterChainBuilder<T> where T : IFilterable
{
IFilter<T> GenerateFilterResponsabilityChain(IEnumerable<string> filtersParam);
}
public class FilterChainBuilder<T> : IFilterChainBuilder<T> where T : IFilterable
{
private readonly Dictionary<string, IFilter<T>> _paramToFiltersMap;
public FilterChainBuilder()
{
_paramToFiltersMap = new Dictionary<string, IFilter<T>>(StringComparer.OrdinalIgnoreCase)
{
{"Flyers", new FlyerFilter()}, // Compile error, cannot convert from FlyerFilter to IFilter<T>
{"SmallTransport", new SmallTransportFilter()} // Compile error, cannot convert from SmallTransportFilter to IFilter<T>
};
}
public IFilter<T> GenerateFilterResponsabilityChain(IEnumerable<string> filtersParam)
{
IFilter<T> filterResponsabilityChain = null;
foreach (var parameter in filtersParam)
if (_paramToFiltersMap.TryGetValue(parameter, out var filter))
{
if (filterResponsabilityChain == null)
filterResponsabilityChain = filter;
else
filterResponsabilityChain.SetNext(filter);
}
else
{
throw new ArgumentException(
$"config parameter {parameter} has no associated IFilter");
}
return filterResponsabilityChain ?? new BaseFilter<T>();
}
}
I can understand why it doesnt compile. Since FlyerFilter is a BaseFilter<ICanFly> (so a IFilter<ICanFly>), it would be bad if I declared a new FilterChainBuilder<PlaceholderType>. And actually since SmallTransportFilter inherit from a different T type, the only possible IFilterable implementation would have to implement both ITransport and ICanFly.
I tried to remove the generic T type entirely but the consummer of this chain of responsability relies on that IEnumerable<T> Filter(IEnumerable<T> data) signature and wants an enumeration of concrete types rather than IFilterable.
I am not sure how could I fix this problem, I am currently stuck here.

Pavel is correct - Your definition of IFilter makes the type parameter T invariant. Putting covariance/controvariance/invariance aside, the design itself is questionable. For example, FlyFilter works only against ICanFly instances, but there is no code filters the input down to ICanFly elements only - shouldn't that be the responsibility of FlyFilter as well? I would personally suggest you use type info in your filters directly, maybe something like below:
public interface IFilterable { }
public class CanFly : IFilterable { }
public class Duck : CanFly { }
public abstract class Transportation : CanFly
{
public abstract int Passengers { get; }
}
public class Plane : Transportation
{
public override int Passengers => 5;
}
public class FlyerFilter : BaseFilter<IFilterable>
{
public override IEnumerable<IFilterable> Filter(IEnumerable<IFilterable> data)
{
return base.Filter(data.Where(x => x is CanFly));
}
}
public class SmallTransportFilter : BaseFilter<IFilterable>
{
public override IEnumerable<IFilterable> Filter(IEnumerable<IFilterable> data)
{
return base.Filter(data.Where(x => x is Transportation t && t.Passengers < 8));
}
}

Related

Chain of transformations based on generic types

I'm thinking about a chain, that will allow me to perform a set of transformations with data type changing from transformation to transformation. So far I've got something like this:
public abstract class TransformationStep<T, TY>
{
public abstract TY Execute(T input);
}
public class Times2<TY> : TransformationStep<int, TY>
{
public Times2(TransformationStep<int, TY> next)
{
Next = next;
}
public TransformationStep<int, TY> Next { get; set; }
public override TY Execute(int input)
{
var res = input * 2;
return Next.Execute(res);
}
}
public class ToString<TY> : TransformationStep<int, TY>
{
public ToString(TransformationStep<string, TY> next)
{
Next = next;
}
public TransformationStep<string, TY> Next { get; }
public override TY Execute(int input)
{
var res = input + "!!!";
return Next.Execute(res);
}
}
The only problem I see is the end chain type, where I can't convert T to TY.
public class End<T, TY> : TransformationStep<T, TY>
{
public override TY Execute(T input)
{
return input;
}
}
Do you have any solution? Also what do you think about this design and do you know any good materials on stuff like that?
It looks like you want transformations:
public abstract class TransformationStep<TIn, TOutput>
{
public abstract TOutput Execute(TIn input);
}
And if you want to just return input type, then it is possible to create another method with return type:
public abstract class TransformationStep<TIn, TOutput>
{
public abstract TOutput Execute(TIn input);
public abstract TIn ExecuteWithoutTransformation(TIn input);
}
Dataflow
If you want to connect chains of data into pipeline or graph, then you can use TransformBlock.
What is about Chain of Responsibility pattern?
As wiki says about "Chain of Responsibility pattern":
In object-oriented design, the chain-of-responsibility pattern is a
behavioral design pattern consisting of a source of command objects
and a series of processing objects.2 Each processing object contains
logic that defines the types of command objects that it can handle;
the rest are passed to the next processing object in the chain. A
mechanism also exists for adding new processing objects to the end of
this chain.
Your code looks similar, however, code and goal of chain responsibility pattern is slightly different. It does not make transformations, it gives object to the next processing object in the chain.
So one of the variations of code of chain of the responsibility pattern can look like this:
An abstraction of desired behaviour of chain of the responsibility pattern:
public abstract class MyHandler<T>
{
private MyHandler<T> Next { get; set; }
public virtual void Handle(T request)
{
Next?.Handle(request);
}
public MyHandler<T> SetNext(MyHandler<T> next)
{
Next = next;
return Next;
}
}
And let us imagine that we are publishing house and we want that each property of article should be validated.
So, concrete implemetations of handling article can look like this:
public class OnlyNewArticleValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (document.DateCreated.Year < DateTime.Now.Year)
{
throw new Exception("Only new articles should be published.");
}
base.Handle(document);
}
}
public class AuthorValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (string.IsNullOrWhiteSpace(document.Author))
{
throw new Exception("Author is required.");
}
base.Handle(document);
}
}
public class NameRequiredValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (string.IsNullOrWhiteSpace(document.Name))
{
throw new Exception("Name is required.");
}
base.Handle(document);
}
}
And ArticleProcessor would look like this:
public class MyChainAticleProcessor
{
public void Validate(Document document)
{
var handler = new NameRequiredValidationHandler();
handler.SetNext(new AuthorValidationHandler())
.SetNext(new OnlyNewArticleValidationHandler());
handler.Handle(document);
}
}
And it can be run like this:
new MyChainAticleProcessor().Validate(
new Document { Author = "Author 1", Name="Name 1" }
);

Implementing overload methods for subtypes of an abstract class in C#

I'm new to C#, and I would really like to implement specific different methods for each subtype of a defined abstract class, but I am having trouble figuring out how to get the compiler to do this properly. For example:
public abstract class MasterClass { }
public class SubClass1 : MasterClass { }
public class SubClass2 : MasterClass { }
public class SeparateClass
{
public void HandleMasterClass(MasterClass item)
{
/*
stuff generic to both subclasses...
*/
SpecificMethod(item)
}
public void SpecificMethod(SubClass1 item)
{
//something specific to SubClass1
}
public void SpecificMethod(SubClass2 item)
{
//something specific to SubClass2
}
}
This returns an error in compiling because there is no SpecificMethod(MasterClass item), but what I really want is for it to choose the right method based on the subclass without having to write separate HandleMasterClass(SubClass1 item) and HandleMasterClass(SubClass2 item) methods because they are mostly the same code
my main language is Jula so I'm very used to relying on multiple dispatch and doing this kind of thing. I know its probably not idiomatic in C#, so how would I do this better?
EDIT: showing that the methods are not free but part of a separate class
here's a better concrete example
public abstract class MasterClass { public abstract int Stuff(); }
public class SubClass1 : MasterClass
{
public override int Stuff() { /*calculate and return an int*/ }
}
public class SubClass2 : MasterClass
{
public override int Stuff() { /*calculate and return an int*/ }
}
public class MasterClassDictionary
{
public Dictionary<int, SubClass1> subClass1Dict{get;} = new Dictionary<int, SubClass1>()
public Dictionary<int, SubClass2> subClass2Dict{get;} = new Dictionary<int, SubClass2>()
public void Add(MasterClass item)
{
int val = item.Stuff();
AddToDict(val, item);
}
void AddToDict(int val, SubClass1 item) { subClass1Dict[val] = item; }
void AddToDict(int val, SubClass2 item) { subClass2Dict[val] = item; }
}
I know this is a bit of a contrived example, but its similar to what I'm trying to do.
Generally, you want to put code specific to a class inside that class. So your abstract class would define the specific method signature, using the abstract keyword, and the implementation would live inside the class, using the override keyword, like this:
public abstract class MasterClass {
public abstract void SpecificMethod();
}
public class SubClass1 : MasterClass {
public override void SpecificMethod()
{
//something specific to SubClass1
// use the this keyword to access the instance
}
}
public class SubClass2 : MasterClass {
public override void SpecificMethod()
{
//something specific to SubClass2
// use the this keyword to access the instance
}
}
public class SeparateClass
{
public void HandleMasterClass(MasterClass item)
{
/*
stuff generic to both subclasses...
*/
item.SpecificMethod()
}
}
Per your comment, this is how I might implement the thing in your concrete example, though it may not meet your requirements:
public class MasterClassDictionary
{
public Dictionary<int, SubClass1> subClass1Dict{get;} = new Dictionary<int, SubClass1>()
public Dictionary<int, SubClass2> subClass2Dict{get;} = new Dictionary<int, SubClass2>()
public void Add(MasterClass item)
{
int val = item.Stuff();
if (item is SubClass1)
{
subClass1Dict[val] = item;
}
if (item is SubClass2)
{
subClass2Dict[val] = item;
}
}
}
The standard design pattern for this situation is the Visitor pattern. This is a somewhat complicated pattern, but the basic idea is that the subclasses know what type they are so we are going to call over to them via an virtual method called "Accept" and they will pass themselves back as a reference. The method they call back is called Visit and is overloaded for all the possible subclasses. Here is an implementation for your example:
public abstract class MasterClass
{
public abstract int Stuff();
// New method that all subclasses will have to implement.
// You could also have this be virtual with an implementation
// for Visit(MasterClass) to provider a default behavior.
public abstract void Accept(IVisitor visitor);
}
public class SubClass1 : MasterClass
{
public override int Stuff() => 0;
// We must override this even though its the "same" code in both subclasses
// because 'this' is a reference to a different type.
public override void Accept(IVisitor visitor) => visitor.Visit(this);
}
public class SubClass2 : MasterClass
{
public override int Stuff() => 1;
// We must override this even though its the "same" code in both subclasses
// because 'this' is a reference to a different type.
public override void Accept(IVisitor visitor) => visitor.Visit(this);
}
public interface IVisitor
{
// Need an overload for all subclasses.
void Visit(SubClass1 item);
void Visit(SubClass2 item);
}
public class MasterClassDictionary
{
public Dictionary<SubClass1, int> subClass1Dict { get; } = new Dictionary<SubClass1, int>();
public Dictionary<SubClass2, int> subClass2Dict { get; } = new Dictionary<SubClass2, int>();
public void Add(MasterClass item)
{
int val = item.Stuff();
var visitor = new Visitor(this, val);
item.Accept(visitor);
}
void AddToDict(SubClass1 item, int val) { subClass1Dict[item] = val; }
void AddToDict(SubClass2 item, int val) { subClass2Dict[item] = val; }
// Provides the visitor implementation that holds any state that might
// be needed and dispatches to the appropriate method.
private class Visitor : IVisitor
{
private MasterClassDictionary _parent;
private int _value;
public Visitor(MasterClassDictionary parent, int val)
{
_parent = parent;
_value = val;
}
public void Visit(SubClass1 item) => _parent.AddToDict(item, _value);
public void Visit(SubClass2 item) => _parent.AddToDict(item, _value);
}
}
That said, C# has added pattern matching with switch that would look substantially simpler. It's only downside is that it is doing more type checks which might be slower if this is in some really performance sensitive code, but is certainly going to be faster than using dynamic:
public void Add(MasterClass item)
{
int val = item.Stuff();
switch (item)
{
case SubClass1 i: AddToDict(i, val); break;
case SubClass2 i: AddToDict(i, val); break;
}
}

Generic Factory Method Casting Issue

Trying to create a factory to return a generic interface (following this answer) but getting the error:
Can't implicitly convert IFinancialsSyncService<Vendor, QuickBooksVendor> to IFinancialsSyncService<TEntity, TQuickBooksEntity>. Anexplicit conversion exists, are you missing a cast?
public class QuickBooksEntityServiceFactory
{
public IFinancialsSyncService<TEntity, TQuickBooksEntity> Create<TEntity, TQuickBooksEntity>()
where TEntity : class, IEntity, IFinancials, new()
where TQuickBooksEntity : class, IQuickBooksEntity
{
if (typeof(TEntity) == typeof(QuickBooksVendor))
{
return new QuickbooksVendorService();
}
throw new InvalidOperationException();
}
}
The service confirms to the IFinancialsSyncService interface:
public class QuickbooksVendorService : IFinancialsSyncService<Vendor, QuickBooksVendor>
However, if I cast it explicitly, I get a Cast is redundant error along with the first error still.
return (IFinancialsSyncService<Vendor, QuickBooksVendor>)new QuickbooksVendorService();
So the error is confusing me. What am I doing wrong?
UPDATE
This is what I'm trying to simplify. There are several instances similar to this also that call other common methods of the interface.
switch (enumDataElement)
{
//Export jobs
case DataElement.Item:
var itemService = new QuickbooksItemService();
exportResult = itemService.UpdateMozzoEntityWithFinancialsId(session, response, EntityId, intUserId);
break;
case DataElement.Vendor:
var VendorService = new QuickbooksVendorService();
exportResult = UpdateMozzoEntityWithFinancialsId(new QuickbooksVendorService(),session, response, EntityId, intUserId);
break;
case DataElement.Bill:
var billService = new QuickbooksBillService();
exportResult = billService.UpdateMozzoEntityWithFinancialsId(session, response, intUserId);
break;
case DataElement.PurchaseOrder:
var qbPOService = new QuickbooksPurchaseOrderService();
exportResult = qbPOService.UpdateMozzoEntityWithFinancialsId(session, response, intUserId);
break;
case DataElement.SalesReceipt:
var salesReceiptService = new QuickbooksSalesReceiptService();
exportResult = salesReceiptService.UpdateStratusEntityWithFinancialsId(session, response, intUserId);
break;
}
And replace it with something like:
var qbEntityService = EntityServiceFactory.Create(enumDataElement);
What would this factory look like?
This has to do with Liskov's Substitution Principle. Imagine that your Generic type is instead a property of the interface:
public interface IFinancials { }
public interface IFinancialsSyncService
{
IFinancials Financials { get; set; }
}
Now we implement this interfaces:
public class Financials : IFinancials {}
public class FinancialsSyncService : IFinancialSyncService
{
public Financials Financials { get; set; }
}
This results in a compiler error:
Compilation error: 'Program.FinancialsSyncService' does not implement interface member 'Program.IFinancialsSyncService.Financials'. 'Program.FinancialsSyncService.Financials' cannot implement 'Program.IFinancialsSyncService.Financials' because it does not have the matching return type of 'Program.IFinancials'.
Both problems have the same issue. In my example, the interface states that the result is of type IFinancials but is a more specific derived type Financials and even though any valid value that is placed in the property fulfills the interface, it cannot be replaced with any value derived from IFinancials only types that derive from Financials.
So if your code looked like:
public interface IFinancialsSyncService<TEntity>
where TEntity : IEntity
{
TEntity Financials { get; set; }
}
and you create a class:
public class QuickbooksVendorService : IFinancialSyncService<Vendor>
{
public Vendor Financials { get; set; }
}
However, now QuickbooksVendorService is a IFinancialSyncService<Vendor> not a IFinancialSyncService<TEntity> because the property is the derived type. Even if you didn't have this specific property it still leads to the same problem that generic type is more specific than the interface.
use Factory method and Adapter pattern
[TestFixture]
public class Class1
{
[Test]
public void Go()
{
var qbItem = Export(1);
var qbVendor= Export(2);
var qbSales= Export(3);
}
public qbEntityService Export(int number)
{
var qb = Class1.Create(number);
return qb.UpdateMozzoEntityWithFinancialsId();
}
public static IEntityService Create(int enumDataElement)
{
switch (enumDataElement)
{
case 1:
return new QuickbooksItemService();
case 2:
return new QuickbooksVendorService();
case 3:
return new QuickbooksSalesReceiptServiceAdapter(new QuickbooksSalesReceiptService());
default:
throw new Exception();
}
}
}
public interface IEntityService
{
qbEntityService UpdateMozzoEntityWithFinancialsId();
}
public class qbEntityService
{
}
public class QuickbooksItemService : IEntityService
{
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksItemService, performing UpdateMozzoEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksVendorService : IEntityService
{
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksVendorService, performing UpdateMozzoEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksSalesReceiptService
{
public qbEntityService UpdateStratusEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksSalesReceiptService, performing UpdateStratusEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksSalesReceiptServiceAdapter : IEntityService
{
private QuickbooksSalesReceiptService adaptee;
public QuickbooksSalesReceiptServiceAdapter(QuickbooksSalesReceiptService adaptee)
{
this.adaptee = adaptee;
}
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
return adaptee.UpdateStratusEntityWithFinancialsId();
}
}

c# generic type on where constraint

I have below simple object model where Manager class consist List Of Child objects and Child object must have reference to it's parent object:
public class ManagerBase<T1> where T1 : ChildBase<???>
{
public ManagerBase()
{
ChildObjects = new List<T1>();
}
public List<T1> ChildObjects { get; set; }
}
public class ChildBase<T1> where T1 : ManagerBase<???>
{
public ChildBase(T1 parentMgr)
{
ParentMgr = parentMgr;
ParentMgr.ChildObjects.Add(this);
}
public T1 ParentMgr { get; set; }
}
Above are BASE classes. Now, below are inherited sample Manager and Child classes. I don't know how to make below classes to compile as above BASE classes are not valid yet. Could you pls help? Thanks.
public class CatalogManager : ManagerBase<Catalog>
{
}
public class Catalog : ChildBase<CatalogManager>
{
}
To provide more clear idea: I have BASE Manager class, BASE Child Object class. There are different type of inherited Managers (CatalogManager, DocumentManager etc.) and different type of Child Objects (Catalog, Document etc). Now, each Manager must consist of List not List. F.e: CatalogManager with List, DocumentManager with List. At the same time, each child object must have reference to it's Manager. In other words, I need strong typing instead of using Base classes. Hope it clear. Thanks for your time.
You can achieve that by creating non-generic base classes for the generic base classes. Answer updated to avoid type casting; to do so, ChildObjects property had to be IEnumerable<T> because it's type parameter is covariant, while classes, IList<T>, and ICollection<T> are contravariant.
public abstract class ManagerBase
{
protected ManagerBase()
{
innerChildObjectList = new List<ChildBase>();
}
private IList innerChildObjectList;
public IEnumerable<ChildBase> ChildObjects
{
get
{
foreach (ChildBase child in innerChildObjectList.OfType<ChildBase>())
yield return child;
}
}
public void AddChild<T>(T child) where T : ChildBase
{
innerChildObjectList.Add(child);
}
public void RemoveChild<T>(T child) where T : ChildBase
{
innerChildObjectList.Remove(child);
}
public bool ContainsChild<T>(T child) where T : ChildBase
{
return innerChildObjectList.Contains(child);
}
//Add 'Insert', 'RemoveAt' methods if needed.
}
public abstract class Manager<T>
: ManagerBase
where T : ChildBase
{
public new IEnumerable<T> ChildObjects
{
get { return base.ChildObjects.OfType<T>(); }
}
}
public abstract class ChildBase
{
protected ChildBase(ManagerBase mgr)
{
ParentMgr = mgr;
}
private ManagerBase parentMgr;
public ManagerBase ParentMgr
{
get { return parentMgr; }
set
{
if (parentMgr != null && parentMgr.ContainsChild(this))
parentMgr.RemoveChild(this);
parentMgr = value;
parentMgr.AddChild(this);
}
}
}
public abstract class Child<T>
: ChildBase
where T : ManagerBase
{
protected Child(T mgr) : base (mgr)
{
}
public new T ParentMgr
{
get { return base.ParentMgr as T; }
set { base.ParentMgr = value; }
}
}
Now this will be okay:
public class CatalogManager : Manager<Catalog>
{
}
public class Catalog : Child<CatalogManager>
{
public Catalog(CatalogManager parentMgr) : base(parentMgr)
{
}
}

Keep a Dictionary<Type, MyClass<T>> where elements are referenceable by type

I have an abstract class called EntityTypeTransform with a single abstract method designed to hold a Func delegate that converts an IDataRecord into an instance of T.
public abstract class EntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransform();
}
An implementation of that class might look like (does look like) this:
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransform()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
Now I want to keep an instance of each of these classes in a generic Dictionary, something like:
Dictionary<Type, EntityTypeTransform<T>>
But this doesn't work because (for example) an instance of EntityTypeTransform Of Task is not the same as an instance of EntityTypeTransform Of TaskParameter.
Can anyone help me out?
Edit: I should add that the Type key = typeof(T)
Actually, you don't need to use a dictionary at all! You can use the fact that GenericClass<T> is actually a different type for each T, so it can have its own static fields (i.e. GenericClass<Foo>.SomeField is not shared with GenericClass<Bar>.SomeField)
For instance you can implement your cache like this:
static class TransformCache<TEntityType>
{
public static EntityTypeTransform<TEntityType> Transform { get; set; }
}
And use it like this:
TransformCache<TaskParameter>.Transform = new TaskParameterEntityTypeTransform();
You can't specify a strong-typed collection that would hold different generic types. Here's the approach I've used in a similar problem, modified to match your requirement:
class TransformCollection
{
private Hashtable cache = new Hashtable();
public void Add<T>(EntityTypeTransform<T> transform) where T : class
{
this.cache[typeof(T)] = itemToCache;
}
public bool Exists<T>() where T : class
{
return this.cache.ContainsKey(typeof(T));
}
public EntityTypeTransform<T> Get<T>() where T : class
{
if (!this.Exists<T>())
throw new ArgumentException("No cached transform of type: " + typeof(T).Name);
return this.cache[typeof(T)] as EntityTypeTransform<T>;
}
}
This gives you type-safe cache for your generic type (though type-safety is enforced by the class's logic, not C#). You can use it as follows:
var collection = new TransformCollection();
collection.Add(SomeMethodToGetTransform<Task>());
//...
if (collection.Exists<Task>())
{
var transform = collection.Get<Task>();
//...
}
You could use an interface that is non-generic and then implement that interface explicitly inside that abstract class, It's pretty common in the .Net library itself:
public interface IEntityTypeTransform
{
Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<TEntityType> : IEntityTypeTransform
where TEntityType : class
{
public virtual Func<IDataRecord, TEntityType> GetDataTransform()
{
return this.GetDataTransformImpl();
}
public abstract Func<IDataRecord, TEntityType> GetDataTransformImpl();
Func<IDataRecord, object> IEntityTypeTransform.GetDataTransform()
{
return this.GetDataTransform();
}
}
You would have to create a non-generic base class, e.g.
public abstract class EntityTypeTransformBase
{
public abstract Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<TEntityType> : EntityTypeTransformBase where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransformImpl();
public override Func<IDataRecord, object> GetDataTransform()
{
return GetDataTransformImpl();
}
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransformImpl()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
Now you can create your dictionary:
var d = new Dictionary<Type, EntityTypeTransformBase>();
d.Add(typeof(TaskParameter), new TaskParameterEntityTypeTransform());
You can use KeyedByTypeCollection to get type-safety and you can define an interface with a covariant type parameter to make sure that only objects of type EntityTypeTransform<T> can be added to the dictionary:
public interface IEntityTypeTransform<out TEntityType> where TEntityType : class
{
TEntityType Transform(IDataRecord dataRecord);
}
public abstract class EntityTypeTransform<TEntityType> : IEntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract TEntityType Transform(IDataRecord dataRecord);
}
public class TaskParameter
{
public int TaskId;
public string Name;
public string Value;
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override TaskParameter Transform(IDataRecord dataRecord)
{
return new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
public class SomeClass
{
public KeyedByTypeCollection<IEntityTypeTransform<object>> TransformDictionary = new KeyedByTypeCollection<IEntityTypeTransform<object>>()
{
new TaskParameterEntityTypeTransform(),
// More transforms here
};
}
Now you can use it like this:
public void SomeMethod(IDataRecord dataRecord)
{
TaskParameter taskParameter = TransformDictionary.Find<TaskParameterEntityTypeTransform>().Transform(dataRecord);
}
I have tried to understand what you exactly want I hope this is exactly what you are looking for!
You shall set in TaskParameter class the correct parameters: TaskId, Name, Value
public abstract class EntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransform();
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransform()
{
return x => new TaskParameter { X = x.FieldCount };
}
}
public class TaskParameter
{
public int X { get; set; }
}
Dictionary<Type, EntityTypeTransform<TaskParameter>> imADict;
Add a non generic interface to your transformers:
public interface IEntityTypeTransform
{
Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<T> : IEntityTypeTransform
{
public abstract Func<IDataRecord, object> GetDataTransform();
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, object> GetDataTransform()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task id"],
};
}
}
Then you can encapsulate your dictionary for ensure that datatypes will always match. Never allow to add a IEntityTypeTransform of a bad type :
public class TransformDistributor
{
private readonly Dictionary<Type, IEntityTypeTransform> _transforms = new Dictionary<Type, IEntityTypeTransform>();
public void Add<T>(EntityTypeTransform<T> type)
{
this._transforms.Add(typeof(T), type);
}
public T Transform<T>(IDataRecord record)
{
var transform = this._transforms[typeof(T)].GetDataTransform()(record);
if (transform is T)
{
return (T)transform;
}
else
{
// theorically can't happen
throw new InvalidOperationException("transformer doesn't return instance of type " + transform.GetType().Name);
}
}
}
The advantage are that at compile time, your are sure that nobody can insert a bad transformer, even if your are not using generics.
Usage :
var transforms = new TransformDistributor();
transforms.Add<TaskParameter>(new TaskParameterEntityTypeTransform());
var taskParameter = transforms.Transform<TaskParameter>(new DataRecord());

Categories