0

I am having an issue with "carrying forward" the results of a DAL-level class to my BLL class of the same definition.

Error: "Unable to cast object of type 'DeserializedGame' to type 'DeserializedGameBLL'."

//DAL Classes
public class GameCollection{
    public List<DeserializedGame> Games { get; set; }
    public int RefreshInterval { get; set; }
    public string CurrentDate { get; set; }
    public string NextDate { get; set; }
    public string PrevDate { get; set; }
}

public class DeserializedGame
{
    public string Atcommon { get; set; }
    public string Canationalbroadcasts { get; set; }
    public string Ata { get; set; }
    public bool Rl { get; set; }
    public int Atsog { get; set; }
    public string Bs { get; set; }
    public string Htcommon { get; set; }
    public int Id { get; set; }
    public string Atn { get; set; }
    public int Hts { get; set; }
    public string Atc { get; set; }
    public string Htn { get; set; }
    public string Usnationalbroadcasts { get; set; }
    public bool Gcl { get; set; }
    public string Hta { get; set; }
    public int? Ats { get; set; }
    public string Htc { get; set; }
    public int Htsog { get; set; }
    public string Bsc { get; set; }
    public int Gs { get; set; }
    public bool Gcll { get; set; }
}

//BLL Classes
public class GameCollectionBLL : IEnumerable
{
    public List<DeserializedGame> Games { get; set; }
    public int RefreshInterval { get; set; }
    public string CurrentDate { get; set; }
    public string NextDate { get; set; }
    public string PrevDate { get; set; }

    public GameCollectionBLL(List<DeserializedGame> gameList, int refreshInterval, string currentDate,
        string nextDate, string previousDate)
    {
        this.Games = gameList;
        this.RefreshInterval = refreshInterval;
        this.CurrentDate = currentDate;
        this.NextDate = nextDate;
        this.PrevDate = previousDate;
    }

    public IEnumerator GetEnumerator()
    {
        return (Games as IEnumerable).GetEnumerator();
    }
}

public class DeserializedGameBLL : BaseSeasonSchedule
{
    public string Atcommon { get; set; }
    public string Canationalbroadcasts { get; set; }
    public string Ata { get; set; }
    public bool Rl { get; set; }
    public int Atsog { get; set; }
    public string Bs { get; set; }
    public string Htcommon { get; set; }
    public int Id { get; set; }
    public string Atn { get; set; }
    public int Hts { get; set; }
    public string Atc { get; set; }
    public string Htn { get; set; }
    public string Usnationalbroadcasts { get; set; }
    public bool Gcl { get; set; }
    public string Hta { get; set; }
    public int? Ats { get; set; }
    public string Htc { get; set; }
    public int Htsog { get; set; }
    public string Bsc { get; set; }
    public int Gs { get; set; }
    public bool Gcll { get; set; }
}

public abstract class BaseSeasonSchedule
{
    public int GameID { get; set; }
    public DateTime GameDate { get; set; }
}

//UI usage
public partial class Form1 : Form
{
    private SeasonSheduleBLL objSeasonSchedule = new SeasonSheduleBLL();

    private void button1_Click(object sender, EventArgs e)
    {
            GameCollectionBLL gameDay = objSeasonSchedule.GetGameCollection(json_string);

            foreach (DeserializedGameBLL game in gameDay)  //error occurs here when referencing the DeserialzedGameBLL
                    {
                        // do stuff here
                    }
    }
}

What do I need to do to make my DeserialzedGame collection work properly?

I have looked at this question (and attempted to follow @Measuring advice in the original post). I have considered (and still haven't elimiated using DTO's, but I am unsure how to use them correctly).

If this belongs in the Code Review section, I will happily post in there instead, but I thought enough people might be experiencing the same frustration a I am.

Community
  • 1
  • 1
Kulstad
  • 79
  • 8
  • Use Automapper or some other library to map between objects in your layers. – Janne Matikainen Jan 14 '16 at 17:50
  • The error is telling you that you can't implicitly cast between two unrelated types. You'd have to create a new instance of the target type and set its properties to match the source type's. But... Why do you have two identical types in the first place? Just use one of them throughout the system and remove the other one. – David Jan 14 '16 at 17:55
  • @David Would it be correct to assume that your suggestion is what DTO's are all about? – Kulstad Jan 14 '16 at 18:07
  • 1
    @Kulstad: Well, your objects already *are* DTOs. So I don't really know what you're asking there. But the point is that you really don't need *completely identical objects*. – David Jan 14 '16 at 18:08
  • @David I think this is where I start feeling like I'm coding outside of my weight class. I know what you're telling me, I just don't know how to implement it. – Kulstad Jan 14 '16 at 18:16
  • @Kulstad: I'm not sure how else to explain it. Delete one of your two identical classes and use the other one exclusively in the rest of your code. What exactly would prevent you from being able to do that? – David Jan 14 '16 at 18:18
  • @David: using DeserializedGame class, from the DAL, I would need to include a reference to the DAL (and the `using` statement) in my UI layer to use it? If so, doesn't that violate the "the UI layer should never interact directly with the DAL" best practice? – Kulstad Jan 14 '16 at 18:25
  • @Kulstad: And duplicating the code violates other best practices as well. Put the object in a common library which is referenced by both the UI and the DAL. (A central "domain core" library should contain all of the common types/functionality/interfaces/etc. used throughout the codebase, and should not reference anything outside of itself.) – David Jan 14 '16 at 18:27
  • @David: (how I wish I had enough rep to take this to chat with you)...simply create a new ClassLibrary project, and add it as a reference to the DAL, BLL, and UI layers? – Kulstad Jan 14 '16 at 18:40
  • @Kulstad: That's a common approach. Normally I put the common stuff in the business layer and the other layers reference that. But that requires some measure of dependency inversion, which may be a bit much in this case. So having a separate "common" library which is kind of outside the three layers and just contains common types like this is a valid approach as well. – David Jan 14 '16 at 18:42

