I am trying to learn Code First EF6 and I am confused regarding DBContext.
The database I will be working on contains 800+ tables, while when working of specific parts of the application I am only dealing with 1-10 tables.
So my question is; would not having a DBContext involving 800+ Classes have a big negative impact on system resources?
I guess I am new to this technology and confused regarding the actual meaning of the information that I am taking in during my research.
Thank you.
NOTE: Thank you for your inputs. Please take a look at this post: Using multiple DbContexts with a generic repository and unit of work. There it states I cannot have tables in separate contexts that relate to each other?!
But in a real world scenerio my understanding is that it is common to break up the table relationships in focused areas, how is this done in Code First EF? Thanks again.
Updated
If you are using Repository Pattern, you cannot go make multiple DbContext, You Create One Generic, And pass it to your Generic Repository like below :
public class Repository<T> : IRepository<T>
where T : EntityBase
{
internal MyDbContext context;
internal DbSet<T> dbSet;
public Repository()
{
context = new MyDbContext();
this.dbSet = context.Set<T>();
}
public void Add(T entity)
{
dbSet.Add(entity);
}
public void Delete(T entity)
{
dbSet.Remove(entity);
}
public void Delete(int id)
{
dbSet.Remove(dbSet.Find(id));
}
public T GetById(int id)
{
return dbSet.Find(id);
}
public IEnumerable<T> GetAll()
{
return dbSet.AsEnumerable();
}
public void Update(T entity)
{
dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
}
And you should include your DbSets there as well.
If you are doing EF Code-First then its yours to design your POCO class based on how many are needed but no more. But based on what you said about 800 tables i think you may want to try Database-First Approach rather. i suggest you this article very carefully as it explain everything you need.
Update:
If you Approach this from DataBase-First: Ado.NET Entity Model Creates your DbContext for you! If you take a closer look at the .Edmx file it is basically your POCO Classes within.
Now if you try Code-First Approach, lets say you have this DbContext Class:
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=MyConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, YourApplication.Migrations.Configuration>("MyConnection"));
}
//Every time you need to add new Table you add them here.
public DbSet<Users> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//And Map them here
modelBuilder.Configurations.Add(new UsersMap());
}
}
You just add a new DbSet<Class> to your DbContext like below:
public DbSet<POCO CLASS> CLASS { get; set; }
And so on, i normally create a DbContext for Every Area i have in my MVC Application. So you can go like Admin Area -> AdminDbContext And so on.
You only need the tables you are working with in your db context (if the db already exists). The only reason you'd need a db context with all the tables would be if you want to recreate the whole db from scratch.
Take a look at the bounded context pattern from DDD: http://martinfowler.com/bliki/BoundedContext.html
Related
Given a DbContext and a ClientContext (custom session data about the user) is it possible create a DbContext that is "authorised": where only a subset of the rows on each "table" is available?
With an authorised DbContext I'm trying to have a central row-level authorisation implementation.
I've researched it a bit and the only way to filter out a DbSet would be to use something like Queryable.Where but that returns an IQueryable<T> and there doesn't seem to be a way to return a filtered DbSet<T> (except maybe for global queries that you can setup in Startup but they don't have access to injected dependencies like ClientContext).
Is it possible to define DbSet<T> authorisation filters via an injected scoped dependency like ClientContext?
There are model-level query filters: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#model-level-query-filters
From the link:
This feature allows LINQ query predicates (a boolean expression
typically passed to the LINQ Where query operator) to be defined
directly on Entity Types in the metadata model (usually in
OnModelCreating). Such filters are automatically applied to any LINQ
queries involving those Entity Types, including Entity Types
referenced indirectly, such as through the use of Include or direct
navigation property references.
Example from the link:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public int TenantId { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().HasQueryFilter(
p => !p.IsDeleted
&& p.TenantId == this.TenantId);
}
}
You can use this for simple scenarios. You define an instance property in your DbContext and in OnModelCreating you specify HasQueryFilter on any entity you want to filter. The property is an instance property, so if you have a scoped DbContext, the correct property value from that request would be used, which is handy if you want to filter by something from your UserContext. I have personally never tried this so I don't know how complex it allows your implementation to be, but you can play with it.
I'm not sure about EF and EF core, but we abstract the DbContext away into functional specific 'logic' blocks.
e.g:
class DbContext()
{
public DbSet<PeopleEntity> peoples;
}
class PeopleLogic()
{
DbContext _context;
PeopleLogic(DbContext context)
{
_context = context;
}
IEnumerable GetAllPeoples()
{
// create context,
// apply filters
// return result
}
}
We ofcourse have a base for simple CRUD operations;
public void AddOrUpdate(){
lock (SyncDatabaseWriteObject)
{
try
{
using (var context = CreateContext())
{
//insert the entity and add it to the db context
context.Set<TEntity>().AddOrUpdate((TEntity)entity);
context.SaveChanges();
}
return entity;
}
catch (Exception ex)
{
throw new DatabaseAccessException("Error occured while getting saving.", ex);
}
}
}
And instead of passing the dbcontext around, we pass around logics.
e.g. we seperate the logic for the database and the access to the database into 2 seperate projects, the business layer then only uses the dbAccess layer.
I am trying to delete an entity of Employee from the database which contains different tables like Employee, Project, Skills using a generic repository pattern.
namespace Information.Repository
{
public class IRepositoy<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly ApplicationDbContext _dbContext;
public IRepositoy(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public void Remove(int id)
{
TEntity element = _dbContext.Set<TEntity>().Find(id);
_dbContext.Set<TEntity>().Remove(element);
}
}
}
When the above Remove method is called it makes two database call
One for getting the entity.
Second for deleting it.
I have found the query like the below one which executes with single SQL query
when the entity type(Employee or Project or Skill) is known
public void Remove(int id)
{
Employee employee = new Employee { EmployeeId = id };
_dbContext.Entry(employee).State = EntityState.Deleted;
}
can anyone please suggest me how to delete an entity without fetching it using a generic repository pattern similar to the above example.
Using raw SQL
Entity Framework doesn't allow you to delete an object that you haven't loaded (or attached). This also extends to conditional deletes (e.g. deleting all users named John) as it requires you to load the users before deleting them.
You can get around this by executing raw SQL. It's not ideal as you tend to use EF so you don't have to write SQL, but the lack of a decent delete behavior (without loading) makes this an acceptable solution.
Something along the lines of:
using (var context = new FooContext())
{
var command = "DELETE * FROM dbo.Foos WHERE Id = 1";
context
.Database
.ExecuteSqlCommand(command);
}
Where relevant, don't forget about SQL injection protection. However, it's usually a non-issue for simple deletes as the FK is usually a GUID or int, which doesn't expose you to injection attacks.
Making it generic
The example you posted works as well, but you're probably not using it because it can't easily be made generic-friendly.
What I tend to do in all my EF projects is to have an (abstract) base class for all my entities, something along the lines of:
public class BaseEntity
{
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
public string UpdatedBy { get; set; }
}
An interface would also work, I just prefer a base class here.
The audit fields are not part of this answer but they do showcase the benefits of having a base class.
When all your entities inherit from the same base class, you can put a generic type constraint on your repositories which ensures that the generic type has an Id property:
public class IRepositoy<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
At which point you can generically implement your proposed solution:
public void Remove(TEntity obj)
{
dbContext.Entry(obj).State = EntityState.Deleted;
}
You can also specify a parameterless constructor type constraint:
where TEntity : BaseEntity, new()
which enables you to instantiate your generic type as well:
public void Remove(int id)
{
TEntity obj = new TEntity() { Id = id };
dbContext.Entry(obj).State = EntityState.Deleted;
}
Note
There is a generic raw SQL solution as well, but I've omitted it as it is more complex because it requires you to retrieve the table name based on the entity type.
The raw SQL variant is only valuable in cases where you want to execute conditional deletes (e.g. removing all entities whose id is an even number).
However, since most conditional deletes are entity-specific, this means that you generally don't need to make them generic, which makes the raw SQL approach more viable as you only have to implement it in a specific repository and not the generic one.
You still have to fetch it. Entity Framework caches your dbSets so it's usually pretty quick. Use the same context like so:
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
Where dbSet =
context.Set<TEntity>();
The current limitation of Entity Framework is, in order to update or delete an entity you have to first retrieve it into memory. However there are few alternatives to delete a specific record.
You can try ExecuteSqlCommandto delete a specific record
_dbContext.Database.ExecuteSqlCommand("Delete Employee where EmployeeId = {0}", id );
or try using EntityFramework.Extended Library to delete a specific record
_dbContext.Settings.Where(s=> s.EmployeeId == id).Delete();
I am making an intranet website using ASP.NET MVC and SQL Server 2012. I am making a repository and architecturing with Onion Architecture. My problem is that the company in which I am working, already has several Server DBs in which the tables have no relations between each other. Instead, there are tables to map these relations. For example a table User, and a table Document have a table User_joint_Document to make a relation, containing both IDs (IDDocument and IDUser). Now when I write my generic repository:
class Repository<T> : IRepository<T> where T : class
the problem is the Generic type T makes no sense and I can't affect values in my model using EF queries which is normal, and what would be great would be to have a parent class BaseEntity to have IDs defined for each tables, then I can write:
class Repository<T> : IRepository<T> where T : BaseEntity
And all my table models would inherit from BaseEntity. But that would also mean rewriting the whole DB in a relational manner and mapping every DB POCO manually(correct me if I'm wrong), and I do not have the skillset to do this(there are over 300 tables in the different server DBs and I lack proper knowledge and experience to do this kind of operation).
Is there a way to keep my original DB structure, and still write a Generic Repository? How would one go about doing this?
EDIT To clarify my question because #saeb answered partially to my question. Can I have a generic repo without having a parent class for my DB POCOs? Or do I need it in order to then have only ONE repository to rule them all? For example:
class Repository<T>:IRepository<T> where T : class
{
private readonly ApplicationContext context;
private DbSet<T> entities;
public Repository(PrincipalServerContext context)
{
this.context = context;
entities = context.Set<T>();
}
public T Get(long id)
{
return entities.SingleOrDefault(s => s.IDUser == id);
//This does not work, IDUser isn't recognized
}
Thanks for your help!
... has several Server DBs in which the tables have no relations between each other ...
But they do have a relationship, a Many-to-Many relationship, which is defined via that third mapping table (whether that's a correctly defined relationship is another topic)
... the problem is the Generic type T makes no sense and I can't affect values in my model using EF queries ...
Why doesn't it and why can't you? considering your table examples, you'd have two entities, User and Document and they'd look like this:
public class User
{
public int IDUser { get; set; }
public virtual ICollection<Document> Documents { get; set; }
...
}
public class Document
{
public int IDDocument { get; set; }
public virtual ICollection<User> Users { get; set; }
...
}
And you can use the fluent API in your context's OnModelCreating to set up the relationship via the third table:
public class YourContext: DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany<Document>(u => u.Documents)
.WithMany(d => d.Users)
.Map(userJointDocument =>
{
userJointDocument.MapLeftKey("IDUser");
userJointDocument.MapRightKey("IDDocument");
userJointDocument.ToTable("User_joint_Document");
});
}
...
}
And then you can query Users and Documents in your repository as you would if there was a direct relationship between them. Here are more good sources to learn more about this if you like.
As far as I can see your problem, there is now way of achieving this without putting at least a base class or an interface to your entities/POCOs
You can play around with expressions for achieving a generic Repository
public interface IEntity<T> where T : class
{
Expression<Func<T, bool>> GetByIdPredicate(long id);
}
public partial class User : IEntity<User>
{
public int UserID { get; set; }
public Expression<Func<User, bool>> GetByIdPredicate(long id)
{
return (User entity) => entity.UserID == id;
}
}
class Repository<T>:IRepository<T> where T : class, IEntity, new()
{
private readonly ApplicationContext context;
private DbSet<T> entities;
T dummyEntity;
public Repository(PrincipalServerContext context)
{
this.context = context;
entities = context.Set<T>();
dummyEntity = new T();
}
public T Get(long id)
{
return entities.SingleOrDefault(dummyEntity.GetByIdPredicate(id));
}
There's probably a cleaner way that also gets rid of the dummyEntity field
I have two types of entities: an employee entity and an office entity, with a one to many relationship between the two such that there are many employees for one office. For EF, a DbSet is created in the context file for each entity:
public DbSet<Office> Offices { get; set; }
public DbSet<Employee> Employees { get; set; }
An EF tutorial that I did had me do my CRUD methods for a specific entity. For example, the method below creates an office and adds it to the office DbSet (ignore the MVC stuff -- I am not doing that anymore):
public ActionResult Create([Bind(Include = "Address,BusinessName")] Office office)
{
try
{
if (ModelState.IsValid)
{
db.Offices.Add(office);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(office);
}
Basically the two things I want to emphasize is that an Office object is passed into the method, and that the office is added to the Office DbSet by explicitly writing db.Offices:
db.Offices.Add(office);
However, I need to write a method in which a generic entity object can be passed in, and I can add this to its correct DbSet. The rough idea for the method I have is something like this (I have ignored all the MVC stuff):
public void Create(object entityToCreate)
{
db.CorrespondingEntityType.Add(entityToCreate);
db.SaveChanges();
}
So let's say I have an Employee object. I can pass this Employee into the Create method and it can see that this is an Employee, and so it would add it to the Employees DbSet. I don't know if EF supports this though. An alternative would be to make a switch statement and that way depending on the type of the entity being passed in, I could directly call which DbSet to add the entity to. But I want to avoid that because I will be working with a lot more entities than just these two. Also I will be having to do similar things for the other CRUD methods.
I saw this documentation from msdn about the ObjectSet.AddObject Method, and it seems like it should be useful, but I'm not sure how it works.
You might consider a generic class like so:
public class GenericRepository<T> where T : class
{
internal YourConext context;
internal DbSet<T> dbSet;
public GenericRepository(YourContext context)
{
this.context = context;
this.dbSet = context.Set<T>();
}
public virtual void Insert(T entity)
{
dbSet.Add(entity);
context.SaveChanges();
}
}
If you have an extension method like...
public static void Create<T>(this DbContext db, T entityToCreate)
where T : class
{
db.Set<T>().Add(entityToCreate);
db.SaveChanges();
}
...C# will do the type inference for you. You can just call it as...
db.Create(office);
...without ever having to worry about the type. Of course you should enter a known entity type.
I still have some confusion with the Repository Pattern. The primary reason why I want to use this pattern is to avoid calling EF 4.1 specific data access operations from the domain. I'd rather call generic CRUD operations from a IRepository interface. This will make testing easier and if I ever have to change the data access framework in the future, I will be able to do so without refactoring a lot of code.
Here is an example of my situation:
I have 3 tables in the database: Group, Person, and GroupPersonMap. GroupPersonMap is a link table and just consists of the Group and Person primary keys. I created an EF model of the 3 tables with VS 2010 designer. EF was smart enough to assume GroupPersonMap is a link table so it doesn't show it in the designer. I want to use my existing domain objects instead of EF's generated classes so I turn off code generation for the model.
My existing classes that matches the EF model are as follows:
public class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}
public class Person
{
public int PersonId {get; set; }
public string FirstName { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
I have a generic repository interface like so:
public interface IRepository<T> where T: class
{
IQueryable<T> GetAll();
T Add(T entity);
T Update(T entity);
void Delete(T entity);
void Save()
}
and a generic EF repository:
public class EF4Repository<T> : IRepository<T> where T: class
{
public DbContext Context { get; private set; }
private DbSet<T> _dbSet;
public EF4Repository(string connectionString)
{
Context = new DbContext(connectionString);
_dbSet = Context.Set<T>();
}
public EF4Repository(DbContext context)
{
Context = context;
_dbSet = Context.Set<T>();
}
public IQueryable<T> GetAll()
{
// code
}
public T Insert(T entity)
{
// code
}
public T Update(T entity)
{
Context.Entry(entity).State = System.Data.EntityState.Modified;
Context.SaveChanges();
}
public void Delete(T entity)
{
// code
}
public void Save()
{
// code
}
}
Now suppose I just want to map an existing Group to an existing Person. I would have to do something like the following:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>("name=connString");
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
group.People.Add(person);
groupRepository.Update(group);
But this doesn't work because EF thinks Person is new, and will try to re-INSERT the Person into the database which will cause a primary key constraint error. I must use DbSet's Attach method to tell EF that the Person already exists in the database so just create a map between Group and Person in the GroupPersonMap table.
So in order to attach Person to the context I must now add an Attach method to my IRepository:
public interface IRepository<T> where T: class
{
// existing methods
T Attach(T entity);
}
To fix the primary key constraint error:
EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString");
EFRepository<Person> personRepository = new EFRepository<Person>(groupRepository.Context);
var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First();
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First();
personRepository.Attach(person);
group.People.Add(person);
groupRepository.Update(group);
Fixed. Now I have to deal with another issue where Group is being UPDATE'd in the database every time I create a Group/Person map. This is because in my EFRepository.Update() method, the entity state is explicitly set to Modified'. I must set the Group's state toUnchangedso theGroup` table doesn't get modified.
To fix this I must add some sort of Update overload to my IRepository that does not update the root entity, or Group, in this case:
public interface IRepository<T> where T: class
{
// existing methods
T Update(T entity, bool updateRootEntity);
}
The EF4 implentation of the Update method would look something like this:
T Update(T entity, bool updateRootEntity)
{
if (updateRootEntity)
Context.Entry(entity).State = System.Data.EntityState.Modified;
else
Context.Entry(entity).State = System.Data.EntityState.Unchanged;
Context.SaveChanges();
}
My question is: Am I approaching this the right way? My Repository is starting to look EF centric as I start to work with EF and the repository pattern. Thanks for reading this long post
The primary reason why I want to use this pattern is to avoid calling
EF 4.1 specific data access operations from the domain. I'd rather
call generic CRUD operations from a IRepository interface. This will
make testing easier
No it will not make your testing easier. You exposed IQueryable so your repository is not unit testable.
if I ever have to change the data access framework in the future, I
will be able to do so without refactoring a lot of code.
No you will have to change a lot of code anyway because you exposed IQueryable and because EF / ORM is leaky abstraction - your upper layer expects some behavior happens magically inside your ORM (for example lazy loading). Also this is one of the most odd reasons to go for repository. Simply choose the right technology now and use it to get the bets of it. If you have to change it later it means either that you did a mistake and chose the wrong one or requirements have changed - in either case it will be a lot of work.
But this doesn't work because EF thinks Person is new, and will try to
re-INSERT the Person into the database which will cause a primary key
constraint error.
Yes because you are using a new context for each repository = that is wrong approach. Repositories must share the context. Your second solution is not correct as well because you put your EF dependency back to the application - repository is exposing the context. This is usually solved by second pattern - unit of work. Unit of work wraps the context and unit of work forms the atomic change set - SaveChanges must be exposed on unit of work to commit changes done by all related repositories.
Now I have an issue with the Group being UPDATE'd in the database
every time I want to create a Group/Person map.
Why do you change the state? You received entity from the repository so until you detached it there is no reason to call Attach and change the state manually. This all should happen automatically on attached entity. Simply call SaveChanges. If you are using detached entities then you must correctly set state for every entity and relation so in such case you will indeed needs some logic or update overloads to handle all scenarios.
Am I approaching this the right way? My Repository is starting to look
EF centric as I start to work with EF and the repository pattern.
I don't think so. First of all you are not using aggregate roots. If you do you would immediately found that generic repository is not suitable for that. Repository for aggregate roots have specific methods per aggregate root to handle working with relations aggregated by the root. Group is not part of Person aggregate but GroupPersonMap should be so your Person repository should have specific methods to handle adding and removing groups from person (but not to create or delete groups themselves). Imo generic repository is redundant layer.