Saving in entity framework - c#

I have read this article and still misunderstanding key moments. Don't we need call
_context.SaveChanges()
in every Delete/Update/... operations?
If I change property of any entity does SaveChanges() submitted result to database or I must manually set EntityState.Modifyed?
Here is my code:
public class Repository<T> : IRepository<T>
where T : class
{
private IDbContext _context;
public Repository(IDbContext context)
{
_context = context;
}
private IDbSet<T> DbSet
{
get
{
return _context.Set<T>();
}
}
#region IRepository<T> Members
public void Insert(T entity)
{
DbSet.Add(entity);
}
public void Delete(T entity)
{
DbSet.Remove(entity);
}
public IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
}
public IQueryable<T> GetAll()
{
return DbSet;
}
public T GetById(int id)
{
return DbSet.Find(id);
}
#endregion
}
public interface IDbContext
{
IDbSet<T> Set<T>() where T : class;
int SaveChanges();
void Dispose();
}

You ask:
Don't we need call
_context.SaveChanges()
in every Delete/Update/... operations?
No we don't. When calling Delete we don't accually delete the entity - we mark it for deletion.
Same thing with Update, although you dont have to do anything other that make the changes you want to the entity. All properties (generated by the default template) will implement INotifyPropertyChanged so it knows when a entity is modified.
All entities (in database first - autogenerated by defullt template) have a State property. This property is maintained by the ObjectContext as long as the chages take place within the scope of the ObjectEntity.
e.g.
Customer c;
using(var context = new MyEntityContext())
{
c = context.Customer.FirstOrDefault(); //state is now Unchanged
c.Name = "new name"; // this set the State to Modified
//context.SaveChanges(); // will persist the data to the store, and set the State back to unchaged
}
//if we look at our customer outside the scope of our context
//it's State will be Detacth
Console.WriteLine(c.State);
Then you call SaveChanges all entites that have a state of Added Deleted or Modified will have their changes persisted to the database in a local transaction
EDIT
If an entity is marked for deletion, and you try to modify it - you will get an InvalidOperationException

You can perform many changes in your in-memory context,
such as inserts, updates and deletes.
Once you call SaveCahnges() all the changes you've made will be saved
in the DB at a single transaction.
This means that eiteher they are all submited, or none of them
in case of an error.

Related

Remove an entity without fetching it in the generic repository pattern entity framework

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();

Entity Framework and Repository Pattern conceptual difficulties

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

C# Entity Framework DBContext

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

Repository pattern Generic Write Repository issue - Object State Manager Issue

I am doing an MVC project using a repository pattern and I have a core write repository as follows
public abstract class WriteRepository<TContext> : IWriteRepository
where TContext : DbContext, new()
{
private readonly TContext _context;
protected TContext Context { get { return _context; } }
protected WriteRepository()
{
_context = new TContext();
}
public TItem Update<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
{
return PerformAction(item, EntityState.Modified, saveImmediately);
}
public TItem Delete<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
{
return PerformAction(item, EntityState.Deleted, saveImmediately);
}
public TItem Insert<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
{
return PerformAction(item, EntityState.Added, saveImmediately);
}
public void Save()
{
_context.SaveChanges();
}
protected virtual TItem PerformAction<TItem>(TItem item, EntityState entityState, bool saveImmediately = true) where TItem : class, new()
{
_context.Entry(item).State = entityState;
if (saveImmediately)
{
_context.SaveChanges();
}
return item;
}
public void Dispose()
{
_context.Dispose();
}
}
I wanted to update a single field in my db on an action method and I was doing a get all before I could update that value like below
public ActionResult UpdateTenant(string id)
{
Tenant model = new Tenant();
model = _TenantServices.GetItemById(Guid.Parse(id));
model.IsLoginEnabled = true;
_TenantServices.Update(model);
return RedirectToAction("ViewDetails", new { id = model.TenantId });
}
When I do that I am getting an error saying "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
I am using AsNoTracking to retrieve data as follow
public Tenant GetItemById(Guid id)
{
return Context.Tenants.AsNoTracking().Where(t => t.TenantId == id).FirstOrDefault();
}
Any Idea how can I solve this ?
Whenever you retrieve an object from the database, Entity Framework begins tracking (attaches) the object immediately. You will be able to make changes to the retrieved object (i.e. set property values) and call SaveChanges() so that the object will be updated in the database, without the need to set the EntityState.
And in fact, if you attempt to Attach or set the EntityState of an already-tracked object, you will get the error you mentioned above.
So, to resolve the error, you can:
Use one instance of of your TContext to retrieve and another instance to update. In this case, you should attach and set the EntityState in the update method for the changes to get persisted.
Use a single instance of your TContext to retrieve and update, but don't attempt to Attach or to set the EntityState anymore. Call SaveChanges directly after setting the property values.
Use a single instance of your TContext, but when retrieving the record you can call AsNoTracking(). This will allow you to safely Attach or set EntityState during the update.
Hope that helps.

Repository Inserting c#

Controller:
var d = new Repository<Department>(new MyDBEntities());
var newDepartment = new Department();
newDepartment.Name = "Software Department";
d.Insert(newDepartment); //I insert data here
Model:
public class Repository<T> where T : class
{
protected DbSet<T> DbSet;
public Repository(DbContext dataContext)
{
DbSet = dataContext.Set<T>();
}
public void Insert(T entity)
{
DbSet.Add(entity);
}
}
I need to insert data to my database.However if i insert data , it does not work , whenever i check my database, data never appears in my database, also i can not see anything like "SaveChanges();"in DbSet.
Where i miss exactly ?
Thanks
After Inserting data. I think you need to update your DBSet. go here to have some info :DbSet(TEntity)
In my understanding , you still need to call SaveChanges() from DbContext underlying your DbSet to actually save the data to database.
DbSet.Add() : Adds the given entity to the context underlying the set in the Added state such that it will be inserted into the database when SaveChanges is called. [MSDN]

Categories