1

system developer in training here...

I'm in the middle of a .net core project, where we've tried to make a "nice architecture".

Currently, we have these projects: Models, DAL, BLL, UI (references kinda in that order too).

However, we've just decided to switch from entity framework to dapper, hence we'll loose lazy loading, and other features from ef.

My solution, in the models class, remove the references in the "base" models project, and have "derived" classes in the BLL.

However, when I was then working with the BL Layer repositories, I'm finding myself needing to cast from the base models class, to the derived extended models, giving me an InvalidCastException.

Where did I go wrong? What would be the "proper" way to have done this from the beginning?

Example

In my models project, I'll have the class foo and in BLL, a class: class foo : Models.foo, then in the BLL repository class fooRepository a method like:

public foo GetFooById(int Id) {
    return (foo)DAL.FooRepository.GetById(Id) 
}
  • the foo here would be form the models extension in BLL, but the one comming from the DAL, would be foo's base class.

Specific, compilable example: https://github.com/jona8690/ChildInheritanceExample

PeaceDealer
  • 977
  • 2
  • 9
  • 20
  • 4
    At a high level, think of it this way (since this is probably the mistake you made). An apple is a kind of fruit. Anything that expects a piece of fruit - you can give it an apple and it will be happy. But **is every piece of fruit an apple**? No, it isn't. Now, read that sentence again but replace `apple` with `foo` and `fruit` with `Models.foo`, – mjwills Oct 30 '18 at 12:34
  • Possible duplicate of [Unable to Cast from Parent Class to Child Class](https://stackoverflow.com/questions/988658/unable-to-cast-from-parent-class-to-child-class) – mjwills Oct 30 '18 at 12:35
  • I'm aware of the duplicate question, but I'm wondering where in my architecture I went wrong to get into this situation – PeaceDealer Oct 30 '18 at 12:47
  • I have added an example project, something i just thew together-ish, showing exactly what I'm trying to do – PeaceDealer Oct 30 '18 at 12:51
  • @mjwills regarding your example, is that not why we have casting? It was my understanding, that i could "tell" the code that this `fruit` is actually an `apple`. – PeaceDealer Oct 30 '18 at 13:17
  • `is that not why we have casting?` C# casting doesn't work the way you think it does. It can't magically turn a `fruit` (that isn't an `apple`) or `orange` or `strawberry` into an `apple`. Did you read the duplicate I suggested (including all of the comments)? – mjwills Oct 30 '18 at 20:26
  • If you _really_ want to stick with your current design (which I don't suggest since it seems to be based on a flawed understanding of types) then https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit may be worth a read. – mjwills Oct 30 '18 at 20:44
  • If the current design is as flawed, I'd rather not stick to it. The alternatives I'm looking into right now are the extension methods, or potentially trying to make the properties virtual and then override them in the DAL somehow. – PeaceDealer Oct 31 '18 at 08:50

2 Answers2

1

You can tell if this "fruit" is an "apple" by asking with reflection or by inspecting the properties - then only casting if it is capable of being an "apple"?

Capture the object returned from the repository object, inspect/compare the result of the object's object.GetType() result, only return the cast/convert object if appropriate, else make some appropriate null return or error response?

SazooCat
  • 150
  • 1
  • 6
  • I was reading somewhere, that even that wornt be possible, since I am instanciating `fuit` and not `apple`s. Therefor, the gettype wornt have the `apple`s – PeaceDealer Oct 30 '18 at 14:00
  • Dapper can return more generic objects, you could inspect properties and convert (make) an apple from the vague fruity result? Or as Adam G suggests above, have a more specific getAppleById() method that specifically pulls back Apples (to continue the analogy) – SazooCat Oct 31 '18 at 09:03
1

Ok, this took me a while. I guessed roughly what you were hitting, but your classes literally have the same name as its ancestor but in a different namespace, so a first read of the code literally appears that your dal and bll are using the same class.

So why doesn't this work? The answer comes down to what you are and are not doing when you cast an object. You are not actually mutating the object. A cast simply moves what would be a compile time check to rather be a runtime check. Let's simplify your example.

This will work:

object o = new Foo();
Foo myFoo = (Foo)o;

This will compile, but at runtime will throw your invalidcastexception.

object o = new Bar();
Foo myFoo = (Foo)o;

Why? Because Bar is not a Foo.

Or using the helpful fruit example in the comments above.

public class Fruit {}
public class Banana : Fruit {}
public class Apple : Fruit {}

private Fruit getFruit()
{
  return new Banana();
}

private void DoSomething()
{
  Apple apple = (Apple)this.getFruit(); // obviously can't cast
}

But, I hear you protest, my situation is different because Apple doesn't descend from Banana. That much is true, however remember that an Apple will have properties and methods in addition to the base Fruit class, methods or properties that the application can use when dealing with what it knows to be an Apple but not any other Fruity type.

In your case, you expect a more specialised type of Bar than your dal provides

Adam G
  • 1,283
  • 1
  • 6
  • 15
  • What would be the "right" way of going about this then? I'm currently looking into the extension systems, but they don't seem to support properties, which would be a slight inconvenience. – PeaceDealer Oct 31 '18 at 09:02
  • It depends on what you mean when you want to introduce a property in the BLL. Even if it was permitted, how could the DAL know how to meaningfully populate it? I am sure your use case makes sense, it is just a bit hard to see when talking about Foos and Bars. Possibly you could define a dto class in a shared third library that is used by both your DAL and BLL and share one of these. – Adam G Oct 31 '18 at 11:56