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.
Related
I have two schemas in Oracle that have a lot of overlapping tables. But still the data must be kept separate. That is why I have 2 schemas with 80% of the tables exactly the same. I have a windows forms application have two Entity Framework 6 edmx that connect to each schema.
PMSEntities pmsdb = new PMSEntities();
GRMSEntities grmsdb = new GRMSEntities();
I have a clients entity on both edmx.
To access it, I can do this:
if (ispms)
pmsdb.clients.whatever;
else
grmsdb.clients.whatever;
This is just very cumbersome, doing everything twice.
I tried:
DbContext db;
if (ispms)
db = new PMSEntities();
else
db = new GRMSEntities();
db.clients.whatever;
But then db doesn't recognize the Client class.
I tried using just one edmx with client entity on it. Model/Database first. Then just changing and passing the connectionstring if I initialize the edmx. But edmx are built from a specific schema. So although the table name and properties are exactly the same. The edmx just work from the schema the edmx was built from.
The application will only connect to one oracle schema at a time throughout the application. It will either be to GRMS or PMS schema.
How can I declare just one db variable that will look either to the PMSEntities.edmx or the GRMSEntities.edmx.
Apparently you have two classes with a lot of similar properties and methods: PmsEntities and GRMSEntities. Although you didn't say so, I think that both classes derive from DbContext.
I see two solutions. They have nothing to do with entity framework, just plain simple object oriented programming: either create a class with all DbSets that are in both DbContext. Derive Pms and Grms from this.
Or similar use an interface
class BaseDbContext : DbContext
{
public DbSet<Client> Clients {get; set;} // both Pms and Grms have Clients
...
}
class PmsEntities : BaseDbContext
{
public DbSet<OnlyPmsType> OnlyPmsTypes {get; set;} // only in Pms
...
}
class PmsEntities : BaseDbContext
{
public DbSet<OnlyGrmsType> OnlyGrmsTypes {get; set;} // only in Grms
...
}
The other, somewhat neater solution would be using an interface. Decide what DbSets you want from both Pms and Grms:
interface IMyInterface
{
IDbSet<Client> Clients {get; set;}
IDbSet<...> ...
void SaveChanges();
}
class Pms : DbContext, IMyInterface {...}
class Grms: DbContext, IMyInterface {...}
And a factory function:
IMyInterface GetDbContext(bool ispms)
{
return ispms ? new Pms() : new Grms();
}
Usage would be as if you have your original DbContext:
bool ispms = ...
using (var dbContext = GetDbContext(ispms)
{
var fetchedClient = dbContext.Clients
.Where(client => ...)
.FirstOrDefault();
if (fetchedClient != null)
{
fetchedClient.Name = ...
dbContext.SaveChanges();
}
}
And of course the compiler ensures sure that you can't access a Pms DbSet that is not a Grms DbSet,
I am using ASP.NET MVC 5 with Entity Framework. In several controllers, I need to modify some tables (during a save operation), based on an integer ID and string ReferenceTable. Instead of duplicating the code, as well as other code, I want to write a base class, derived off a controller, and then deriving all other controller classes from it.
public class BaseController : Controller
{
protected virtual Boolean SaveTable<TableMap>(MyEntities _context, int ReferenceID, string ReferenceTable) where TableMap : GenericMapTable, new()
{
var GenericTableMap = (from s in _context.Set<TableMap>()
where s.ID == ReferenceID && s.ReferenceTableName = ReferenceTable
select s).ToList();
.... code ....
}
}
The reason I have GenericMapTable is for it to know the "shape" of the class, as all classes that I will pass it in EF with have both this ID and ReferenceTable string.
public abstract class GenericMapTable
{
[Required]
public int ID {get; set;}
[Required]
public string ReferenceTableName {get;set;}
}
In one of the controllers, I try to use this template, and as I am sure you can guess, I am having problems.
public class MyController : BaseController
{
public ActionResult SaveForm(MyViewModel viewModel)
{
Using(MyEntities _context = new MyEntities())
{
... code ...
var result = SaveTable<ConcereteTable>(_context, ReferenceID, ReferenceTable)
.... code ....
}
}
}
My thought is, that in the template class, I need something that describes the class as having ID and ReferenceTable, and then deriving my tables from it, but I can't do that, since the classes are part of EF and are auto-generated. For instance, it isn't like I can derive ConcreteTable from GenericMapTable.
ConcreteTable is of course one of several tables that I might pass.
The error I get is:
The type 'Namespace.ConcreteTable' cannot be used as type parameter 'TableMap' in the generic type or method 'BaseController.SaveTable(MyEntities, int, string).' There is no implicit reference conversion from ....
I did some searching, but I can't quite seem to find a good answer on the best practices. This must be fairly common.
I have a problem with a realisation of issue discussed in one of my previous thread (How to choose specific DbSet from context by name).
I have a class:
public class MyObjectRepository<TEntity> : MyObjectRepository<TEntity>
where TEntity : MyObject
{
private readonly DbContext _dbContext;
public MyObjectRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public bool DoesRecordExist(string id)
{
return _dbContext.Set<TEntity>()
.Any(x => x.id == id);
}
}
I also have a service class that have a repository field & executes operations like DoesRecordExist(string id). I have few classes registered with EntityFramework (every of them refers to db table: MyEFClass and myEFANotherClass respectively) that look like that:
public partial class MyEFClass: MyObject
{
}
public partial class MyEFAnotherClass: MyObject
{
}
Classes are empty here because I use code-first & MyObject in fact has all fields of EF classes so the are empty & should be used only for navigation between db tables.
Now, how do I tell to context which DbSet is active. For example how to say to generic repository that I use MyEFClass or MyEFAnotherClass?
I belive it should be called from service.
I've been trying with context.Set<something>() and something.GetType() from assemblies but failed anyway.
At final moment I always have response that I use base class MyObject which is not a valid EF DbSet (just a base).
I would be happy to know your ideas or approaches.
Apparently you have something you called a service, which has MyObjectReposistory as a field. The object repository has a DbContext as field.
This service has the notion of active MyObject. It knows whether MyEfClass or MyOtherEfClass is the active one.
This means that someone should tell your service which class is the active repository. Once this is done, whenever you'd like to do something with the active repository, we know which repository is meant.
Usually repositories are designed to hide the real internals of the database. Therefore I'd not expose the types of your tables to the users of your service.
Consider using an enum:
enum ActiveRepository
{
MyEfClass,
MyOtherEfClass,
}
class Service
{
private MyEfClass myEfRepository = ...;
private MyEfClass myOtherRepository = ...;
public ActiveRepository ActiveRepository {get; set;}
private IDbSet<MyObject> GetActiveRepository()
{
if (this.ActiveRepository == MyEfClass)
return this.myEfRepository;
else
return this.myOtherEfRepository;
}
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.
}
I am not sure if this is possible. I have several particular tables in my database represented by entity framework's code first classes. These classes have different properties except in terms of the Id property which is always a string. I am wondering if there is anyway to create a generic repository that can select and render a sequence of just the Id property. For example something like:
class GetDbIds<T> where T : class
{
// PROPERTIES
DbContext DbContext {get;set;}
DbSet<T> DbSet {get;set;}
// CONSTRUCTOR
public GetDbIds(DbContext dbContext)
{
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
// METHODS
public IEnumerable<string> GenerateNewIdSequence()
{
return DbSet.Select(x => x.Id);
}
}
I know how to set up a basic generic repository, but I haven't came across any patterns that also let you dynamically query the repository as well.
You can constrain T to an interface that has the property:
interface IIdentifiable { string Id { get; } }
class GetDbIds<T> where T : IIdentifiable, class