How to pass DbSet as generic parameter? - c#

I have a custom validator for FluentValidation to validate whether Guid exist in the Database.
public static class TestCustomValidator
{
public static IRuleBuilderOptions<T, Guid> MustExistInDatabase<T>(this IRuleBuilder<T, Guid> ruleBuilder, ApplicationContext context)
return ruleBuilder.Must(id => context.Product.Find(id) != null).WithMessage("'{PropertyName}' {PropertyValue} not found.");
}
Then I will call it like this
RuleFor(r => r.ProductId).NotEmpty().MustExistInDatabase(context);
However, this part context.Product.Find(id) != null is currently only for Product and I have a lot of tables so I was hoping I could do something like
RuleFor(r => r.ProductId).NotEmpty().MustExistInDatabase<Product>();
RuleFor(r => r.CustomerId).NotEmpty().MustExistInDatabase<Customer>();
And it would automatically look for the Product or Customer table. Any idea how to do this?

You can define a base class as follows and inherit entities from it
public abstract class BaseEntity
{
public Guid Id { get; set; }
}
public class Product : BaseEntity
{
//other properties
}
public class Customer : BaseEntity
{
//other properties
}
Then change your extension method as below
public static IRuleBuilderOptions<T, Guid> MustExistInDatabase<T>(this IRuleBuilder<T, Guid> ruleBuilder, ApplicationContext context) where T : BaseEntity
{
return ruleBuilder.Must(id => context.Set<T>().Find(id) != null).WithMessage("'{PropertyName}' {PropertyValue} not found.");
}
and use as follows :
RuleFor(r => r.ProductId).NotEmpty().MustExistInDatabase<Product>(dbContext);
RuleFor(r => r.CustomerId).NotEmpty().MustExistInDatabase<Customer>(dbContext);

Related

EF Core Fluent API Configurations with Inheritance

EF CORE Fluent Api Configuration in separate files are Working fine with simple classes Ref #1 && Ref # 2. The problem comes when entities are Inherited from KeyedEntity or AuditableEntity
class abstract KeyedEntity<TValue> {
public TValue Id {get; set;}
}
class abstract AuditableEntity<TValue> : KeyedEntityBase<TValue>{
public DateTime DateCreated {get; set;}
public DateTime DateModified {get; set;}
}
Mapper Goes Something like this
public class KeyedEntityMap<TEntity, TId>
: IEntityTypeConfiguration<TEntity> where TEntity
: KeyedEntityBase<TId> where TId : struct
{
public void Configure(EntityTypeBuilder<TEntity> builder)
{
// Primary Key
builder.HasKey(t => t.Id);
// Properties
builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
}
}
public class AuditableEntityMap<TEntity, TId>
: IEntityTypeConfiguration<TEntity> where TEntity
: AuditableEntity<TId> where TId : struct
{
public void Configure(EntityTypeBuilder<TEntity> builder)
{
// Properties
builder.Property(t => t.DateCreated).HasColumnName("DateCreated");
builder.Property(t => t.DateModified).HasColumnName("DateModified");
}
}
Now the Problem Occurs with the Entity that inherits from AuditableEntity. I need to register Map from that Particular Enitity class along with AuditableEntityMap class and KeyedEntityMap class.
Now I can either forget about Map Inheritance and merge all the complex inheritance Maps in the entity class, which I don't want to do and respect DRY . The problem with complex inheritance is its not registering my entity maps
There are several ways you can achieve DRY for base entity configuration.
Bit the closest to your current design is to simply follow the entity hierarchy in the configuration classes:
public class KeyedEntityMap<TEntity, TId> : IEntityTypeConfiguration<TEntity>
where TEntity : KeyedEntityBase<TId>
where TId : struct
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
// ^^^
{
// Primary Key
builder.HasKey(t => t.Id);
// Properties
builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
}
}
public class AuditableEntityMap<TEntity, TId> : KeyedEntityMap<TEntity, TId>
// ^^^
where TEntity : AuditableEntity<TId>
where TId : struct
{
public override void Configure(EntityTypeBuilder<TEntity> builder)
// ^^^
{
base.Configure(builder); // <<<
// Properties
builder.Property(t => t.DateCreated).HasColumnName("DateCreated");
builder.Property(t => t.DateModified).HasColumnName("DateModified");
}
}
and then for specific entity that needs additional configuration:
public class Person : AuditableEntity<int>
{
public string Name { get; set; }
}
you would register
public class PersonEntityMap : AuditableEntityMap<Person, int>
{
public override void Configure(EntityTypeBuilder<Person> builder)
{
base.Configure(builder);
// Properties
builder.Property(t => t.Name).IsRequired();
// etc...
}
}

