Reputation: 155
I have an interface:
public interface IReminder<T> where T : class, IIdentifiableEntity
{
IEnumerable<T> GetRemindersToBeSent(IRepository<T> repository);
}
and class TimesheetReminder
that implements this interface:
public class TimesheetReminder : IReminder<InvoiceSummary>
{
public IEnumerable<InvoiceSummary> GetRemindersToBeSent(IRepository<InvoiceSummary> invoiceSummaryRepository)
{
var today = DateTime.Today;
return invoiceSummaryRepository.List.Where(inv =>
inv.InvoiceSummaryStatus.CKAStatusName == "Draft" &&
inv.InsertDateUTC <= today.AddDays(-3) &&
inv.InsertDateUTC >= today.AddDays(-6) &&
inv.EndDate <= today.AddDays(-3)
);
}
The InvoiceSummary
implements IIdentifyableEntity
, yet
public static class ReminderFactory<T> where T : class, IIdentifiableEntity
{
public static IReminder<T> GetReminder(string applicationType)
{
IReminder<T> reminder;
switch (applicationType)
{
case "Invoicing":
reminder = (IReminder<T>)new TimesheetReminder();
break;
default:
reminder = null;
break;
}
return reminder;
}
}
Invoicing case returns null.
If TimesheetReminder
didn't implement IReminder
of an IIdentifiableEntity
I would understand it, but it does.
What am I doing wrong?
Upvotes: 3
Views: 917
Reputation: 679
For any other reason, if you still want to pass interface (IIdentifiableEntity) while creating factory instance
var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");
Solution (Proof of concept): Which I don't prefer since it is bad design.
We need to change interfaces (IReminder, IRepository) as covaraint.
While reading IIdentifiableEntity data we need to cast to corresponding concreate class type.
Sample Code shown below: Please follow code comments
public class Program
{
static void Main(string[] args)
{
//Hp --> Note: While creating factory instance we are passing interface type.
var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");
var x = factory.GetRemindersToBeSent(new InvoiceRepository());
//Hp --> Note: While reading data we need to cast to corrsponding concreate class type.
x.Cast<InvoiceSummary>().ToList().ForEach(item =>
Console.WriteLine($"{item.EntityName}:{item.TotalAmount}"));
Console.ReadKey();
}
}
public interface IRepository<out T>
{
// Hp --> Note: You can't use setter since T it is out parameter (covariant)
IEnumerable<T> List { get; }
}
public interface IReminder<out T> where T : class, IIdentifiableEntity
{
//Hp --> Note: You can't use IRepository<T> here since T is out parameter (covariant)
//Instead of T use interface IIdentifiableEntity
IEnumerable<T> GetRemindersToBeSent(IRepository<IIdentifiableEntity> repository);
}
public class TimesheetReminder : IReminder<InvoiceSummary>
{
public IEnumerable<InvoiceSummary> GetRemindersToBeSent(
//Hp --> Note: We need to cast IIdentifiableEntity to corrsponding concreate class type.
IRepository<IIdentifiableEntity> repository) =>
repository.List.Where(I => IsEqual("Invoice", I.EntityName)).Cast<InvoiceSummary>();
private bool IsEqual(string source, string target) =>
string.Equals(source, target, StringComparison.CurrentCultureIgnoreCase);
}
public interface IIdentifiableEntity
{
string EntityName { get; set; }
}
public static class ReminderFactory<T> where T : class, IIdentifiableEntity
{
public static IReminder<T> GetReminder(string applicationType)
{
IReminder<T> reminder;
switch (applicationType)
{
case "Invoicing":
reminder = new TimesheetReminder() as IReminder<T>;
break;
default:
reminder = null;
break;
}
return reminder;
}
}
public class InvoiceRepository : IRepository<InvoiceSummary>
{
public IEnumerable<InvoiceSummary> List => new List<InvoiceSummary> {
new InvoiceSummary { EntityName = "Invoice", TotalAmount = 100.00M } };
}
public class InvoiceSummary : IIdentifiableEntity
{
public string EntityName { get; set; }
public decimal TotalAmount { get; set; }
}
Upvotes: 0
Reputation: 679
I think you are creating factory instance by passing IIdentifiableEntity interface as shown below:
var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");
Which will always return null even when we type cast
reminder = new TimesheetReminder() as IReminder<T>;
Solution:
While creating factory instance, we need to pass concrete class type of InvoiceSummary
var factory = ReminderFactory<InvoiceSummary>.GetReminder("Invoicing");
Even we can modify creating factory instance by removing hard coded string "Invoicing"
var factory = ReminderFactory.Create<InvoiceSummary>();
The sample code is shown below:
public class Program
{
static void Main(string[] args)
{
//Hp --> Logic: Create factory instance by passing concrete class type.
var factory = ReminderFactory.Create<InvoiceSummary>();
var x = factory.GetRemindersToBeSent(new InvoiceRepository());
x.ToList().ForEach(item =>
Console.WriteLine($"{item.EntityName}:{item.TotalAmount}"));
Console.ReadKey();
}
}
public interface IIdentifiableEntity
{
string EntityName { get; set; }
}
public static class ReminderFactory
{
public static IReminder<T> Create<T>() where T : IIdentifiableEntity
{
IReminder<T> reminder = null;
if (typeof(InvoiceSummary) == typeof(T))
{
reminder = new TimesheetReminder() as IReminder<T>;
}
return reminder;
}
}
public interface IReminder<T> where T : IIdentifiableEntity
{
IEnumerable<T> GetRemindersToBeSent(IRepository<T> repository);
}
public interface IRepository<T>
{
IEnumerable<T> List { get; }
}
public class InvoiceRepository : IRepository<InvoiceSummary>
{
public IEnumerable<InvoiceSummary> List => new List<InvoiceSummary> {
new InvoiceSummary { EntityName = "Invoice", TotalAmount = 100.00M } };
}
public class InvoiceSummary : IIdentifiableEntity
{
public string EntityName { get; set; }
public decimal TotalAmount { get; set; }
}
public class TimesheetReminder : IReminder<InvoiceSummary>
{
public IEnumerable<InvoiceSummary> GetRemindersToBeSent(
IRepository<InvoiceSummary> repository) =>
repository.List.Where(I => IsEqual("Invoice", I.EntityName));
private bool IsEqual(string source, string target) =>
string.Equals(source, target, StringComparison.CurrentCultureIgnoreCase);
}
Upvotes: 0
Reputation: 144
Try following....
IReminder<InvoiceSummary> reminder = new TimesheetReminder() as IReminder<InvoiceSummary>;
Upvotes: 1
Reputation: 32790
What is T
? TimeSheetReminder
is IReminder<InvoiceSummary>
so if T
is not InvoiceSummary
then the reference conversion is not possible:
class Foo: IIdentifiableEntity { ... }
var reminder = new TimesheetReminder() as IReminder<Foo>; //returns null
Upvotes: 4