2 Answers2

1

The error is simply telling you that you can't implicitly convert between two unrelated types. Just because they look identical doesn't mean they're the same type. To correct this, you'd have to create an instance of the target type and copy all of the data from the source object to the target object. (Or use a library like Automapper to do the same thing.)

However, this really begs the question... Why do you have identical types in the first place?

[based on comments on the question above...] It appears that you have an architecture like this:

  • Application layer references business layer.
  • Business layer references data layer.
  • Data layer references nothing.

So what's happening is:

  • Data layer has a type so it can materialize data from the database.
  • Business layer gets that object from the data layer.
  • Application layer needs that object, but can't reference the data layer. So the business layer needs to convert the data layer object to a business layer object which the application layer can receive.

Just talking about it makes it sound like a headache :)

Instead, consider a slightly revised architecture:

  • Common library contains DTOs (as well as perhaps interfaces and other common types), but no meaningful logic, and references nothing.
  • Application layer references business layer and common library.
  • Business layer references data layer and common library.
  • Data layer references common library.

Now that object being returned by the data layer can go all the way to the application layer without modification, since every layer understands that type.

This isn't to say that this setup is ideal. In many cases I would even argue that this "common DTOs" approach can quickly become an anti-pattern and is a poor replacement for a proper domain driven architecture. But, I've always been a bit of a purist about such things. My argument is contingent upon the idea that the business layer references nothing and the data layer references the business layer. Which, unless you're familiar with dependency inversion patterns and techniques, is going to be difficult for you.

In simple cases with this simple 3-layer vertical architecture, having a common library can be very handy to avoid this exact problem.

David
  • 208,112
  • 36
  • 198
  • 279
  • Thank you so much for your comments in my original post, and for this clarification. I've implemented what you've suggested, and it worked like a charm. – Kulstad Jan 14 '16 at 21:25
0

You could use use a library such as Automapper or you could do this yourself by using the explicit keyword.

public class DeserializedGame
{
    public string Atcommon { get; set; }
    public string Canationalbroadcasts { get; set; }
    public string Ata { get; set; }
    public bool Rl { get; set; }
    public int Atsog { get; set; }
    public string Bs { get; set; }
    public string Htcommon { get; set; }
    public int Id { get; set; }
    public string Atn { get; set; }
    public int Hts { get; set; }
    public string Atc { get; set; }
    public string Htn { get; set; }
    public string Usnationalbroadcasts { get; set; }
    public bool Gcl { get; set; }
    public string Hta { get; set; }
    public int? Ats { get; set; }
    public string Htc { get; set; }
    public int Htsog { get; set; }
    public string Bsc { get; set; }
    public int Gs { get; set; }
    public bool Gcll { get; set; }
    public static implicit operator DeserializedGameBLL(DeserializedGame game)
   {
       return new DeserializedGameBLL()
       {
           pubAtcommon = pubAtcommon,
           Canationalbroadcasts= Canationalbroadcasts,
           Ata = Ata ,
           Rl = Rl ,
           Atsog = Atsog ,
           Bs = Bs ,
           Htcommon = Htcommon ,
           Id = Id ,
           Atn =Atn ,
           Hts =Hts ,
           Atc =Atc ,
           Htn =Htn,
           Usnationalbroadcasts =Usnationalbroadcasts,
           Gcl =Gcl,
           Hta =Hta,
           Ats =Ats,
           Htc =Htc,
           Htsog = Htsog,
           Bsc = Bsc,
           Gs = Gs,
           Gcll = Gcll 
        };
    }
}

You would be able to use this like this:

DeserializedGame b = new DeserializedGame();
DeserializedGameBLL c = b;

You could also use the explicit operator and make it so you don't need a cast as well if you didn't want automatic conversions.

Jetti
  • 2,418
  • 1
  • 17
  • 25
  • Wouldn't adding the`'public static implicit operator` to the `DeserializedGame` class in the DAL create the need for the DAL class to know about the BLL class? – Kulstad Jan 14 '16 at 18:15
  • @Kulstad yes, it would but it wouldn't be any different than using Automapper or similar libraries – Jetti Jan 15 '16 at 15:20