I am wrapping up a generically-crafted SQL table editor for a configuration application, using Entity Framework, a Repository pattern, so on and so forth. Probably a bit overkill on the layers. In any case, I have a generic MVC controller (we'll call it MyController<T>) looking to send its calls to a generic service layer (MyServices<T>), where everything "of T" is some data model class representing a SQL table, directly. Each and every data model class has an ident field called "Id", that is implemented from a base model (that itself, is implementing an interface).
Everything is very simple and smooth, except when I need to call the FindBy option on a data set, where i have to provide a delegate to search on something of a generic type. I understand that the type constraints need to have an interface attached to it so that I can access that "Id" field in any type T, but doing so causes conflict with any controllers that explicitly implement the generic controller. So:
Generic Service layer:
public abstract class MyServices<T> : IMyService<T> where T : class, new()
{
IMyRepository<T> _MyRepository;
public MyServices(IMyRepository<T> MyRepository)
{
_MyRepository = MyRepository;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return _MyRepository.FindBy(predicate);
}
}
Generic Repository:
public partial class MyRepository<T> : IMyRepository<T> where T : class, new()
{
MyTablesEntities _entities = new MyTablesEntities();
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
}
Generic Controller:
public class MyController<T> : Controller where T : class, new()
{
private string ViewTitle = typeof(T).ToString();
private readonly IMyServices<T> _MyServices = default(IMyServices<T>);
public MyController() { }
public MyController(IMyServices<T> mtt)
{
_MyServices = mtt;
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
T editItem = _MyServices.FindBy(c => **c.Id == id**).SingleOrDefault();
if (editItem == null)
{
return HttpNotFound();
}
return PartialView(editItem);
}
}
and a Controller using MyController:
public class AccountsController : MTTablesController<Accounts>
{ }
Obviously, with this setup, the FindBy delegate clause cannot differentiate the type of T until runtime, so it fails to compile this way, giving:
'T' does not contain a definition for 'Id' and no extension method 'Id' accepting a first argument of type 'T' could be found
If, however, I add the interface (IMyTableEntity) that contains a property of 'Id' to the constraint of the generic controller:
public class MyController<T> : Controller where T : class, MyTables.DataAccess.Metadata.Base.IMyTableEntity, new()
public interface IMyTableEntity
{
int Id { get; set; }
}
I now get THIS error, over on the implementing Controller:
The type 'MyTables.DataAccess.Account' cannot be used as type parameter 'T' in the generic type or method 'MyController<T>'. There is no implicit reference conversion from 'MyTables.DataAccess.Account' to 'MyTables.DataAccess.Metadata.Base.IMyTableEntity'.
I am at an impasse here as both errors lead me back into trying the other way, neither of which are working. I need the generic 'T' on the generic controller to be smart enough to carry the Id field with it, without confusing the implementing controllers above it.
trailmax may have found it...
Your class 'MyTables.DataAccess.Account' must implement IMyTableEntity
My Data modelling is set up in this way:
EF base partial classes:
public partial class Accounts {
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
}
Metadata models that contain data Annotations:
public class AccountsMetadata : MyTables.DataAccess.Metadata.Base.MyTableEntity {
[Key]
[Required]
[UniqueValidator]
public string Code { get; set; }
[Display(Name="Description")]
[StringLength(100)]
public string Description { get; set; }
}
a bootstrap that ties them together
[MetadataType(typeof(AccountsMetadata))]
public partial class Accounts { }
what i needed to do was add the implementation of the interface to the bootstrap definitions for every partial, i.e.:
[MetadataType(typeof(AccountsMetadata))]
public partial class Accounts : MyTables.DataAccess.Metadata.Base.IMyTableEntity { }
and then follow that up by setting the IMyTableEntity interface as a constraint on T in the Generic Controller, as i outlined above...
Related
public class DS2DbContext : DbContext
{
public DbSet<DocumentFileData> DocumentFileData { get; set; }
public DbSet<WatermarkFileData> WatermarkFileData { get; set; }
public DS2DbContext(DbContextOptions<DS2DbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class FileDataController<T> : ODataController where T : class
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS1061: 'T' does not contain a definition for 'ID'
// and no accessible extension method 'ID' accepting a
// first argument of type 'T' could be found
return SingleResult.Create(result);
}
}
How do I properly (and if possible, elegantly) access members of variables with type T and get their values?
Update
When I use an interface like this:
public class FileDataController<T> : ODataController where T : IFileData
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS0452: The type 'T' must be a reference type in
// order to use it as parameter 'TEntity' in the generic
// type or method 'DbSet<TEntity>'
return SingleResult.Create(result);
}
}
I am getting an error when calling _context.Set().
What I have come up with is
var result = _context.Set<T>()
.Where(x => (Guid) x.GetType().GetProperty("ID").GetValue(x) == ID);
But this looks horribly complicated.
The story behind this
is that I have to store data of the exact same structure in two different database table depending on the data's semantics (requirements document or watermark). Since I am doing Blazor, code first, I need to have two different classes for this to make it correctly create two tables and handle them via two different controllers. However, these controllers share the exact same code and the exact same underlying data structure. Here is a simplified example just implementing an ID:
public interface IFileData
{
public Guid ID { get; set; }
}
using System.ComponentModel.DataAnnotations;
public class FileData : IFileData
{
[Key]
public Guid ID { get; set; } = Guid.Empty;
}
public class DocumentFileData : FileData { }
public class WatermarkFileData : FileData { }
Consequently, there is a controller for DocumentFileData and one for WatermarkFileData to access the proper database table for each type of data. However, all underlying operations are identical. So I hope to able to solve this via a generic class to save me the trouble of having to make identical changes to two different classes everytime something in file data handling changes.
Since you have an common interface exposing ID property provide corresponding generic constraint - (you need to specify both generic type constraints - interface and class):
public class FileDataController<T> : ODataController where T : class, IFileData
As #GuruSiton correctly commented, you should introduce an interface with ID in it:
interface IClassWithId
{
Guid ID { get; } // Notice that you only require get - the implementer can choose how the ID get init'd - private / c'tor / etc
}
public class FileDataController<T> : ODataController where T : IClassWithId
{ ... }
This assumes that you can make - or require - all classes used as T to implement IClassWithId. If you're stuck with existing classes that define their own ID and can't change them, then you must resort to reflection, as hinted to by #NaeemAhmed.
I have a database with 3 tables for classes that all implement my IMeetingEntityEntityBaseClass.cs
public class BookingAppDbContext : DbContext
{
public BookingAppDbContext(DbContextOptions<BookingAppDbContext> options) : base(options)
{
}
public DbSet<MeetingSpace> MeetingSpaces { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Booking> Bookings { get; set; }
}
I started off with only the MeetingSpaces table and was trying to make my database operation methods generic so they could be reused for the other two tables. However they (and my ASP.NET pages) rely on the Id to identify the meeting space. My problem is that I cant figure out how to make the GetById class generic. For the MeetingSpace table query it is as follows:
public MeetingSpace GetById(int id)
{
return db.MeetingSpaces.Find(id);
}
I attempted to use the Find overload with the type specified but the typeof(T) returns 'object', throwing an error that type 'object' cannot be converted to 'T'.
public T GetById<T>(int id) where T : IMeetingEntityBaseClass
{
return db.Find(typeof(T), id);
}
You need to use the DbContext.Set method. Also, you need to add a type constraint since the Set method relies in it (as do all EF entities.) For example:
public T GetById<T>(int id)
where T : class, IMeetingEntityBaseClass
{
return db.Set<T>().Find(id);
}
I have .net core rest api, which contains hybrid structure in which it only contains repositories and not the service layer.
Now, there is one issue that I am facing with base repository and main structure. let me explain the issue first.
So, consider one entity. let's say Product and below is the definition for that entity. this entity has one base class called FullAuditedEntity.
[Table(name: "Products")]
public class Product : FullAuditedEntity
{
public string Name { get; set; }
}
public class FullAuditedEntity: IFullAuditedEntity
{
public FullAuditedEntity() { }
[Key]
public virtual int Id { get; set; }
}
public interface IFullAuditedEntity
{
int Id { get; set; }
}
The Base repository and it's interfaces are as below.
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
private readonly ApplicationContext context;
public EntityBaseRepository(ApplicationContext context)
{
this.context = context;
}
public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);
public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
}
public interface IEntityBaseRepository<T> where T : class, new()
{
IEnumerable<T> items { get; }
T GetSingle(int id);
}
So, my Product repository will be like this.
public interface IProductRepository : IEntityBaseRepository<Product> { }
public class ProductRepository : EntityBaseRepository<Product>, IProductRepository
{
private readonly ApplicationContext context;
public ProductRepository(ApplicationContext context) : base(context: context)
{
this.context = context;
}
}
Now, up-to here everything is good, I can access this repository in controllers and can perform the actions that are listed in base class.
Issue I am facing : So with this structure, If I tries to add any new entity without FullAuditedEntity (see Product entity above, I have base class full audited entity over there), my structure of repository fails and it gives error.
let's say if I tries to add new entity Implementation, and this new entity has a random Id, so I do not want to inherit the FullAuditedEnitity base class. now in this case most of the thing will work fine but when I will try to create repository for Implementation entity, it will give generic error. see below snap of that.
What I tried so far...
I was thinking to create a parallel Base repository which does not inherit FullAuditedEntity as a generic class but I am not sure if it's best practice or not. also my concern is that what if I am doing any mistake in my current structure of repository pattern and Dependency injection?
Any help world be best and really appreciated.
Thank you in advance for your time.
Repositories are usually mapped to database tables. Database table should always have some column which can uniquely identify the row in table and it is common practice to call this column as Id. So you correctly implemented your FullAuditedEntity as there is Id property. However, your Id has always type of int. I suggest you to use the following construction and then your Id would be any type of struct such as int, decimal, Guid, and etc:
/// <summary>
/// Abstraction of the Entity
/// </summary>
public interface IEntity
{
object Id { get; set; }
}
/// <summary>
/// Base class for IDs
/// </summary>
public abstract class Entity<T>: IEntity where T: struct
{
public T Id { get; set; }
object IEntity.Id
{
get { return Id; }
set {
Id = (T)value;
}
}
}
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntity, new()
{
// The code is omitted for the brevity
}
In addition, try to avoid entities without Id like Implementation as in future you will have to figure out how to find rows without Id in your database table.
UPDATE:
If you do not want to inherit FullAuditedEntity, then you can create BaseRepository<T> and then derive it in the EntityBaseRepository.
public abstract class BaseRepository<T> : IEntityBaseRepository<T> where T : class, new()
{
public virtual IEnumerable<T> items => throw new NotImplementedException();
public virtual T GetSingle(int id)
{
throw new NotImplementedException();
}
}
public class EntityBaseRepository<T> : BaseRepository<T> where T : class
, IFullAuditedEntity, new()
{
public override IEnumerable<T> items => base.items;
public override T GetSingle(int id)
{
return base.GetSingle(id);
}
}
and then your Implementation repository:
public interface IImplementationRepository : IEntityBaseRepository<Implementation> { }
public class ImplementationRepository: BaseRepository<Implementation>
, IImplementationRepository
{
public override Implementation GetSingle(int id)
{
return base.GetSingle(id);
}
}
UPDATE 1:
In my view, it is better to use services(Service layer) which consume ITRepository<T>.
Because it gives to you new abilities such as:
Add some calculations on data which got by Repository
Remap your entities which are pulled by the Repository
It is one additional layer of decoupling. So when you edit your service layer, then you don't need to edit Repository layer and then recompile your assembly
I have two kinds of base classes:
public class Parent { }
public abstract class Child : Parent
{
string ChildKey { get; set; }
}
Derived from Parent, there are many kids:
public class Kid1 : Parent { public string Name { get; set; } }
public class Kid2 : Parent { public long Number { get; set; } }
...
and also many Children as a special group of Childs with extra properties:
public class Child1 : Child { public string Street { get; set; } }
public class Child2 : Child { public long Account { get; set; }}
Now I have two generic repository classes where the "Special One" acts more specific on the extra properties by using an additional filter:
public class Repository<T> : IRepository<T> where T : Parent
{
public IEnumerable<T> GetAll() { return something; }
}
public class ChildRepository<T> : Repository<T>, IChildrenRepository<T> where T : Child
{
public override IEnumerable<T> GetAll() { return base.GetAll().Where(x => x.ChildKey == "y"); }
}
with the interfaces:
public interface IRepository<T> where T : Parent
{ IEnumerable<T> GetAll(); }
public interface IChildRepository<T> : IRepository<T> where T : Child { }
I also need the type safety of the GetAll()-results.
Now I need a generic method to create the desired repository:
IRepository<T> GetRepository() where T : WhatConstraint
{
if (typeof(Child).IsAssignableFrom(T))
return new ChildRepository<T>(); // return 1
return new Repository<T>(); // return 2
}
What is the correct constraint? return 1 needs Child-Constraint (which is wrong for return 2), saying that Type T cannot be used as type parameter in method since there is no implicit reference conversion from T to Child.
The T : Child-constraint is more precise in ChildRepository (and therefore useful, since I can rely on some properties). If I use the same T : Parent-constraint of the Repository, I have to type-check whether T is derived from Child all the times...
Are there any solutions to this?
Okay, here is a detailed solution (which can be written shorter as well as less readable). Since Repository and ChildRepository have conflicting constraints (which is good for the repositories, but bad for GetRepository-factory), I cannot create the ChildRepository using new-keyword. I have to create this object via CreateInstance.
IRepository<T> GetRepository() where T : Parent
{
if (typeof(Child).IsAssignableFrom(T))
{
Type childType = typeof(T); // which is both, Parent and Child
Type classType = typeof(ChildRepository<>);
Type[] typeParams = { childType };
Type repositoryType = classType.MakeGenericType(typeParams);
return Activator.CreateInstance(resultType) as IRepository<T>;
}
return new Repository<T>();
}
Downside of this solution: More complex code analysis, unclear nullability of result, not really intuitive readable (especially existing constraints). But it works.
I have completely re-written this hoping to make my question clearer. I have chosen the concept of services making use of repositories in my example code.
Example code:
class Program
{
interface IEntity
{
int Id { get; set; }
}
// Example entity could be:
class Book : IEntity
{
public int Id { get; set; }
}
class Magazine : IEntity
{
public int Id { get; set; }
}
interface IRepository<TEntity> where TEntity : class, IEntity
{
IEnumerable<TEntity> GetEntities();
}
interface IBooksRepository : IRepository<Book> { }
interface IMagazineRepository : IRepository<Magazine> { }
class DataStore<TEntity> where TEntity: class, IEntity
{
public IEnumerable<TEntity> GetFromStore()
{
throw new NotImplementedException();
}
}
abstract class RepositoryBase<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
DataStore<TEntity> _dataStore;
public RepositoryBase()
{
_dataStore = new DataStore<TEntity>();
}
public IEnumerable<TEntity> GetEntities()
{
return _dataStore.GetFromStore();
}
}
class BookRepository : RepositoryBase<Book>, IBooksRepository { }
class MagazineRepository : RepositoryBase<Magazine>, IMagazineRepository { }
abstract class ServiceBase<IEntityRepository, TEntity>
where IEntityRepository : IRepository<TEntity>
where TEntity : class, IEntity
{
IEntityRepository _repository;
public ServiceBase(IEntityRepository repository)
{
_repository = repository;
}
public IEnumerable<TEntity> GetEntitiesFromRepository()
{
return new List<TEntity>();
}
}
class BookService : ServiceBase<IBooksRepository, Book>
{
public BookService(IBooksRepository bookRepository)
: base(bookRepository)
{ }
}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>
{
public MagazineService(IMagazineRepository magazineRepository)
: base(magazineRepository)
{ }
}
static void Main(string[] args)
{
var aBookService = new BookService(new BookRepository());
var aMagazineService = new MagazineService(new MagazineRepository());
var books = aBookService.GetEntitiesFromRepository();
var magazines = aMagazineService.GetEntitiesFromRepository();
}
}
This all works fine and perhaps it is valid to ask why I want to change this. Mainly I am just curious if I can make this more neat. It is more a point of curiosity that one of functional correctness I suppose.
Both IBookRepository and IMagazineRepository know which concreate type they represent 'Book' and 'Magazine'
When I define my concreate services: BookService and MagazineService I have to specify the type as well as the interface:
class BookService : ServiceBase<IBooksRepository, Book>{}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>{}
I wondered if I could simplify thier signatures as the Interfaces already know The type I am expecting Book or Magazine.
Can I extract the Entity Type from the inteface such that I no longer need to specify the type when creating concreate service types?
As I pondered this, I discovered a deeper issue with my knowledge of C#:
What exactly is the type of 'thing' that the generic system is looking for between those angle brackets: IEnumerable<TThisThing>.
When I look at intellisense is says T is the type of objects to enumerate.
So as an experiment I grabbed the type of MyType:
Type typeOfMyType = instanceOfMyType.GetType();
IEnumerable<typeOfMyType> enumerable = new List<typeOfMyType>(); //crude example.
Now of course this does not work. So what kind of thing is TThisThing that works between the angle brackets?
is there a way of extracting this information so that I can forgo the
inclusion of 'MyType' in the class definition and use the discovered
TMyType in the example method?
Yes, you simply need to define the generic type parameter in the methods name:
public IEnumerable<TMyType> GetMyTypes<TMyType>()
{
// get list of TMyType instances;
return list;
}
If you don't want to use a generic type parameter at all, you'll have to defer to reflection, and you won't be able to use a compile-time generic type such as returning an IEnumerable<T>.
So what kind of thing is TThisThing that works between the angle
brackets?
TThisThing should be a compile-time known type parameter. When you use Type typeOfMyType = instanceOfMyType.GetType();, the type of instanceOfMyType is only known at run-time.
For example:
var obj = new SomeClass<Foo>();
IEnumerable<Bar> bars = obj.GetMyTypes<Bar>();
Where Foo and Bar:
public class Foo { }
public class Bar { }