Hi I have a simple issue with an sql application that I seem not to be capable of resolve. It involves the use of generics of which I am not too familiar with. I did researches here and on-line but I do not seem to find a fitting solution for my case.
I have two classes: Table and Field. I want Table to contain a List of Field and I want each Field to contain a List of RecordSet. The tricky part is that I want the user to choose which type of RecordSet to implement.
The Class Definition of Table is:
namespace DBlib
{
public class DBTable<T>
{
public List<DBField<T>> FieldName = new List<DBField<T>>();
public DBTable (string NameOfTable)
{
}
public void AddField (string Name)
{
DBField<T> TempList = new DBField<T>();
FieldName.Add(TempList);
}
}
}
The Class Definition of Field is:
namespace DBlib
{
public class DBField<T>
{
public List<T> RecordSet = new List<T>();
public DBField()
{
}
}
}
With this code the user is forced cast the type when he is instantiating DBTable. This is not correct. I want the user to cast the type when the AddField method is invoked. Can you suggest a simple way to solve this issue?
UPDATE #1
I changed TempList as DBField in the Table Class definition. Sorry for the confusion there.
I want to add also this code to explain better what my issue is. Assuming the first Field of the table is an integer, the user should do:
namespace SpecifytheName
{
public class User
{
DBTable<int> Table = new DBTable<int>();
public User()
{
}
}
}
Instead, I want the user to do:
namespace SpecifytheName
{
public class User
{
DBTable Table1 = new DBTable("Table1");
// SPECIFY THE TYPE OF FIELD1 ONLY AT THIS POINT
Table1.AddField<int>("Field1"); //or something like this
public User()
{
}
}
}
I would generally solve this issue using a non-generic interface to store your fields.
So start with this interface:
public interface IDBField
{
IList RecordSet { get; }
Type FieldType { get; }
}
Now implement DBField<T> like this:
public class DBField<T> : IDBField
{
public List<T> RecordSet = new List<T>();
IList IDBField.RecordSet
{
get
{
return this.RecordSet;
}
}
Type IDBField.FieldType
{
get
{
return typeof(T);
}
}
}
Then you can implement DBTable like this:
public class DBTable
{
public List<IDBField> FieldName = new List<IDBField>();
public void AddField<F>(string Name)
{
FieldName.Add(new DBField<F>());
}
}
You can use the FieldType property on IDBField to determine the type of the field and then use reflection as necessary to use the values of RecordSet appropriately.
The only way I can see this working is by not using Generics, but just use the Object class.
for example:
public class DBTable
{
public List<DBField<Object>> FieldName = new List<DBField<Object>>();
public DBTable (string NameOfTable)
{
}
public void AddField(string Name)
{
List<DBField<Object>> TempList = new List<DBField<Object>>();
FieldName.Add(TempList);
}
}
This will mean you can use any type in the RecordSet object without restricting the type in the DBTable class.
I could be a bit off base here as I'm not sure what you're trying to achieve, for one you aren't going anything with the Name parameter passed into the AddField method, and you're TempList object isn't the same type as FieldName so it should throw some errors there..
EDIT:
I think I understand more clearly what you're trying to do, try this -
public class DBTable
{
public List<DBField<Object>> FieldName = new List<DBField<Object>>();
public DBTable (string NameOfTable)
{
}
public void AddField<FieldType>(string Name)
{
DBField<FieldType> field = new DBField<FieldType>(Name);
FieldName.Add(field);
}
}
This way each Field (Column) is still forced to a type, but the DBTable isn't tied down to that same type.
Related
I want to instantiate a generic list of objects like:
public static class TablesClass
{
private static IList<Tables<T>> TablesInstance { get; set; }
static TablesClass() => Tables = new List<Tables<T>>();
public static void AddTable(Table<t> table) => Tables.Add(table);
}
I can't change Tables<T>, this is a nuget package class.
How may i achieve this? All i have tried just does not work (setting a type T to class, using object instead T and casts - not desired solution).
Can somebody help me?
TablesClass is not a generic class and you are not telling the compiler what type T is supposed to be somewhere.
If you want to be able to add different kinds of objects into the same IList<Tables<T>> list, T must be a common base type for all these objects.
For example, if you want to be able to add apples, pears and bananas to the list, the type parameter T may be specified as Fruit provided that Fruit is the base class for all these types.
Obviously you will need to cast from Fruit if you want to be able to access any member of an item in the list that is specific to a concrete implementation of Fruit class but this is inevitable. You don't throw a bunch of different kinds of fruits into a single basket and expect to be able to always pick up a specific fruit, do you?
Your nuget class must be this style:
public abstract class Tables
{
}
//the Generic class must has a base, by which you can list them
public class Tables<T> : Tables // where T: something base class of your object
{
//...
}
then your class must be:
public static class TablesClass
{
//Search source code of your Nuget package, find its base class of Generic class, the list must be defined as its base
private static IList<Tables> Tables { get; set; }
static TablesClass()
{
Tables = new List<Tables>();
}
public static void AddTable(Tables table)
{
Tables.Add(table);
}
}
then you can use it like this:
public class Test
{
public static void Mains()
{
TablesClass.AddTable(new Tables<A>());
TablesClass.AddTable(new Tables<B>());
TablesClass.AddTable(new Tables<C>());
}
}
If you want to do this kind of thing you need to hold the references using object, but make a method that allows you to store and fetch each table using strong-typing.
Try a class like this:
public class Repository
{
private Dictionary<Type, Dictionary<string, object>> _store
= new Dictionary<Type, Dictionary<string, object>>();
public void Store<T>(string key, T value)
{
if (!_store.ContainsKey(typeof(T)))
{
_store.Add(typeof(T), new Dictionary<string, object>());
}
_store[typeof(T)][key] = value;
}
public T Fetch<T>(string key)
{
return (T)_store[typeof(T)][key];
}
public bool TryFetch<T>(string key, out T value)
{
var success = _store.ContainsKey(typeof(T)) && _store[typeof(T)].ContainsKey(key);
value = success ? this.Fetch<T>(key) : default(T);
return success;
}
public bool TryInject<T>(string key, Action<T> inject)
{
var success = this.TryFetch<T>(key, out T value);
if (success)
{
inject(value);
}
return success;
}
}
Then you can strongly-type the objects into the repository (collection) and strongly-type fetching them out like this:
var repository = new Repository();
repository.Store("a", new TableA());
repository.Store("b", new TableB());
repository.Store("c", new TableC());
repository.Store("d", new TableD());
/* Somewhere else in your code */
TableA a = repository.Fetch<TableA>("a");
TableB b = repository.Fetch<TableB>("b");
TableC c = repository.Fetch<TableC>("c");
TableD d = repository.Fetch<TableD>("d");
The key value (i.e. "a") is optional - you can remove it from the code - but it is useful if you need to store more that one object of a specific type.
Way much simple solution: List<dynamic>.
I have a casting issue and I was wondering if anyone would be able to help me see how I can fix this issue?
Basically I'm trying to create a CSV generator console program that will take a list of machine records from the DB and put them into a CSV.
The code below is simplified but generally similar to my implementation currently:
public interface IRecord
{
}
public class MachineRecord : IRecord
{
string Name {get;set;}
string ErrorCount {get;set;}
}
public interface IRecordStore
{
string GenerateRecord(IRecord record);
}
public class CSVMachineRecordStore : IRecordStore
{
public string GenerateRecord(IRecord record)
{
var machineRecord = (MachineRecord)record;
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
So basically here I've created a class that inherits from IRecordStore, and in our case we have to create an implementation of GenerateRecord. The problem here is I have to cast the record of be of type MachineRecord or the compiler will throw a casting error out, this is due to Record not having any properties set.
Is it possible to not have the direct cast in this implementation so this line of code will be like:
public class CSVMachineRecordStore : IRecordStore
{
public string GenerateRecord(IRecord record)
{
var machineRecord = record; //we want to remove the explicit cast
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
I know I can fix this issue by adding Name and ErrorCount properties to IRecord, but IRecord is a very generic interface that can have anything.
We want to be able to create a CSV program that can handle machine records, customer records, employee records, supplier records, etc...
I'm thinking of using generics, and I've played with them a bit but I've had some trouble getting it to work too.
Use generics:
public interface IRecordStore<T> where T:IRecord
{
string GenerateRecord(T record);
}
Then implement:
public class CSVMachineRecordStore : IRecordStore<MachineRecord>
{
public string GenerateRecord(MachineRecord record)
{
var machineRecord = record; //we want to remove the explicit cast
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
I wrote this in a hurry, make sure to check for silly errors.
The type casting exception occurs when the IRecord instance is not an instance of MachineRecord.
To answer your question how to fix the casting issue, you should have a look at the as operator.
However, for me this looks like making the IRecordStore interface generic could help you prevent the type issues you're experiencing:
[Edit] I noticed since IRecordStore is an interface you could even make the generic type contravariant. This allows you to assign a variable
of type IRecordStore<IRecord> with an instance of IRecordStore<MachineRecord> or more explicitly CSVMachineRecordStore.
public interface IRecordStore<in T> where T : IRecord
{
string GenerateRecord(T record);
}
public class CSVMachineRecordStore : IRecordStore<MachineRecord>
{
public string GenerateRecord(MachineRecord record)
{
var machineRecord = record;
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
I am using C# and I thought I finally had the chance to understand a Generic type. I have several strongly typed objects that need the same static method. Rather than create one static method for each type I thought I could make it generic. Something I have never done and really wanted too.
Here is where I invoke it.
bool isDuplicate = Utilities.GetDuplicates<RoomBookingModel>(roomBookings);
Here is my static method which resides in a static class called Utilities.
public static bool GetDuplicates<T>(List<T> pBookings)
{
foreach (var item in pBookings)
{
var myVal = item.bookingId
}
return true;
}
So I want to get at the values within var item inside the foreach loop so I can do comparisons. It's definately passed pBookings because I can hover and they have a .Count() with a collection of my strongly typed object. I am missing something here, possibly a casting process. I was wondering if anyone could advise me where I am coming up short.
var myVal = item.bookingId - I cannot get the bookingID from item because I am lacking in some basic understanding here. bookingId doesn't exist, I just get access to extension methods such as .toString and .equals
ANSWER OF SORTS What I did based on all of your really helpful assistance. I utilised Anderson Pimentel. I'm probably still off the mark but wanted to garner anyones thoughts here.
So basically I have several booking models, all need checking for duplicates. I really wanted to understand Generics in this way. So what I did is. Created a base class.
public class BookingBaseModel
{
public int BookingID { get; set; }
public DateTime BookingStartDateTime { get; set; }
public DateTime BookingEndDateTime { get; set; }
}
Then had my booking classes all inherit whats common to all. Like this...
public class RoomBookingModel : BookingBaseModel
{
public string RoomName{ get; set; }
}
public class vehicleBookingModel : BookingBaseModel
{
public string vehicleName{ get; set; }
}
Then in my utilities static helper I did this..
public static void GetDuplicates<T>(List<T> items) where T : BookingBaseModel
{
foreach (var item in items)
{
int myId = item.ID;
DateTime startDateTime = item.BookingStartDateTime;
DateTime endDateTime = item.BookingEndDateTime;
//Do you logic here
}
}
Then finally did something like this in corresponding controller action.
RoomController...
Utilities.GetDuplicates<RoomBookingModel>(roomBookings);
VehicleController....
Utilities.GetDuplicates<VehicleBookingModel>(vehicleBookings);
Is this basically how we go about using generics in this way?
The compiler has no hint of what type is T. If you have a base class (or an Interface) which has the bookingId attribute, like BaseModel, you can constrain the generic type like the following:
public class BaseModel
{
public int Id { get; set; }
}
public static bool GetDuplicates<T>(List<T> items) where T : BaseModel
{
foreach (var item in items)
{
var myId = item.Id;
//Do you logic here
}
return true;
}
Once you're inside your GetDuplicates method, you have lost all knowledge of the RoomBookingModel type. That's the point of generic methods - they should be able to act on whatever type has been passed in to them, e.g. the logic within them should be generic across any type.
So your foreach loop is fine - you know you've been given a list of something, and you know lists can be iterated. But inside that foreach, item is just a T. You don't know what actual type it is because any type could have been passed in. So it doesn't make sense to access a specific property or method off of item - for example, what if I called GetDuplicates passing in a List<int>? It wouldn't have a bookingId property.
As written by others, you don't know anything of T. A classical solution, used by LINQ (see for example GroupBy) is to have your method receive a delegate that does the key-extraction, like:
public static bool GetDuplicates<T, TKey>(List<T> pBookings, Func<T, TKey> selector)
{
foreach (var item in pBookings)
{
TKey key = selector(item);
}
return true;
}
You then use it like:
GetDuplicates(pBookings, p => p.bookingId);
If you like to use a generic method, you have to provide also a generic method, which is able to generate a key out of the specified type T. Luckily we have LINQ which already provides the needed parts to build your generic method:
internal class Extensions
{
public static IEnumerable<T> GetDuplicates<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
{
return source.GroupBy(keySelector)
.Where(group => group.Skip(1).Any())
.SelectMany(group => group);
}
public static bool ContainsDuplicates<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
{
return GetDuplicates(source, keySelector).Any();
}
}
By having this (and type inference) you can use these methods e.g. by calling:
var hasDuplicates = roomBookings.ContainsDuplicates(item => item.bookingId);
if(hasDuplicates)
{
Console.WriteLine("Duplicates found:");
foreach (var duplicate in roomBookings.GetDuplicates(item => item.bookingId))
{
Console.WriteLine(duplicate);
}
}
I wonder if generics is really the tool for the job here. Your needs would be better served if each of your strongly typed objects shared a common interface.
"I have several strongly typed objects that need the same static method."
In this situation, all of the classes must share a common feature, such as, for instance, a property BookingId.
So, you'd need to formalize this by extracting this common interface:
public interface IBooking
{
int BookingId{ get; }
}
Make sure that every one of your strongly typed items implements the interface:
public class RoomBooking : IBooking
{
//etc...
}
And now make your static method accept IBooking instances:
public static bool GetDuplicates(IEnumerable<IBooking> pBookings)
{
//does pBookings contain items with duplicate BookingId values?
return pBookings.GroupBy(b => b.BookingId).Any(g => g.Count() > 1);
}
An easy read that isn't obfuscated by the unnecessary use of generics.
Since there are no constraints or hints about what T is, the compiler does not have enough information. Consider
bool isDuplicate = Utilities.GetDuplicates<int>(roomBookings);
Clearly an int does not have a bookingId member.
Every possible specific type for T would have to have a common base class or interface that has a bookingId, and even then you would have to add a generic constraint to your method signature to access that.
Perhaps you are looking for something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Duplicates
{
public static class EnumerableExtensions
{
public static bool HasDuplicates<T, I>(this IEnumerable<T> enumerable, Func<T, I> identityGetter, IEqualityComparer<I> comparer )
{
var hashSet = new HashSet<I>(comparer);
foreach (var item in enumerable)
{
var identity = identityGetter(item);
if (hashSet.Contains(identity)) return true;
hashSet.Add(identity);
}
return false;
}
public static bool HasDuplicates<T, I>(this IEnumerable<T> enumerable, Func<T, I> identityGetter)
{
return enumerable.HasDuplicates(identityGetter, EqualityComparer<I>.Default);
}
}
public class Booking
{
public int BookingId { get; set; }
public string BookingName { get; set; }
}
public class Customer
{
public string CustomerId { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var bookings = new List<Booking>()
{
new Booking { BookingId = 1, BookingName = "Booking 1" },
new Booking { BookingId = 1, BookingName = "Booking 1" }
};
Console.WriteLine("Q: There are duplicate bookings?. A: {0}", bookings.HasDuplicates(x => x.BookingId));
var customers = new List<Customer>()
{
new Customer { CustomerId = "ALFKI", Name = "Alfred Kiss" },
new Customer { CustomerId = "ANATR", Name = "Ana Trorroja" }
};
Console.WriteLine("Q: There are duplicate customers?. A: {0} ", customers.HasDuplicates(x => x.CustomerId));
}
}
}
I have two custom types Customer and Employee which implement the interface ITablefy. This interface has only one method, GetPropertyList which returns a list of strings of the property names of the object that implements it. I have a web service which looks like:
public string ReturnPropertyNames(ITablefy i)
{
List<string> propList = new List<string>();
TableFactory factory = new TableFactory();
ITablefy table = factory.CreateTable(i);
propList = table.GetPropertyList(table);
return propList[1];
}
so in this example the Factory creates a concrete type that implements ITablefy
I realized when I had a problem when both of my classes Customer and Employee implemented their GetPropertyList methods exactly the same:
//property list is a private member variable in each class
public List<string> GetPropertyList(ITablefy i)
{
TableFactory factory = new TableFactory();
ITablefy table = factory.CreateTable(i);
foreach (var propInfo in table.GetType().GetProperties())
{
propertyList.Add(propInfo.Name);
}
return propertyList;
}
Rather than copy and paste that code I'm looking for a better solution to what I have currently. If I only want certain types to use the GetPropertyList method how can I control that without having to copy and paste this same code? Harcoding the type to create in each class doesn't seem like a good solution to me. Employee and Customer don't logically make sense to use inheritance either. What's a proper solution for something like this?
factory:
public class TableFactory
{
public ITablefy CreateTable(ITablefy i)
{
if (i is Employee)
{
return new Employee();
}
else if (i is Customer)
{
return new Customer();
}
else
{
return null;
}
}
}
public static List<string> GetPropertyNames(this Object o)
{
List<string> names = new List<string>
foreach (PropertyInfo prop in o.GetType().GetProperties())
names.Add(prop.Name);
return names;
}
Now you can implement ITablefy in terms of any object.GetPropertyNames() using the extension method above.
There are a few questions that comes to my mind:
If It's so easy to do generically, why are you even using the interface?
Shouldn't you be checking properties for public accessors?
Shouldn't your interface be returning a more general type like IEnumerable<string> or ICollection<string>?
Wouldn't the interface be better designed to filter out property names that you don't want? That way you could assume all public properties are part of the set except those that aren't.
You make the interface be something like:
public interface IPropertyInfoFilterProvider {
public Func<PropertyInfo, bool> PropertyInfoSkipFilter { get; set; }
}
or
public interface IPropertyNameFilterProvider {
public Func<string, bool> PropertyNameSkipFilter { get; set; }
}
and then you can initialize the default to (prop) => false.
so now you can harvest the property names automagically and in one place and let implementations determine what gets taken and what doesn't and your harvesting code could use that filter in a linq where clause.
You could make it an extension method on ITablefy.
Or a static method on ITablefy
I'm trying to figure out how I can make a Generics call take a variable for the Type. In the call below it take a type "DAL.Account" and works fine.
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
I want to change that so that I can pass a variable in place of the "DAL.Account". Something like this but I know that won't work as you can't pass property as a Type.
ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray())
Below is the shell pieces of code I think explains what I'm trying to do. Generics is not my strong suit so I'm looking for some help. Is there anyway that I can make this happen?
//Stores a "Type" that indicates what Object is a Criteria for.
public class AccountCriteria : IGeneratedCriteria
{
...
public Type EntityType
{
get {return typeof(DAL.Account);}
}
}
//I have added a function to the DataContext called "GetTable"
// And then used it as an example in a Console App to test its functionality.
public class ADRPDataContext : NHibernateDataContext
{
...
public CodeSmith.Data.NHibernate.ITable<T> GetTable<T>() where T : EntityBase
{
var tb = new CodeSmith.Data.NHibernate.Table<T>(this);
return tb;
}
}
// console application that uses DataContext.GetTable
class Program
{
static void Main(string[] args)
{
using (var ctx = new ADRPDataContext())
{
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
}
}
}
//ExistsCommand class that uses the EntityType property of the Critera to generate the data.
public class ExistsCommand
{
private IGeneratedCriteria Criteria { get; set; }
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
//This was my first attempt but doesn't work becuase you can't pass a property in for a Type.
//But I can figure out how to write this so that it will work.
Result = ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
You are looking to instantiate a generic type. Some info can be found here
This is a simple example demonstrating how to instantiate a List with a capacity of 3. Here is a method that you can call to create a generic when you don't know the type:
public static Object CreateGenericListOfType(Type typeGenericWillBe)
{
//alternative to the followin:
//List<String> myList = new List<String>(3);
//build parameters for the generic's constructor (obviously this code wouldn't work if you had different constructors for each potential type)
object[] constructorArgs = new Object[1];
constructorArgs[0] = 3;
//instantiate the generic. Same as calling the one line example (commented out) above. Results in a List<String> with 3 list items
Type genericListType = typeof(List<>);
Type[] typeArgs = { typeGenericWillBe };
Type myNewGeneric = genericListType.MakeGenericType(typeArgs);
object GenericOfType = Activator.CreateInstance(myNewGeneric, constructorArgs);
return GenericOfType;
}
And here is some sample code that will show you the example method works:
List<String> Strings = (List<String>)InstantiateGenericTypeWithReflection.CreateGenericListOfType(typeof(String));
//demonstrate the object is actually a List<String> and we can do stuff like use linq extensions (isn't a good use of linq but serves as example)
Strings.Add("frist");
Strings.Add("2nd");
Strings.Add("tird");
Console.WriteLine("item index 2 value: " + Strings.Where(strings => strings == "2").First());
In your example, replace your GetTable<Criteria.EntityType>() with CreateGenericTableOfType(Criteria.EntityType). This will return a generic table of whatever type you pass in. You will of course need to implement the method properly (handle constructor args, change List to Table etc).
I think you need to change the way you're doing this slightly, and instead use generics instead of the EntityType property. Perhaps something along the lines of the following:
// Create an abstract class to be used as the base for classes that are supported by
// ExistsCommand and any other classes where you need a similar pattern
public abstract class ExtendedCriteria<T> : IGeneratedCriteria
{
public ExistsCommand GetExistsCommand()
{
return new ExistsCommand<T>(this);
}
}
// Make the non-generic ExistsCommand abstract
public abstract class ExistsCommand
{
protected abstract void DataPortal_Execute();
}
// Create a generic sub-class of ExistsCommand with the type parameter used in the GetTable call
// where you were previously trying to use the EntityType property
public class ExistsCommand<T> : ExistsCommand
{
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
Result = ctx.GetTable<T>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
// Derive the AccountCriteria from ExtendedCriteria<T> with T the entity type
public class AccountCriteria : ExtendedCriteria<DAL.Account>
{
...
}