Reputation: 364
I have the following set of classes in my application:
This represents an entity, which is a SQL table. Every entity classes inherit from this interface.
public interface IBaseEntity
{
public int Id { get; set; }
}
Then I have a class that will hold all business logic for that entity. BaseBLL
holds methods that are common to all Business objects.
public class BLLPai<D>
where D : class
{
protected D EntidadeData;
}
public class BaseBLL<E, D> : BLLPai<D>
where E: IBaseEntity
where D: BaseData<E>
{
//commom methods
}
To create a business object for the entity, it is also needed to create a Data
object, which will hold all database related commands.
public class BaseData<E> : DataPai
where E : IBaseEntity
{
// commom methods
}
public class DataPai
{
protected DbConnection dbCon { get; set; }
public DataPai()
{
dbCon = new DbConnection();
}
}
This would be an example of a working entity (database table) in this design:
public class MyTable : IBaseEntity
{
public int Id {get;set;}
public string Name {get;set;}
}
public MyTableData: BaseData<MyTable>
{
public List<MyTable> GetAllItems()
{
// return result of SELECT
}
}
public MyTableBLL : BaseBLL<MyTable, MyTableData>
{
public List<MyTable> GetAllItems()
{
// business logic
return this.EntidadeData.GetAllItems();
}
}
In order to use this in my architecture, I designed a Controller base class, which I am using in all of my Controllers. (I'm using this in a WebApi .Net Core project) The class is as follows:
public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL>
{
}
[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
where Entidade : IBaseEntity
where BLL : BaseBLL<Entidade, BaseData<Entidade>>
{
}
What I am not understanding is why it gives me an error when I try to create the MyTableController
class:
Error:
The type 'MyTableBLL' cannot be used as type parameter 'BLL' in the generic type or method '
GerenciadorLogadaCadastroController<Entidade,BLL>
'. There is no implicit reference conversion from 'MyTableBLL
' to 'BaseBLL<MyTable, BaseData<MyTable>>
'.
However, if I change the GerenciadorLogadaCadastroController
and the MyTableController to this, it seems to work fine.
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL, Data> : ControllerBase
where Entidade : IBaseEntity
where BLL : BaseBLL<Entidade, Data>
where Data: BaseData<Entidade>
{
}
public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL, MyTableData>
{
}
But I don't want to type the Data class because it is already included in the BLL class. I found a similar design of what I am trying to do in a Vb.Net application and it works just fine. What am I missing here that is causing that implicit reference conversion error?
Upvotes: 0
Views: 238
Reputation: 117019
I did a bit of simplification to your code to get it down to an example that shows what has gone wrong. There are a few steps.
Here is my initial simplification:
public interface IBaseEntity { }
public class MyTable : IBaseEntity { }
public class BaseData<E> where E : IBaseEntity { }
public class MyTableData : BaseData<MyTable> { }
public class BaseBLL<E, D> where E : IBaseEntity where D : BaseData<E> { }
public class MyTableBLL : BaseBLL<MyTable, MyTableData> { }
public abstract class Controller<E, B> where E : IBaseEntity where B : BaseBLL<E, BaseData<E>> { }
public class MyTableController : Controller<MyTable, MyTableBLL> { }
Now, at this point, the whole IBaseEntity
portion of the code is not needed to replicate your issue so I removed it:
public class BaseData { }
public class MyTableData : BaseData { }
public class BaseBLL<D> where D : BaseData { }
public class MyTableBLL : BaseBLL<MyTableData> { }
public abstract class Controller<B> where B : BaseBLL<BaseData> { }
public class MyTableController : Controller<MyTableBLL> { }
Now I did some crafty renaming of the types:
public class Fruit { }
public class Apple : Fruit { }
public class BowlOf<D> where D : Fruit { }
public class BowlOfApples : BowlOf<Apple> { }
public abstract class Controller<B> where B : BowlOf<Fruit> { }
public class BowlOfApplesController : Controller<BowlOfApples> { }
What this is then showing is that when you define Controller<BowlOfApples>
you are saying that BowlOfApples
must inherit from BowlOf<Fruit>
. But BowlOfApples
inherits from BowlOf<Apple>
and not BowlOf<Fruit>
!
This constraint should make sense. Let's assume that BowlOf<T> : List<T>
and we have Banana : Fruit
then imagine that this code were legal:
BowlOf<Apple> apples = new BowlOf<Apple>();
BowlOf<Fruit> fruit = apples; // BOOM!!! imagine if this could be done!
Banana banana = new Banana();
fruit.Add(banana);
We have code which could add a Banana
to a BowlOf<Apple>
! That can't happen. The illegal thing is to cast an BowlOf<Apple>
to a BowlOf<Fruit>
.
Simply put BowlOf<Apple>
DOES NOT INHERIT FROM BowlOf<Fruit>
. This is in spite of Apple
inheriting from Fruit
.
Your code is making the same mistake when you try to cast BaseBLL<MyTable, MyTableData>
to BaseBLL<MyTable, BaseData<MyTable>>
.
You did correctly identify, in your question, that this change would work:
public abstract class Controller<F, B> where F : Apple where B : BowlOf<F> { }
public class BowlOfApplesController : Controller<Apple, BowlOfApples> { }
So when you say "But I don't want to type the Data class because it is already included in the BLL class." Well, you can't avoid that.
Upvotes: 2
Reputation: 357
Here's a suggestion. Add an IBaseDLL interface.
public interface IBaseBLL
{
//commom methods
}
Then add it to the list interfaces on your BaseBLL class.
public class BaseBLL<E, D> : BLLPai<D>, IBaseBLL
where E : class, IBaseEntity
where D : BaseData<E>
{
//commom methods
}
Now you'll be able to change your base controller to the following:
[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
where Entidade : IBaseEntity
where BLL : IBaseBLL
{
}
-Isaac
Upvotes: 0