1

I have a class that looks like this

public class TableMapper<TSource> { .... .... public TableMapper(IQueryExtractor queryExtractor, string tableAliasPrefix = null) { QueryExtractor = queryExtractor; TableAliasPrefix = tableAliasPrefix; } .... .... } 

I create an instance of this class from within another class like so

public class CustomerMapper : ReportTemplate { private readonly IQueryExtractor QueryExtractor; private readonly TableMapper<Customer> customerMapper; private readonly TableMapper<Client> clientMapper; public CustomerMapper(DbContext context) : base(new QueryBuilder(), new QueryExecutor(context.Database.Connection.ConnectionString)) { QueryExtractor = new QueryExtractor(context); customerMapper = new TableMapper<Customer>(QueryExtractor, "Customer"); clientMapper = new TableMapper<Client>(QueryExtractor); } public List<IReportRelation> ReportRelations { return new List<IReportRelation> { new ReportRelation { ForeignColumn = customerMapper.GetReportColumn(x => x.ClientId), LocalColumn = clientMapper.GetReportColumn(x => x.Id), }, }; } } 

As you can see in the ReportRelations method, I return list of ReportRelation implementations. What I need to do is add 2 more public properies to my IReportRelation interface and ReportRelation implementation to hold instance of my TableMapper<TSource> class

How can I define a public variable in IReportRelation interface that will have a copy/instance of customerMapper property?

Here is how my IReportRelation definition

public interface IReportRelation { //Here I need to add an instance that hold the localTable //something like this TableMapper<TSource> LocalMapper //something like this TableMapper<TSource> ForeignMapper IReportColumn ForeignColumn { get; set; } IReportColumn LocalColumn { get; set; } } 

UPDATED

After Tim's suggestion below I change my code to the following

public interface IReportRelation<TLocal, TForeign> { TableMapper<TLocal> LocalMapper { get; set;} TableMapper<TForeign> ForeignMapper { get; set; } IReportColumn ForeignColumn { get; set; } IReportColumn LocalColumn { get; set; } } 

Then in my CustomerMapper class my function will look like this

public List<IReportRelation<Customer,Client> ReportRelations { return new List<IReportRelation<Customer,Client>> { new ReportRelation<Customer,Client> { LocalMapper = this.customerMapper, ForeignMapper = this.clientMapper, ForeignColumn = customerMapper.GetReportColumn(x => x.ClientId), LocalColumn = clientMapper.GetReportColumn(x => x.Id), }, }; } 

But the problem is that ReportRelations will always return a list of IReportRelation<Customer,Client>. What if I want to return mix list something like this for example

 public List<IReportRelation<Customer,Client> ReportRelations { return new List<IReportRelation<Customer,Client>> { new ReportRelation<Customer,Client> { LocalMapper = this.customerMapper, ForeignMapper = this.clientMapper, ForeignColumn = customerMapper.GetReportColumn(x => x.ClientId), LocalColumn = clientMapper.GetReportColumn(x => x.Id), }, new ReportRelation<Customer,Team> { LocalMapper = this.customerMapper, ForeignMapper = this.teamMapper, ForeignColumn = customerMapper.GetReportColumn(x => x.TeamId), LocalColumn = clientMapper.GetReportColumn(x => x.Id), }, }; } 
9
  • 2
    You could use a property. Interfaces don't have variables. Commented Jul 6, 2016 at 20:50
  • How can I define my property to store instance of my customerMapper Commented Jul 6, 2016 at 20:52
  • 2
    TableMapper<TSource> LocalMapper { get; set; }, the same way you define other properties. I'm a little confused as to what the challenge is here. Commented Jul 6, 2016 at 20:53
  • you could create an base class that inherit from this interface, and in the base class constructor you can enforce the user to init this property. Commented Jul 6, 2016 at 20:54
  • I guess the problem comes from Client and Customer not being fixed. Commented Jul 6, 2016 at 21:04

2 Answers 2

2

You cannot mix different generic type parameters in a list. A common solution to this problem is to have a non generic base interface and to derive the generic one from it.

public interface ITableMapper { // Use Sytem.Type arguments where appropriate and // the object type instead of generic types ... } public class TableMapper<TSource> : ITableMapper { ... } 
public interface IReportRelation { ITableMapper LocalMapper { get; } ITableMapper ForeignMapper { get; } IReportColumn ForeignColumn { get; set; } IReportColumn LocalColumn { get; set; } } public interface IReportRelation<TLocal, TForeign> : IReportRelation { new TableMapper<TLocal> LocalMapper { get; set; } new TableMapper<TForeign> ForeignMapper { get; set; } } 
public class ReportRelation<TLocal, TForeign> : IReportRelation<TLocal, TForeign> { ITableMapper IReportRelation.LocalMapper { get { return LocalMapper; } } ITableMapper IReportRelation.ForeignMapper { get { return ForeignMapper; } } public TableMapper<TLocal> LocalMapper { get; set; } public TableMapper<TForeign> ForeignMapper { get; set; } public IReportColumn ForeignColumn { get; set; } public IReportColumn LocalColumn { get; set; } } 

Make sure to implement the non generic interface explicitly. This hides it when working directly with the class.

Now you can have a List<IReportRelation> and fill in ReportRelation<TLocal, TForeign> objects having different generic type arguments.

You can find this pattern in the .NET Framework Libary. See IList<T> : IList, IEnumerable<T> : IEnumerable and so on ...

Sign up to request clarification or add additional context in comments.

1 Comment

I have never seen such a pattern. That looks pretty useful. I guess that's the difference between having 45k and 1k points ;)
0

I assume Client and Customer could change. Here what you could do:

public interface IReportRelation<TLocal, TForeign> { TableMapper<TLocal> LocalMapper { get; set; } TableMapper<TForeign> ForeignMapper { get; set; } IReportColumn ForeignColumn { get; set; } IReportColumn LocalColumn { get; set; } } public class ReportRelation<TLocal, TForeign> : IReportRelation<TLocal, TForeign> { public TableMapper<TLocal> LocalMapper { get; set; } public TableMapper<TForeign> ForeignMapper { get; set; } public IReportColumn ForeignColumn { get; set; } public IReportColumn LocalColumn { get; set; } } 

And for your CustomMapper class

public class CustomerMapper : ReportTemplate { ... public List<IReportRelation<Customer, Client>> ReportRelations { return new List<IReportRelation<Customer, Client>> { new ReportRelation<Customer, Client> { LocalMapper = this.customerMapper, ForeignMapper = this.clientMapper, ForeignColumn = customerMapper.GetReportColumn(x => x.ClientId), LocalColumn = clientMapper.GetReportColumn(x => x.Id) } }; } } 

5 Comments

Please check my updated question as I did this but the problem is that I need to return a list of mix sources it is not always going to be <Customer,Client> it will change
I don't see any way of having such a list.
Is there a way to return a list of dictionary?
@Jaylen - Yes. List<Dictionary<TKey, TValue>>
How would I add new ReportRelation<Customer, Client> or new ReportRelation<Customer, Team> to the Dictionary?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.