How to access a method in the context through unit of work?

If I have the following Context :
public partial class HRMainDataCTX : DbContext
{
public HRMainDataCTX()
: base("name=HRMainDataCTX")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
//DbSets
public virtual int SEARCHEMPLOYEE(Nullable<decimal> p_EMP_NUM, string p_EMP_NAME)
{
var p_EMP_NUMParameter = p_EMP_NUM.HasValue ?
new ObjectParameter("P_EMP_NUM", p_EMP_NUM) :
new ObjectParameter("P_EMP_NUM", typeof(decimal));
var p_EMP_NAMEParameter = p_EMP_NAME != null ?
new ObjectParameter("P_EMP_NAME", p_EMP_NAME) :
new ObjectParameter("P_EMP_NAME", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("SEARCHEMPLOYEE", p_EMP_NUMParameter, p_EMP_NAMEParameter);
}
}
Now i implement Unit of work like this :
public class HRCTX : IDisposable
{
private readonly HRMainDataCTX _context;
public HRCTX()
{
_context = new HRMainDataCTX();
}
public HRCTX(HRMainDataCTX context)
{
_context = context;
}
public int Save()
{
return _context.SaveChanges();
}
public HRMainDataCTX Context
{
get { return _context; }
}
public void Dispose()
{
_context.Dispose();
}
}
I don't know how to access the method (stored procedure) SEARCHEMPLOYEE through UOW in my code behind.
Well, in your case you would simply add another "Proxy-Method" for this method to your HRCTX proxy / UOW class, or - since HRCTX provides access to its underlying context - call it directly on the context like this:
HRCTX uow = new HRCTX(someContext);
uow.Context.SEARCHEMPLOYEE(123, "123");
But I also wanted to emphasize that the DbContext already represents a Unit of Work pattern (combined with a Repository pattern, see here). You are basically creating a proxy for your context, which - as far as I can see in this example - adds no further benefits or functionality, so I'd suggest to at least think about directly using your HRMainDataCTX and possibly getting rid of the HRCTX class.
You may need to implement repositories along with your Unit Of work pattern if you want to encapsulate your DbContext and your business logic. (As suggested in the AspNet guidelines)
In a generic manner, your unit of work can handle repositories like this:
public class HRCTX : IDisposable
{
private readonly HRMainDataCTX _context;
private Dictionary<Type, object> Repositories { get; set; }
public HRCTX()
{
_context = new HRMainDataCTX();
this.Repositories = new Dictionary<Type, object>();
}
//Get and add a repository to the dictionary if ot does not exist
public IRepository<TEntity> GetNonGenericRepository<TEntity, TRepository>() where TEntity : class
{
if (this.Repositories.Keys.Contains(typeof(TRepository)))
{
return this.Repositories[typeof(TRepository)] as IRepository<TEntity>;
}
var repoType = typeof(TRepository);
var constructorInfo = repoType.GetConstructor(new Type[] { typeof(DbContext)});
IRepository<TEntity> repository = (IRepository<TEntity>) constructorInfo.Invoke(new object[] { this._context});
this.Repositories.Add(typeof(TRepository), repository);
return repository;
}
public IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class
{
if (this.Repositories.Keys.Contains(typeof(TEntity)))
{
return this.Repositories[typeof(TEntity)] as IRepository<TEntity>;
}
IRepository<TEntity> repository = new Repository<TEntity>(this._context);
this.Repositories.Add(typeof(TEntity), repository);
return repository;
}
}
The interface and base class of your repositories:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Find(Expression<Func<TEntity, bool>> match);
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext Context { get; set; }
public Repository(DbContext context)
{
this.Context = context;
}
public TEntity Find(Expression<Func<TEntity, bool>> match)
{
return Context.Set<TEntity>().SingleOrDefault(match);
}
}
Now is the part where you clearly encapsulate your business logic:
public class EmployeeRepository : Repository<Employee>
{
public EmployeeRepository(DbContext context) : base(context) {
}
public override Employee Find(Expression<Func<TEntity, bool>> match)
{
// You can either use the base class method or implement your custom logic
}
//This is where you encapsulate your business logic
public Employee FindSpecific(Nullable<decimal> employeeNum, string employeeName){
return this.Context.SEARCHEMPLOYEE(employeeNum, employeeName);
}
}
Then you can use your Unit Of Work to access you business logic in a domain driven design way.
HRCTX unitOfWork= new HRCTX(dbContext);
unitOfWork.GetNonGenericRepository<Employee, EmployeeRepository>().FindSpecific(1337,"1337");
It can seem to be too much for what you expected as an answer but I think you need to structure your application that way if you don't want to expose you DbContext / Dal directly.
Hope it helps !
This webpage documents exactly how to accomplish your goal.
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Generic Repository for Lookup Values

