Get DbContext from DbSet - c#

I am developing some extensions methods to add some funcionalities for DbSet. However, when creating an "Update" method, I need the DbSet's DbContext to be able to modify the state of a entity. The current implementation:
public void Update<TEntity>(this DbSet<TEntity> repository, TEntity entity) where TEntity : class
{
repository.Attach(entity);
var context = // how get the context from repository?
((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
Does any one know how to get a DbContext from a DbSet instance?

I've found a better way to accomplish that:
public static void MarkAsModified(this DbContext context, object entity)
{
context.Entry(entity).State = EntityState.Modified;
}

Related

How to prevent duplicate insertion of record with Entity Framework and repository pattern?

I am using a database-first approach and repository pattern. I am calling third party API recursively to get the data and add the result to my table. When I call third party API, there can be new record or existing records, API will return all data all the time.
I want to prevent duplicate insertion of record in my table.
I am using the repository pattern to save the data.
This is my code:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly ApplicationDBContext _context;
protected DbSet<T> dbSet;
public GenericRepository(ApplicationDBContext context, ILogger logger)
{
this._context = context;
this.dbSet = context.Set<T>();
}
public virtual async Task<bool> Add(T entity)
{
await dbSet.AddAsync(entity);
return true;
}
}
How can I check if record is already present in table? If already present, then don't add it, else add the record.

.net core testing dbsets

I´m preparing my test infrastructure, but i´m facing some problems with my test repository.
The real repository access EntityFramework DbSet, like this:
public class Repository<T>: IRepository<T> where T : ModelBase
{
protected ApplicationDbContext db;
private DbSet<T> dbSet;
public Repository(ApplicationDbContext db)
{
this.db = db;
dbSet = db.Set<T>();
}
public IQueryable<T> Where(Expression<Func<T, bool>> predicate)
{
return dbSet.Where(predicate).AsNoTracking();
}
....
My TestRepository uses List instead of DbSets:
public class TestRepository<T> : IRepository<T> where T : ModelBase
{
private readonly List<T> dbSet;
protected ApplicationDbContextFake db;
public TestRepository(ApplicationDbContextFake db)
{
this.db = db;
this.dbSet = db.Set<T>();
}
This db.Set<T>() returns a List
The problem occurs when testing my code and there are something like this:
public async Task DeleteAsync()
{
var items = repository.Where(....);
repository.RemoveRange(await items.ToListAsync());
This code runs ok using Entity DbSets, but throws an exception when testing with my TestRepository:
The source IQueryable doesn't implement IAsyncEnumerable. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
Any suggestions to workaround this?
If you are using EntityFramework Core (not EF6) - you may use in-memory implementation for your tests.
See docs for Microsoft.EntityFrameworkCore.InMemory provider.

Abstract Entity Framework

I want to create an abstraction layer between Entity Framework and the rest of my application. But I am having a few problems with Entity Framework.
Basically (I don't show you all the interface layers that I've created too), I've split my application into several projects like this :
Domain
Contains my domain object, an abstraction of my datastorage object
DAL
Creates a link between my datastorage and my business layer. Contains two types of elements :
Private ones : my EDMX, my database object, and some other generated objects providing me some useful methods like ToDomain/ToEntity
Public ones : my Data Access Object, providing CRUD methods
Business
Contains the logic of my application. Only knows about the public elements of the DAL and the Domain Layer.
Presentation
Presents the domain objects for the user. Only knows about the business layer.
As I said, I want to create an abstraction of my datastorage objects (in my case Database object, but I want a solution that works also for file or WCF storage for example) so that my business layer don't know anything about my DAL implementation.
Here is a glimpse of what I've done in my DAL :
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected TDbContext _context;
protected DbSet<TEntity> _dbSet;
public GenericDao(TDbContext dbContext)
{
this._context = dbContext;
this._dbSet = dbContext.Set<TEntity>();
}
public TDomain Create()
{
return this.ToDomain(this._dbSet.Create());
}
public IList<TDomain> GetAll()
{
return this._dbSet.ToList().Select(entity => this.ToDomain(entity)).ToList();
}
public void Update(TDomain domain)
{
var entity = this.ToEntity(domain);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
}
public void Remove(TDomain domain)
{
_dbSet.Remove(this.ToEntity(domain));
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
You will probably see what's wrong with my code by reading it: when I try to delete or update an entity, I am not manipulating an entity attached to Entity Framework. If I try to attach my entity to the dbContext, it fails because there is already an entity in the context with the same id.
I already thought about several solutions, but none of them please me.
Maybe am I doing something wrong in my approach? I am a little bit confused about the Repository and DAO pattern (I read anything and the very opposite about that difference on the internet).
You have two options:
initialize new dbcontext for each operation and dispose it when operation is ended:
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected Func<TDbContext> _contextFactory;
public GenericDao(Func<TDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public TDomain Create()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().Create();
}
}
public IList<TDomain> GetAll()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().ToList()
.Select(entity => this.ToDomain(entity)).ToList();
}
}
public void Update(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
context.SaveChanges();
}
}
public void Remove(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
or you can try to find entity in your instance of dbcontext using property Local of DbSet:
var contextEntity = context.Set<TEntity>().Local
.Where(c=>c.Id == entity.Id).FirstOrDefault();
You seem to be getting stuck coding to an implementation within your abstraction. If you injected an interface to your generic rather than a concrete type (like EF) then your GenericDao becomes much more flexible. You can inject whatever implementation you choose providing it implements the required interface. In your case, WCF, File, Whatever. For example;
protected IDbContext _context;
public GenericDao(IDbContext dbContext)
{
this._context = dbContext;
}
public void Remove(TDomain domain)
{
_context.Remove(this.ToEntity(domain));
}
//abstraction
public interface IDbContext
{
void Remove(Entity entity);
}
//EF Implementation
public MyEfClass : IDbContext
{
public void Remove(Entity entity)
{
//code to remove for EF example
context.Attach(entity);
context.State = EntityState.Modified;
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
//WCF Implementation
public MyWCFClass : IDbContext
{
public void Remove(Entity entity)
{
//Wcf implementation here
}
}
//File example
public FileWriter : IDbContext
{
public void Remove(Entity entity)
{
LoadFile();
FindEntry(entity);
WriteFile(entity);
SaveFile();
}
public void LoadFile()
{
//use app settings for file directory
}
}

What is the advantage of using ObjectSet

As MSDN suggests we can use the following ObjectContext
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
// Add the new object to the context.
context.Products.AddObject(newProduct);
}
However there is also another use of a similiar code with using ObjectSet<T>
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
ObjectSet<Product> pSet = context.CreateObjectSet<Product>();
pSet.AddObject(newProduct);
}
The second paragraph in the article says:
In versions starting with .NET Framework version 4, you can use the
following methods defined on ObjectSet instead of the equivalent
ones defined on ObjectContext.
Is there a spesific reason to use ObjectSet instead of ObjectContext and how do we know which to use when?
ObjectContext and ObjectSet are legacy EF code, for which DbSet and DbContext have created as wrappers around ObjectContext model to make EF a much better experience.
Underneath DbSet and DbContext, EF is still using ObjectContext / ObjectSet.
Starting with EF 7, they got rid of all base code, and are re writing the whole EF ORM.
EDIT
DbContext = A collection of your entity models, connection to the database, logging, tracking, and glue, and probably a whole bunch of things i have missed. This usuall contains 1 or more DbSets<YourEntity>
DbSet is an object representing a collection of a specific entity. This contains information such as caching, inserting, updating, selecting for only a specific entity.
I like to think of these a
DbContext = Database
DbSet = Table
They are ALOT more than that, but conceptually that is how I visualise them, and don't necessarily map 1:1. E.g. an Entity may be a subset of a table or it may even be a combination of multiple tables.
Regarding ObjectSet and ObjectContext I lack experience in how they work internally in order to tell you what the difference is exactly. I know how DbSet/Context works, but I don't know how much of it gets done by ObjectSet/Context and how much is additional.
Maybe an excercise for you to find out in the wild? :-P
There isn't much difference if you use ObjectSet directly.
However by using ObjectContext and OBjectSet together, you can develop reusable generic repository classes (CRUD). The code sample that you provided will only work for retrieving products for that application only, whereas a generic CRUD repository would define methods to Add, Read, Update and Delete which can work with any table (and in other databases).
e.g.
You can define a IRepository interface
public interface IRepository<T> : IDisposable where T : class
{
void Add(T entity);
void Delete(T entity);
void SaveChanges();
...
}
And a generic concrete class
public class DataRepository<C,T> : IRepository<T> where T : class where C : ObjectContext, new()
{
private ObjectContext _context;
private IObjectSet<T> _objectSet;
public DataRepository() : this(new C()) { }
public DataRepository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
}
public void Add(T entity)
{
if(entity == null) throw new ArgumentNullException("entity");
_objectSet.AddObject(entity);
}
public void Delete(Func<T, bool< predicate)
{
var records = from x in _objectSet.Where<T>(predicate) select x;
foreach(T record in records)
_objectSet.DeleteObject(record);
}
public void SaveChanges()
{
_context.SaveChanges();
}
// Other members
// IDisposable members
}
The code above you could copy and paste to each application, or put in a separate assembly and reference in each application.
For your example, you would create this class in your application to retrieve products
public class ProductsRepo : DataRepository<AdventureWorksEntities, Product> {
// You can add other specific methods not covered by the default CRUD methods here
}
And to add a new product
using(var repo = new ProductsRepo())
{
repo.Add(newProduct);
repo.SaveChanges();
}

Update on Entity fails using generic Repository

I have a generic repository:
public class GenericRepository<TEntity> : AbstractRepository<TEntity>, IRepository<TEntity> where TEntity : class
{
private DbContext _context;
[...]
public GenericRepository(DbContext context)
{
_context = context;
context.Configuration.AutoDetectChangesEnabled = true;
_dbSet = _context.Set<TEntity>();
}
[...]
public void SaveChanges()
{
_context.SaveChanges();
}
[...]
public void Add(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_dbSet.Add(entity);
}
[...]
public virtual void Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
}
In my controller, there is this code:
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
if (project.Id == 0)
{
ProjectRepository.Add(project);
}
else
{
ProjectRepository.Update(project);
}
ProjectRepository.SaveChanges();
[...]
Selecting and Inserting works fine, but Updating not: I get an InvalidOperationException (english translation of the german error message is "An object with the same key already exists in the Object State Manager. The Object State Manager can not track multiple objects with the same key.").
I don't understand that, because I'm the only user on my development machine, and I did not modify the record at another place.
Any idea what I'm doing wrong here?
Take a look at these answers:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
"An object with the same key already exists in the ObjectStateManager..." exception is thrown when setting an entity state to modified
Basically you need to do this:
var entity = _context.Projects.Find(project.ProjectId);
_context.Entry(entity).CurrentValues.SetValues(project);
Hopefully this helps.
The project instance is created by model binding, not loaded from your repository, so you need to actually load a project instance from the repository, and then change the properties on that.
Disclaimer: i never used Entity Framework, i am writing this based on my experience with ASP.NET MVC and nHibernate but you should be able to apply the same pattern
Ok first off your actual problem is in fact a double key insert, this is the case because the project-object which get passed to your edit action is not the same object you are trying to update (this one has been created from your modelbinder based on the values in the FormValueProvider). it may has the excat same values and therefor the same id but for your ORM it`s a brand new object which has never been persisted to the DB.
you can prevent this by using the following pattern (quick'n dirty sample-code)
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
if (project.Id == 0)
{
ProjectRepository.Add(project);
}
else
{
Project dbProject = ProjectRepository.GetById(project.Id); //or whatever your method to retrieve entities by Id is named
UpdateModel(dbProject, "project"); //http://msdn.microsoft.com/en-us/library/dd470933.aspx
ProjectRepository.Update(project);
}
}
}
This is my solution for this problem in EF Core. Not sure if it can be done on EF6.
Repository Interface
public interface IRepository<TEntity> where TEntity : class
{
void Update(TEntity entity);
// I ommited the rest of the methos for simplicity
}
Repository Implementation
public class GenericRepository<T> : IRepository<T> where T : class
{
public void Update(T entity)
{
dbContext.Set<T>().Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
// don't forget to dbContext.SaveChanges() here if needed
}
}

Categories