C # LINQ to SQL: refactorizando este método genérico GetByID

Escribí el siguiente método.

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);
}

Básicamente es un método en una clase genérica dondeT es una clase en un DataContext.

El método obtiene la tabla del tipo de T GetTable) y comprueba la primera propiedad (siempre siendo la ID) del parámetro ingresado.

l problema con esto es que primero tuve que convertir la tabla de elementos en una lista para ejecutar unaGetType en la propiedad, pero esto no es muy conveniente porque todos los elementos de la tabla deben enumerarse y convertirse enList.

¿Cómo puedo refactorizar este método para evitar unToList en toda la mesa?

[Actualizar

La razón por la que no puedo ejecutar laWhere directamente en la mesa es porque recibo esta excepción:

Method 'System.Reflection.PropertyInfo [] GetProperties ()' no tiene traducción compatible a SQL.

PorqueGetProperties no se puede traducir a SQL.

[Actualizar

lgunas personas han sugerido usar una interfaz paraT, pero el problema es que laTl parámetro @ será una clase que se generará automáticamente en [DataContextName] .designer.cs, y por lo tanto no puedo hacer que implemente una interfaz (y no es factible implementar las interfaces para todas estas "clases de base de datos" de LINQ; y además, el archivo se regenerará una vez que agregue nuevas tablas al DataContext, perdiendo así todo el datos escritos).

Entonces, tiene que haber una mejor manera de hacer esto ...

[Actualizar

He implementado mi código comoNeil Williams 'sugerencia, pero todavía tengo problemas. Aquí hay extractos del código:

Interfaz

public interface IHasID
{
    int ID { get; set; }
}

DataContext [Ver código]:

namespace MusicRepo_DataContext
{
    partial class Artist : IHasID
    {
        public int ID
        {
            get { return ArtistID; }
            set { throw new System.NotImplementedException(); }
        }
    }
}

Método genérico:

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));
    }
}

La excepción se está lanzando en esta línea:return table.SingleOrDefault(e => e.ID.Equals(id)); y la excepción es:

System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.

[Actualización] Solución:

Con la ayuda deDenis Trollera respuesta publicada de @ y el enlace a la publicación enCode Rant blog, Finalmente logré encontrar una solución:

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();
}

Respuestas a la pregunta(6)

Su respuesta a la pregunta