I wrote the following method.
public T GetByID(int id)
{
var dbcontext = DB;
var table = dbcontext.GetTable<T>();
return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}
Basically it's a method in a Generic class where T
is a class in a DataContext.
The method gets the table from the type of T (GetTable
) and checks for the first property (always being the ID) to the inputted parameter.
The problem with this is I had to convert the table of elements to a list first to execute a GetType
on the property, but this is not very convenient because all the elements of the table have to be enumerated and converted to a List
.
How can I refactor this method to avoid a ToList
on the whole table?
[Update]
The reason I can't execute the Where
directly on the table is because I receive this exception:
Method 'System.Reflection.PropertyInfo[] GetProperties()' has no supported translation to SQL.
Because GetProperties
can't be translated to SQL.
[Update]
Some people have suggested using an interface for T, but the problem is that the T
parameter will be a class that is auto generated in [DataContextName].designer.cs, and thus I cannot make it implement an interface (and it's not feasible implementing the interfaces for all these "database classes" of LINQ; and also, the file will be regenerated once I add new tables to the DataContext, thus loosing all the written data).
So, there has to be a better way to do this...
[Update]
I have now implemented my code like Neil Williams' suggestion, but I'm still having problems. Here are excerpts of the code:
Interface:
public interface IHasID
{
int ID { get; set; }
}
DataContext [View Code]:
namespace MusicRepo_DataContext
{
partial class Artist : IHasID
{
public int ID
{
get { return ArtistID; }
set { throw new System.NotImplementedException(); }
}
}
}
Generic Method:
public class DBAccess<T> where T : class, IHasID,new()
{
public T GetByID(int id)
{
var dbcontext = DB;
var table = dbcontext.GetTable<T>();
return table.SingleOrDefault(e => e.ID.Equals(id));
}
}
The exception is being thrown on this line: return table.SingleOrDefault(e => e.ID.Equals(id));
and the exception is:
System.NotSupportedException: The
member
'MusicRepo_DataContext.IHasID.ID' has
no supported translation to SQL.
[Update] Solution:
With the help of Denis Troller's posted answer and the link to the post at the Code Rant blog, I finally managed to find a solution:
public static PropertyInfo GetPrimaryKey(this Type entityType)
{
foreach (PropertyInfo property in entityType.GetProperties())
{
ColumnAttribute[] attributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attributes.Length == 1)
{
ColumnAttribute columnAttribute = attributes[0];
if (columnAttribute.IsPrimaryKey)
{
if (property.PropertyType != typeof(int))
{
throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int",
property.Name, entityType));
}
return property;
}
}
}
throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name));
}
public T GetByID(int id)
{
var dbcontext = DB;
var itemParameter = Expression.Parameter(typeof (T), "item");
var whereExpression = Expression.Lambda<Func<T, bool>>
(
Expression.Equal(
Expression.Property(
itemParameter,
typeof (T).GetPrimaryKey().Name
),
Expression.Constant(id)
),
new[] {itemParameter}
);
return dbcontext.GetTable<T>().Where(whereExpression).Single();
}
See Question&Answers more detail:
os