Home > Software design >  How can I retrieve data from a foreign key table in SQL with C#
How can I retrieve data from a foreign key table in SQL with C#

Time:01-23

I'm trying to build my first application for learning purposes but I've hit a wall.. My goal is to retrieve data from a table labeled PC and several foreign key tables that has an ID and a string value. I was able to do a SQL query that retrieved all the information from the PC table (uncluding foreign key ID's) but I want to be able to see the "brand" and "model" of a pc.

My errormessage

System.InvalidCastException: 'Specified cast is not valid.'

My PC model in C# looks like this

public class PC
{
    public int PC_ID { get; set; }
    public string PC_SERIAL { get; set; }
    public string PC_NAME { get; set; }
    public Nullable<int> PC_BRAND_ID { get; set; }
    public PCBrand PC_Brand { get; set; }
    public Nullable<int> PC_MODEL_ID { get; set; }
    public PCBrand PC_Model { get; set; }
    public Nullable<int> PC_OS_ID { get; set; }
    public PCOS PC_OS { get; set; }
    public Nullable<int> PC_NET_ID { get; set; }
    public PCNet PC_Net { get; set; }
    public Nullable<int> PC_RAM_ID { get; set; }
    public PCRam PC_Ram { get; set; }
    public override string ToString()
    {
        return $"{PC_ID} - {PC_NAME} - {PC_SERIAL} - {PC_Model} - {PC_Brand} - {PC_OS} - {PC_Net} - {PC_Ram}";
    }
}

For instance, one of my foreign key tables looks like this:

namespace Inventory.Data.Model
{
    public class PCBrand
    {
        public int PC_BRAND_ID { get; set; }
        public string PC_BRAND { get; set; }
    }
}

And this is my code in my repository class

private const string SqlGetAllPC = @"Select PC.PC_ID, PC.PC_NAME,
     PC_BRAND.PC_BRAND,PC_MODEL.PC_MODEL,PC_NET.
     PC_NET,PC_OS.PC_OS,PC_RAM.PC_RAM
        FROM PC
        INNER JOIN PC_BRAND on PC.PC_BRAND_ID=PC_BRAND.PC_BRAND_ID
        INNER JOIN PC_MODEL ON PC.PC_MODEL_ID=PC_MODEL.PC_MODEL_ID
        INNER JOIN PC_NET ON PC.PC_NET_ID=PC_NET.PC_NET_ID
        INNER JOIN PC_OS ON PC.PC_OS_ID=PC_OS.PC_OS_ID
        INNER JOIN PC_RAM ON PC.PC_RAM_ID=PC_RAM.PC_RAM_ID";

    public IEnumerable<PC> GetAllFromPc()
    {
        IEnumerable<PC> list = null;
        try
        {
            connector.OpenConnection();
            System.Data.DataTable data = connector.GetData(SqlGetAllPC);
            if (data != null && data.Rows.Count > 0)
            {
                list = ConvertToPC(data);
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            connector.CloseConnection();
        }
        return list;
    }

    private IEnumerable<PC> ConvertToPC(DataTable data)
    {
        List<PC> tmp = new List<PC>();
        foreach (DataRow row in data.Rows)
        {
            tmp.Add(new PC()
            {
                PC_ID = row.Field<int>(0),
                PC_SERIAL = row.Field<string>(1),
                PC_NAME = row.Field<string>(2),
                PC_BRAND_ID = row.Field<int>(3),
                PC_MODEL_ID = row.Field<int>(4),
                PC_OS_ID = row.Field<int>(5),
                PC_NET_ID = row.Field<int>(6),
                PC_RAM_ID = row.Field<int>(7),
            });
        }
        return tmp;
    }
}

And at last my button that retrieves the data

private void GetAllButton(object sender, RoutedEventArgs e)
{
   Data.Repositories.PCRepository newpc = new Data.Repositories.PCRepository();
   IEnumberable<Data.Model.PC> data = newpc.GetAllFromPc();
   if (data != null)
   {
       StringBuilder sb = new StringBuilder();
       data.ToList()
           .ForEach(x=> sb.AppendLine(x.ToString()))
       MessageBox.Show(sb.ToString());
   }
}

With some editing I was able to retrieve from my PC tabel. But I also want to retrieve the PC's model/brand name from the PC_Brand and PC_Model table in my database. enter image description here

CodePudding user response:

Your SELECT is reading what I take to be string values (PC_MODEL.PC_MODEL, PC_NET.PC_NET, PC_OS.PC_OS, PC_RAM.PC_RAM), but you are assigning them to integers, hence the invalid cast.

Why am I sure that these are strings? Because of your joins. You are joining on e.g. PC_MODEL_ID which are almost certainly integers.

Change your SELECT to:

SELECT PC.PC_ID, PC.PC_NAME, PC_BRAND.PC_BRAND,PC.PC_MODEL_ID, PC.PC_NET_ID, PC.PC_OS_ID,PC.PC_RAM_ID
FROM PC
INNER JOIN PC_BRAND on PC.PC_BRAND_ID=PC_BRAND.PC_BRAND_ID
INNER JOIN PC_MODEL ON PC.PC_MODEL_ID=PC_MODEL.PC_MODEL_ID

Note that you do not need all the other joins, if you are only selecting the integer values.

EDIT

The you need to change your select to

SELECT PC.PC_ID, PC.PC_SERIAL, PC.PC_NAME, 
PC_BRAND.PC_BRAND, PC.PC_BRAND_ID, 
PC_MODEL.PC_MODEL, PC.PC_MODEL_ID, 
PC_NET.PC_NET, PC.PC_NET_ID, 
PC_OS.PC_OS, PC.PC_OS_ID, 
PC_RAM.PC_RAM, PC.PC_RAM_ID 
FROM PC 
INNER JOIN PC_BRAND on PC.PC_BRAND_ID = PC_BRAND.PC_BRAND_ID 
INNER JOIN PC_MODEL ON PC.PC_MODEL_ID = PC_MODEL.PC_MODEL_ID 
INNER JOIN PC_NET ON PC.PC_NET_ID = PC_NET.PC_NET_ID 
INNER JOIN PC_OS ON PC.PC_OS_ID = PC_OS.PC_OS_ID 
INNER JOIN PC_RAM ON PC.PC_RAM_ID= PC_RAM.PC_RAM_ID

Now you can do something like this in ConvertToPC:

private static IEnumerable<PC> ConvertToPC(DataTable data)
{
    List<PC> tmp = new List<PC>();
    foreach (DataRow row in data.Rows)
    {
        tmp.Add(new PC()
        {
            PC_ID = row.Field<int>(0),
            PC_SERIAL = row.Field<string>(1),
            PC_NAME = row.Field<string>(2),
            PC_Brand = new PCBrand
            {
                PC_BRAND = row.Field<string>(3),
                PC_BRAND_ID = row.Field<int>(4)
            },
            PC_BRAND_ID = row.Field<int>(4),
            PC_Model = new PCModel
            {
                PC_MODEL = row.Field<string>(5),
                PC_MODEL_ID = row.Field<int>(6)
            },
            PC_MODEL_ID = row.Field<int>(6),
            PC_Net = new PCNet
            {
                PC_NET = row.Field<string>(7),
                PC_NET_ID = row.Field<int>(8)
            },
            PC_NET_ID = row.Field<int>(8),
            PC_OS = new PCOS
            {
                PC_OS = row.Field<string>(9),
                PC_OS_ID = row.Field<int>(10)
            },
            PC_OS_ID = row.Field<int>(10),
            PC_Ram = new PCRam
            {
                PC_RAM = row.Field<string>(11),
                PC_RAM_ID = row.Field<int>(12)
            },
            PC_RAM_ID = row.Field<int>(12)
        });
    }
    return tmp;
}

Please note that I have changed your code by as little as possible. In practice I would do a lot of things very differently. For example, all the foreign key ids are now duplicated: instead of having both an object of type PCModel and an int PC_MODEL_ID in your PC class, you only need the object PCModel, because it already contains the ID.

  • Related