Customizing features in Repository design classes - c#

I could be probably wrong in the manner I am thinking the things will work
but I am really confused as I am using repository design pattern for the first time.
The problem is that the repository class will only have
GetaLL(), GetALLByID(), Update() or Save().
But what If I need to extract the records such that I have groupofUsers in one table
and for that each group I need to count how many users are there in each group. The user table is a different table.
The relation can be assumed as "UserGroup" as parent table which have unique
usergroups but this UserGroup table has a key which will be foreign key in
another table "Users". There could be many users in a group and similary with this
I need to find out how many roles are under the same table i.e "UserGroup".
This is another table "roles" which will have "UserGroups" in the same way as
it wasin "users" table.
And here is how I ahve to get the records. My repository only have
public abstract class Repository<T>
where T : class
{
private readonly IDbSet<T> _dbset;
public virtual IQueryable<T> GetAll()
{
return this._dbset;
}
}
And in controller I am calling it as below:
private readonly Repository<UserGroup> _GroupRepository;
public IEnumerable<UserGroupModel> GetListedGroups()
{
var list = this._GroupRepository.GetAll();
}

You can have a new class which derived from Repository<T> and add new methods in it.
public class UserGroupRepository : Repository<UserGroup>
{
public UserCountInGroups GetUserCountInGroup()
{
// Do something with this._dbset here.
}
}
So in the controller you use UserGroupRepository instead of Repository<UserGroup>
private readonly UserGroupRepository _GroupRepository;
public IEnumerable<UserGroupModel> GetListedGroups()
{
var list = this._GroupRepository.GetAll();
var userCount = this._GroupRepository.GetUserCountInGroup();
// Do something here.
}

Related

DbContext and DbSet issues in Generic repository

I'm trying to create a project will play the repository role in all my projects.
The Idea:
The Idea was inspired from Generic Repository pattern, so I'm trying to create a generic class will be the repository.
this class will receive the dbcontext at the Instantiation.
this class will implement an Interface.
The Interface :
interface IRepo<Tcontext> where Tcontext : DbContext
{
void GetAll<Table>();
}
The Repo class :
public class Repo<Tcontext>:IRepo<Tcontext> where Tcontext: DbContext
{
private Tcontext _Context { get; set; } = null;
private DbSet _Table { get; set; } = null;
public Repo()
{
_Context = new Tcontext();
}
public void GetAll<Table>()
{
_Table = new DbSet<Table>();
return _Context.Set<Table>().ToList();
}
}
So, the Idea, lets imagine we have this dbcontext class: DBEntities, and if I want to select all records from Client table, and after that in another line I Wanna select all records from Order table
I would use:
Repo<DBEntities> repo = new Repo<DBEntities>();
var clients repo.GetAll<Client>();
var orders repo.GetAll<Order>();
What is the problem:
the problem in Repo class.
the problem is four errors I don't have any Idea how can I solve them.
Errors:
so please, any help to solve this problem? and massive thanks in advance.
The first two errors The Table must be a reference type.... is logged as you have not defined the constraints on your function. Change the signature as shown below; using the below signature the method is putting constraint that generic type Table should be reference type.
public void GetAll<Table>() where Table : class
The third error as there is no public default constructor for DBContext. You should use the parametrized one. Check the DBContext definition here
_Context = new Tcontext(connectionString);
The fourth error will resolve automatically after adding the changes for first two as generic parameter Table constraint is defined. You can check the signature of Set function at here.

C# Repository Pattern? Complex queries still placed inside repository?

I am learning the Repository Pattern. I have managed to write the code to do it.
I read the article say each Repository related to one entities.
This is what i have for my Repository:
public class ProductRepository : BaseProductRepository
{
public ProductRepository(StoreDBContext context) : base(context)
{
this.context = context;
}
public override async Task<IEnumerable<TResult>> GetTopProducts<TResult>(Func<Product, TResult> selectFunc)
{
var products = this.context.Product
.Select(selectFunc)
.ToList();
return products;
}
}
I am having the generic and expression in the method is because I want to have the caller to define what result should be e.g. partial data from product instead of all the data of a product.
Now i encounter an issue. What if the query is little complex which e.g. more table to join and the resulting data is returning not only the data of the Product, may be the number of users purchased the product etc.
Where can those complex query's which return complex dataset would be?
TResult should be in the 'shape' that the method relates to. It certainly shouldn't be a database entity.
In your case TResult for GetTopProducts would be a collection of product summaries.
class GetTopProductsResult
{
public IEnumerable<Product> TopProducts {get;set;}
public class Product
{
public int Id {get;set;}
public string Name {get;set;}
public int UnitsSold {get;set;}
}
}
I also suggest you look at AutoMapper which will basically implement your Select function in a more structured way.

What is a good pattern for a repository and EF data context that return different types?