I have a bunch of Lookup Entities in the database (About 10 in total) that all implement the following interface
interface ILookupValue
{
int Id { get; set; }
string Name { get; set; }
string Description { get; set; }
}
At the moment i have a repository for each entity that implements an ILookupRepository interface
public interface ILookupRepository<T> where T : class
{
IEnumerable<T> GetLookupData();
}
Example Implementation
public class CustomerRepository : ILookupRepository<Customer>
{
public IDbContext _context;
public CustomerRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<Customer> GetLookupData()
{
return _context.Set<Customer>();
}
}
I don't anticipate any of the repositories needing any other methods, so is there a way of making a generic repository for this scenario without having to have have additional code wiring up repository for each lookup type?
Edit: based on Dennis_E's answer, this is what i'm going with
public class LookupRepository<T> : ILookupRepository<T> where T : class, ILookupValue
{
public IDbContext _context;
public LookupRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
The class looks pretty generic to me.
public class LookupRepository<T> : ILookupRepository<T>
{
public IDbContext _context;
public LookupRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
Then instantiate with new LookupRepository<Customer>();
You will need a generic base class and then have your CustomerRepository inherit from that:
public class GenericRepository<T> : ILookupRepository<T>
{
protected readonly IDbContext _context;
protected GenericRepository(IDbContext context)
{
_context = context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
Then you can create an instance of GenericRepository<Customer> directly, or if you prefer, have your IoC container inject that dependency for you.
It looks generic, though one method might come handy when you need to make join statements hitting DB in one go.
One that returns IQueryable

LINQ to SQL: Check for existence of a generic entity in a generic Repository

I have a generic repository as like that
public class Repository<T> : IRepository<T> where T: class
{
DataContext _db;
public Repository()
{
_db = new DataContext("connection string");
}
System.Data.Linq.Table<T> GetTable
{
get { return _db.GetTable<T>(); }
}
public T GetBy(Func<T, bool> exp)
{
return GetTable.SingleOrDefault(exp);
}
....
}
Is it possible to add a generic method to this repository to check for the existence of any entity like that:
public bool IsExisted(T entity)
{
...
}
it is easy to write it in any repository
_productRepository.GetBy(p => p.Id == 5 // or whatever);
where the productRepository like this:
public class ProductRepository : Repository<Product>
{
public ProductRepository()
: base()
{
}
}
I came to this since i always want to check for the existence of an entity alot, so i don't need to write the same method in all repositories.
If all your entities have for example a property Guid Id you can create the following interface for your entities:
public interface IEntity
{
Guid Id { get; set; }
}
And restrict your Repository class to it:
public class Repository<T> : IRepository<T>
where T : class, IEntity
{
....
}
You can then define the following function in your base repository:
public bool Exists(T entity)
{
return GetTable.Any(e => e.Id == entity.Id);
}

C#: Can I code public class MyGenericClass<T> where T:MyClass AND implement an Interface?

I see this:
public class MyGenericClass<T> where T:IComparable { }
and I have this:
public class ProductRepository : IRepository<Product> { }
How would you code something like this?
public class ProductRepository<T> where T : Product : IRepository<Product> {}
After all that, I thought I could simply make a single Repository class then inject the type to it, including the data context. But, we pursue the simplest thing first right? LOL
Update:
If I did this, how would I choose which Entity to use in the Entity Container (or Context)? (Third line from the bottom starting with return _dc.Versions., which would somehow need switched to return _dc.T. )
public class Repository<T> : IRepository<T> where T : class
{
# region Constructor
private AdSketchEntities _dc { get; set; } // Data Container (Data Context)
public Repository() : this(new AdSketchEntities()) { }
public Repository(AdSketchEntities myDC)
{ this._dc = myDC;
}
# endregion
public T Add(T entity)
{
_dc.Versions.AddObject(entity);
return _dc.Versions.Where(n => n.custid == entity.custid &&
n.versionid == entity.versionid).SingleOrDefault();
}
You can specify multiple constraints like this:
public class ProductRepository<T>
where T : Product, IRepository<Product>
{
}

Categories

Resources