Please forgive me if the title is not worded correctly.
I am retrieving data from database tables for various devices and building a list. Different devices could have the same properties and will definitely have some properties that differ, So I am using a Factory Pattern to create whichever is needed at run time.
Factory class:
public interface IImportModel
{
IList CreateImportList(SqlDataReader reader);
}
And Concrete class:
public class Device1ImportModel : IImportModel
{
public string SerialNumber { get; set; }
public string PartA { get; set; }
public IList CreateImportList(SqlDataReader reader)
{
Device1ImportModel linkedItem = new Device1ImportModel();
List<Device1ImportModel> importList = new List<Device1ImportModel>();
while (reader.Read())
{
linkedItem = new Device1ImportModel();
linkedItem.SerialNumber = reader["SerialNo"].ToString();
linkedItem.PartA = reader["PartA"].ToString();
importList.Add(linkedItem);
}
return importList;
}
}
I create the device from the factory:
importModel = ImportModelFactory.CreateImportModel("Device1");
Now when I want to iterate over the importModel, I receive a compile time error on the line where I attempt to access item.SerialNumber
foreach (var item in importList)
{
string number = item.SerialNumber;
}
The error:
'object' does not contain a definition for 'SerialNumber' and no extension method 'SerialNumber' accepting a first argument of type 'object' could be found.
If I place a breakpoint and hover over item variable, I can see the properties and value.
I tried using dynamic instead of var, but then later in my code I can no longer use Linq or Lambda queries.
Edit 1
Added Code for CreateImportModel:
static public IImportModel CreateImportModel(DeviceType device)
{
switch (device)
{
case DeviceType.Device1:
return new Device1ImportModel();
case DeviceType.Device2:
return new DeviceImportModel();
default:
return null;
}
}
If you cannot change your method's signature, you can use:
foreach (var item in importList.Cast<Device1ImportModel>())
{
string number = item.SerialNumber;
}
This will throw an exception, however, if there will be an object in the importList
collection that is not a Device1ImportModel
or its derived class.
If you're not sure that all objects in the list are of that type and want to avoid exceptions, use this apporach:
foreach (var item in importList.OfType<Device1ImportModel>())
{
string number = item.SerialNumber;
}
Change IList
to List<Device1ImportModel>
(or IList<Device1ImportModel>
or IReadOnlyList<Device1ImportModel>
).
public List<Device1ImportModel> CreateImportList(SqlDataReader reader)
IList
is an older interface (pre-generics) and thus if you use IList
(rather than IList<Device1ImportModel
) then the compiler / runtime has no notion of the Type
of your data (ie it treats it as object
), thus:
'object' does not contain a definition for 'SerialNumber' and no extension method 'SerialNumber' accepting a first argument of type 'object' could be found.
You may also need to change the interface to:
public interface IImportModel<T>
{
List<T> CreateImportList(SqlDataReader reader);
}
and the class to:
public class Device1ImportModel : IImportModel<Device1ImportModel>
{
public string SerialNumber { get; set; }
public string PartA { get; set; }
public List<Device1ImportModel> CreateImportList(SqlDataReader reader)
{
You likely also want to change CreateImportModel
so instead of calling it like:
ImportModelFactory.CreateImportModel("Device1");
you instead call it like:
ImportModelFactory.CreateImportModel<Device1ImportModel>();
so that a concrete Device1ImportModel
is returned (and thus SerialNumber
is accessible).
I ended up just using a separate class to hold all the properties, whether each other concrete class uses them or not.
Its not ideal but works.
I would have preferred relying on each concrete class to be responsible for it's own properties though.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.