I have a nice clean domain layer in my app that was developed in a DDD fashion. The database was not considered at all when developing the domain. Property names make sense, aren't in ALL CAPS, and are relevant to my application.
Today, I am implementing a repository to pull from an existing EF DbContext. The DbContext was developed to (basically) match a poorly-designed Oracle database.
Ideally, I would like to implement a repository like this:
public interface IRepository {
IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity;
}
T is my domain entity. But, inside my Find method in my repository, I have to...
Somehow convert the expression to work with the DbContext
I am not sure how to do this yet.
Query the DbContext
Once the expression is 'mapped', this is simple
Somehow map to my domain object
I'm sure I can use AutoMapper or implement my own mapper.
Return an IQueryable having not made a trip to the database yet.
Not sure this is possible after all the meddling done in #'s 1 - 3
So, how has this problem been solved in the past? Are there any reusable patterns here?
Well, you're on the right track already, just implement what your say you want :)
1.You're passing an expression into your find method so, just use that expression in your Where clause
2.You just need to get the correct DbSet from your DbContext to query against, DbContext has a method to get the DbContext of a given type, use that and you can query like
public IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity
{
var dbSet = context.Set<T>();
return dbSet.Where(query);
}
3.If your domain objects are not the ones mapped by EF to the database, you'll need to customize your mapping against what's in your DB in your DbContext class (no need for automapper for that), so you would have something like this in your DbContext class
public class MyContext : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Map(a => a.ToTable("DB_USERS"))
.Property(a => a.Email).HasColumnName("MAIL");
base.OnModelCreating(modelBuilder);
}
}
To map from the table DB_USERS in the DB to the class User, having different names for the fields, etc. here's an article on that
http://www.codeproject.com/Articles/165720/Using-the-Code-First-Model-Configuration-Classes
You could also map the properties to the correct table columns using attributes if you don't want/can't change your DbContext class
http://msdn.microsoft.com/en-us/data/gg193958
Or you can have a different set of entities that are mapped to your DB and use automapper to translate them into your domain objects, but you lose no. 4 bellos since you'll need to materialize the query to automap it to your domain model.
4.No need to do anything special, EF takes care of the that
UPDATE: Solution without having access to the DbContext (not fully generic version but works)
The idea is to create the mapping part of the repository for each domain class, so all gets binded correctly. Continueing with the User domain model and DBUser table model:
public class User : IDomainModelEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class DBUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int USER_ID { get; set; }
[Required]
[MaxLength(150)]
public string USER_NAME { get; set; }
[Required]
[MaxLength(260)]
public string USER_MAIL { get; set; }
}
Then you would have an abstract Repository and an a concrete repository per domain class that implements the basic GetAll query mapped:
public abstract class Repository<T> where T : IDomainModelEntity
{
protected readonly DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
public abstract IQueryable<T> GetAll();
public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
{
return GetAll().Where(predicate);
}
}
public class UserRepository : Repository<User>
{
public UserRepository(DbContext context)
: base(context)
{
}
public override IQueryable<User> GetAll()
{
return _context.Set<DBUser>()
.Select(u => new User
{
Id = u.USER_ID,
Name = u.USER_NAME,
Email = u.USER_MAIL
});
}
}
now to use it you will just call the find or get all on the repository...
using (var context = new CompanyDbContext())
{
var repo = new UserRepository(context);
var list = repo.Find(a=>a.Id >= 2).ToList();
list.ForEach(a => Console.WriteLine("Id: {0}, Name {1}, email {2}", a.Id, a.Name, a.Email));
}
It is not fully generic since you will need to pass a repository for each domain class you need to use, but it may be an acceptable compromise
Hope this helps

nhibernate entity multi mappings

I have entity fluently mapped to existing oracle view:
public class MyEntityMap : ClassMap<MyEntity>
{
public class MyEntityMap()
{
ReadOnly();
SchemaAction.None();
//mappings
}
}
I'm querying oracle view for entities and filtering them based on certain criteria, let's say, where created_date more than 14 days. Those entities should be written into the database for future reporting use. For that purpose I've created a table which is exact clone of oracle view in terms of fields. And I'd like to map exactly the same MyEntity to my own table. Something like that:
public class MyHistoricalEntityMap : ClassMap<MyEntity>
{
public class MyHistoricalEntityMap()
{
Table("HistoricalEntities");
//mappings
}
}
Also, I have a service responsible for querying view, but I want to add a method to store my historical entities, smth like below:
public class MyEntityService : IMyEntityService
{
private IRepository<MyEntity> _repository;
...
public IEnumerable<MyEntity> GetEntities(){...}
public void StoreHistoricalEntities(IEnumerable<MyEntity> historicalEntities) {...}
}
So, question is: how do I specify, that I want to (or nhibernate should) use MyEntityMap for querying, but MyHistoricalEntityMap for storing results? Or what other solution can I apply?
Thanks,
You can't use two different mappings for an entity.
What you can do is use custom SQL for loading.

SharpArch.Core.PreconditionException: For better clarity and reliability, Entities with an assigned Id must call Save or Update

When I do a save of an entity with assigned id I get a
SharpArch.Core.PreconditionException:
For better clarity and reliability,
Entities with an assigned Id must call
Save or Update
My class is
public class CompanyUserRepository :
RepositoryWithTypedId<CompanyUser, string>, ICompanyUserRepository
{
public override CompanyUser SaveOrUpdate(CompanyUser entity)
{
var user = base.SaveOrUpdate(entity);
//Do some stuff
return user;
}
}
How do I go about saving this entity?
RepositoryWithTypedId does not expose a Save method
Related question. This tells you the reason, but I haven't found out the Sharp Architecture way to do a Save.
Digging through the NorthWind example in the S#arp repo, I found out the answer.
The Repository class should derive from NHibernateRepositoryWithTypedId
e.g.
public class CustomerRepository : NHibernateRepositoryWithTypedId<Customer, string>, ICustomerRepository
{
public List<Customer> FindByCountry(string countryName) {
ICriteria criteria = Session.CreateCriteria(typeof(Customer))
.Add(Expression.Eq("Country", countryName));
return criteria.List<Customer>() as List<Customer>;
}
}

Categories

Resources