Marko Marko - 28 days ago 20
C# Question

C# generics for DbSet<something>

I have various methods that should be able to call a generic method passing DbSet as a parameter. For the moment I have this working code (shown here simplified):

public class MyClass
{
private async Task<JsonResult> Delete<T>(int? id, T dbs) where T : DbSet<A>
{
var row = await dbs.SingleOrDefaultAsync(m => m.ID == id);
dbs.Remove(row);
await db.SaveChangesAsync();
return Json(r); // definition od "r" removed from code to simplify it
}

public async Task<JsonResult> A(int? id)
{
return await Delete(id, db.A);
}

public async Task<JsonResult> B(int? id)
{
return await Delete(id, db.B);
}

public async Task<JsonResult> C(int? id)
{
return await Delete(id, db.C);
}


I don't know hot to modify the "Delete" method's signature so that methods A,B,C... can all call it.

Evk Evk
Answer

Since you reference ID field in your Delete, you need to introduce some interface, like

interface IHasID {
    int ID { get; }
}

Then you need to implement that interface (in partial class) for all entities with ID. Then you can change your Delete like this

private async Task<JsonResult> Delete<T>(int? id, DbSet<T> dbs) where T : class, IHasID {            
    var row = await dbs.SingleOrDefaultAsync(m => m.ID == id);
    dbs.Remove(row);
    await db.SaveChangesAsync();
    return Json(r); // definition od "r" removed from code to simplify it
}

And use like this (assuming A implements IHasID):

public async Task<JsonResult> A(int? id) {
   return await Delete(id, db.As);
}

If you don't want to implement IHasID for every entity (though it's very easy, since ID property is already there at your entities), it's possible to do without interface (though less safe because you will be able to pass any entity to this Delete method and you won't find the error until run time):

private async Task<JsonResult> Delete<T>(int? id, DbSet<T> dbs) where T : class
{
    var parameter = Expression.Parameter(typeof(T), "entity");
    // generate expression entity => entity.ID = id
    var condition = (Expression<Func<T, bool>>) Expression.Lambda(
            Expression.Equal(
                Expression.Property(parameter, "ID"),
                Expression.Constant(id)
            )
        , parameter);

    var row = await dbs.SingleOrDefaultAsync(condition);
    dbs.Remove(row);
    await db.SaveChangesAsync();
    return Json(r);
}