I am trying to implement async Repository pattern and would like to update asynchronously:
My controller looks like this:
// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
_categoryService.UpdateCategory(category);
}
My RepositotyBase<T> class:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
// This code throws error: Cannot access a disposed object. A common
// cause of this error is disposing ...
await Task.Run(() => { // I have **await** here
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
});
}
}
My CategoryService:
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
await _categoryRepository.Update(category); // I have **await** here
_unitOfWork.Commit();
return;
}
}
However, the async implementation of method public virtual async Task Update(T entity) of RepositoryBase<T> throws an error:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
// This code throws error: Cannot access a disposed object. A common
// cause of this error is disposing ...
await Task.Run(() => {
dbSet.Attach(entity); // I have **await** here
shopContext.Entry(entity).State = EntityState.Modified;
});
}
}
Cannot access a disposed object. A common cause of this error is
disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.
UPDATE:
Is it correct async implemetation?
My controller:
// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
_categoryService.UpdateCategory(category);
}
My generic repository:
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
}
}
My unit of work:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory dbFactory;
private StoreEntities dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public StoreEntities DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public async Task CommitAsync()
{
//DbContext.Commit();
await DbContext.SaveChangesAsync();
}
}
My service:
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
_categoryRepository.Update(category); // I have **await** here
await Task.Run(()=> {_unitOfWork.Commit()}); //
return;
}
}
You need to setup your controller action PUT api/category to be async/await aware.
#mjwills has alluded to this in the comments.
// PUT api/category
[HttpPut]
public async Task Put([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Categories>(categoryDto);
await _categoryService.UpdateCategory(category);
// you probably want to set a response code. e.g return OK()
}
This is a little more offtopic but I am addressing some of the other comments.
You also need to save the changes to the database in EF. You shouldn't need to manually create your own tasks. To my knowledge EF provides a SaveChangesAsync method for you: https://learn.microsoft.com/en-us/ef/core/saving/async.
public abstract class RepositoryBase<T> where T : class
{
public virtual async Task Update(T entity)
{
dbSet.Attach(entity);
shopContext.Entry(entity).State = EntityState.Modified;
await shopContext.SaveChangesAsync();
}
}
public class CategoryService : ICategoryService
{
public async Task UpdateCategory(Categories category)
{
return await _categoryRepository.Update(category);
}
}
Related
I'm very new to ASP.NET Web API and I'm trying to use Entity Framework Core's Dependency Injection to POST data to the API Controller using MediatR pattern. But every time I run my code and it opens Swagger UI, I get an error 500 response saying
Unable to cast object of type 'AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult,S3E1.Repository.CartItemRepository+<Createitem>d__5]' to type 'System.Threading.Tasks.Task1[S3E1.Entities.CartItemEntity]'.
First, I added Dependency Injections to Program.cs
//Dependency Injection
builder.Services.AddDbContext<AppDataContext>(contextOptions => contextOptions.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")
));
//Connection
builder.Services.AddSingleton<DataConnectionContext>();
These are the classes.
AppDataContext.cs
public class AppDataContext : DbContext
{
public AppDataContext(DbContextOptions<AppDataContext> contextOptions) : base(contextOptions) { }
public DbSet<CartItemEntity> CartItems { get; set; }
public DbSet<OrderEntity> Orders { get; set; }
public DbSet<UserEntity> Users{ get; set; }
}
DataConnectionContext.cs
public class DataConnectionContext
{
private readonly IConfiguration _configuration;
private readonly string _connectionString;
public DataConnectionContext(IConfiguration configuration)
{
_configuration = configuration;
_connectionString = _configuration.GetConnectionString("DefaultConnection");
}
public IDbConnection CreateConnection() => new SqlConnection(_connectionString);
}
Next is making a repository which holds the interface that has the create method.
public interface ICartItemRepository
{
//public Task<IEnumerable<CartItemEntity>> GetCartItems();
//public Task<CartItemEntity> GetCartItemEntity(Guid id);
public Task Createitem(CartItemEntity itemEntity);
}
Then a class that inherits the interface and calls the dependency constructors
public class CartItemRepository : ICartItemRepository
{
private readonly DataConnectionContext _connectionContext;
private readonly AppDataContext _appDataContext;
public CartItemRepository(DataConnectionContext connectionContext, AppDataContext appDataContext)
{
_connectionContext = connectionContext;
_appDataContext = appDataContext;
}
public async Task Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
await _appDataContext.CartItems.ToListAsync();
}
}
Next is a command for POST request MediatR pattern
public record AddCartItemCommand(CartItemEntity cartItem) : IRequest<CartItemEntity>;
and a Handler which manages and returns the method createitem
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
private readonly ICartItemRepository _cartItemRepository;
public AddItemsHandler(ICartItemRepository cartItemRepository) => _cartItemRepository = cartItemRepository;
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await (Task<CartItemEntity>) _cartItemRepository.Createitem(request.cartItem);
}
}
and lastly, in the controller
[Route("api/cart-items")]
[ApiController]
public class CartItemsController : ControllerBase
{
private ISender _sender;
public CartItemsController(ISender sender) => _sender = sender;
[HttpPost]
public async Task<CartItemEntity> Post(CartItemEntity cartItemEntity)
{
return await _sender.Send(new AddCartItemCommand(cartItemEntity));
}
}
I tried modifying the return object in the handler but every time I change anything it always get the error squiggly line, so I just casted the (Task) after the await. Is this where I went wrong? Thank you for any answers.
The exception is clear. You can't cast a VoidTaskResult to Task<CartItemEntity>.
To solve the problem:
In ICartItemRepository, modify the return type for Createitem as Task<CartItemEntity>.
In CartItemRepository, implement Createitem method from the ICartItemRepository interface. Return the inserted itemEntity in the method.
Since you have implemented Task<CartItemEntity> Createitem(CartItemEntity itemEntity) in the ICartItemRepository interface, the casting to (Task<CartItemEntity>) is no longer needed, and suggested to be removed.
public interface ICartItemRepository
{
...
public Task<CartItemEntity> Createitem(CartItemEntity itemEntity);
}
public class CartItemRepository : ICartItemRepository
{
...
public async Task<CartItemEntity> Createitem(CartItemEntity itemEntity)
{
_appDataContext.CartItems.Add(itemEntity);
await _appDataContext.SaveChangesAsync();
return itemEntity;
}
}
public class AddItemsHandler : IRequestHandler<AddCartItemCommand, CartItemEntity>
{
...
public async Task<CartItemEntity> Handle(AddCartItemCommand request, CancellationToken cancellationToken)
{
return await _cartItemRepository.Createitem(request.cartItem);
}
}
There are many examples on the internet about the use of unit of work and generic repository together, but I couldn't find exactly how to apply the unit of work repository pattern to my own project. Because everyone did it differently.
Actually, I set up the unit of work structure, but I don't quite understand how I can use it in my own project? How can I apply the unit of work repository I made to my own project? Can you help me with this? Can you tell me if I have a mistake? This is how I saw the unit of work repository on the internet and implemented it.
First of all, if I just talk about my project, my project is an ASP.NET Core Web API project and basically a 3-layer structure:
API layer. I have controllers in the API layer.
Second is the business layer. The business layer only serves to communicate with the data access layer.
The third layer is the data access layer, in this layer I do the database operations, such as adding, deleting, updating.
I'm doing these with the generic repository. As an example, I am sharing some of my code below.
I just shared category as an example, but I have more than one class like category.
API layer - CategoriesController:
[Route("api/[controller]")]
[ApiController]
public class categoriesController : ControllerBase
{
private ICategoryService category_service;
DatabaseContext c = new DatabaseContext();
public categoriesController(ICategoryService category_service)
{
this.category_service = category_service;
}
[HttpGet("getall")]
public async Task<IActionResult> Get()
{
return Ok(await category_service.TGetAll());
}
[HttpGet("getbyid/{id}")]
public async Task<IActionResult> GetByIdCategory(int id)
{
var category = await category_service.TGetById(id);
if (category != null)
{
return Ok(category); // 200 ok
}
else
{
return NotFound(); //404 not found
}
}
[HttpPost("add")]
public async Task<IActionResult> Add(Category category)
{
var result = category_service.TAdd(category);
if (result != null)
{
return Ok(result);
}
return BadRequest(result);
}
}
Business layer - CategoryManager:
public class CategoryManager:ICategoryService
{
ICategoryDal _categoryDal;
public CategoryManager(ICategoryDal _cateogoryDal)
{
this._categoryDal = _cateogoryDal;
}
public async Task<List<Category>> TGetAll()
{
return await _categoryDal.GetListAll();
}
public async Task<Category> TGetById(int id)
{
return await _categoryDal.GetByID(id);
}
public async Task TAdd(Category entity)
{
await _categoryDal.Insert(entity);
}
public async Task TDelete(Category entity)
{
await _categoryDal.Delete(entity);
}
public async Task TUpdate(Category entity)
{
await _categoryDal.Update(entity);
}
}
Data Access layer - CategoryRepository:
public class CategoryRepository : GenericRepository<Category>, ICategoryDal
{
}
GenericRepository:
public class GenericRepository<T> : IGenericDal<T> where T : class
{
protected DatabaseContext dbContext;
public GenericRepository(DatabaseContext context)
{
dbContext = context;
}
public async Task Delete(T t)
{
dbContext.Remove(t);
await dbContext.SaveChangesAsync();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return dbContext.Set<T>()
.Where(expression)
.AsNoTracking();
}
public async Task<T> GetByID(int id)
{
return await dbContext.Set<T>().FindAsync(id);
}
public async Task<List<T>> GetListAll()
{
return await dbContext.Set<T>().ToListAsync();
}
public async Task<List<T>> GetListAll(Expression<Func<T, bool>> filter)
{
return await dbContext.Set<T>()
.Where(filter)
.ToListAsync();
}
public async Task Insert(T t)
{
await dbContext.AddAsync(t);
await dbContext.SaveChangesAsync();
}
public async Task Update(T t)
{
var updatedEntity = dbContext.Entry(t);
updatedEntity.State = EntityState.Modified;
dbContext.SaveChanges();
}
}
UnitOfWork:
public class UnitOfWorkRepository : IUnitOfWork
{
private readonly DatabaseContext _dbContext;
private IDbContextTransaction _transaction;
private bool _disposed;
public UnitOfWorkRepository(DatabaseContext dbContext)
{
_dbContext = dbContext;
}
public bool BeginNewTransaction()
{
try
{
_transaction = _dbContext.Database.BeginTransaction();
return true;
}
catch (Exception ex)
{
return false;
}
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IGenericDal<T> GetRepository<T>() where T : class
{
return new GenericRepository<T>(_dbContext);
}
public bool RollBackTransaction()
{
try
{
_transaction.Rollback();
_transaction = null;
return true;
}
catch (Exception ex)
{
return false;
}
}
public int SaveChanges()
{
var transaction = _transaction != null ? _transaction : _dbContext.Database.BeginTransaction();
using (transaction)
{
try
{
// Context boş ise hata fırlatıyoruz
if (_dbContext == null)
{
throw new ArgumentException("Context is null");
}
// SaveChanges metodundan dönen int result ı yakalayarak geri dönüyoruz.
int result = _dbContext.SaveChanges();
// Sorun yok ise kuyruktaki tüm işlemleri commit ederek bitiriyoruz.
transaction.Commit();
return result;
}
catch (Exception ex)
{
// Hata ile karşılaşılır ise işlemler geri alınıyor
transaction.Rollback();
throw new Exception("Error on SaveChanges ", ex);
}
}
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
bool BeginNewTransaction();
bool RollBackTransaction();
IGenericDal<T> GetRepository<T>() where T : class;
int SaveChanges();
}
You can check the source in the link.
https://www.linkedin.com/pulse/repository-unit-work-patterns-net-core-dimitar-iliev/
I am using xunit to do integration testing, and below is my test class.
public class CodesAndGuidelinesTest : IClassFixture<SchemaCache>
{
public readonly SchemaCache schemaCache;
public CodesAndGuidelinesTest(PostgreSqlResource resource)
{
schemaCache = new SchemaCache(resource);
}
[Fact]
public async Task Create_Name_Contains_Expression()
{
IRequestExecutor requestExecutor = await schemaCache.CodesAndGuidelinesExecutor;
.......
}
}
Here is the schema cache class
public class SchemaCache : QueryTestBase
{
Task<IRequestExecutor> _codesAndGuidelinesExecutor;
public SchemaCache(PostgreSqlResource resource) : base(resource)
{
_codesAndGuidelinesExecutor = CreateDb(CodesAndGuidelinesMockFixture.codeStandardGuidelines);
}
public Task<IRequestExecutor> CodesAndGuidelinesExecutor
{
get { return _codesAndGuidelinesExecutor; }
}
}
Here CodesAndGuidelinesMockFixture.codeStandardGuidelines is just a mock object, and When I run the test cases, I am getting the below error.
Class fixture type 'API.Tests.SchemaCache` had one or more unresolved
constructor arguments: PostgreSqlResource resource,
CodeStandardGuideline[] codesAndGuidelines The following
constructor parameters did not have matching fixture data:
PostgreSqlResource resource
I am not sure where I am doing wrong with the above code. Could anyone point me in the right direction?
Thanks!!!
Update :
QueryTestBase class
public class QueryTestBase
{
private readonly PostgreSqlResource _resource;
public QueryTestBase(PostgreSqlResource resource)
{
_resource = resource;
}
protected async Task<Func<IResolverContext, IQueryable<T>>> BuildResolverAsync<T>(T[] arrayOfEntities) where T : class
{
var databaseName = Guid.NewGuid().ToString("N");
var options = new DbContextOptionsBuilder<APIDbContext>()
.UseNpgsql(_resource.ConnectionString)
.Options;
.......
.......
return _ => set.AsQueryable();
}
protected async Task<IRequestExecutor> CreateDb<T>(T[] Entities) where T : class
{
Func<IResolverContext, IQueryable<T>> resolver = await BuildResolverAsync(Entities);
return .......
}
}
Your tool (Squadron) provides an easy way to have a PostgreSqlResource.
This resource has this properties:
implement standard IDisposable interface (or xunit speficIAsyncLifetime interface)
has a parameterless contructor
// sync implementation
class PostgreSqlResource : IDisposable
{
public PostgreSqlResource()
{
// init code
}
// props and logic
public Dispose()
{
// dispose code
}
}
// async implementation
class PostgreSqlResource : IAsyncLifetime
{
public PostgreSqlResource()
{
}
public async Task InitializeAsync()
{
// init code
}
// props and logic
public async Task DisposeAsync()
{
// dispose code
}
}
This object can be shared in xunit in 3 way:
for each test: create fixture, execute test, dispose fixture
for each class: create fixture, execute tests inside a class, dispose fixture
for a set of classes: create fixture, execute marked test classes, dispose fixture
In your case you need the 3rd way.
So Squadron provide a fixture for you, jou just need to define a TestCollection to mark your classes.
[CollectionDefinition("Squadron")]
public class DatabaseCollection : ICollectionFixture<PostgreSqlResource>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
and after that you can simply tag your test classes with attribute [Collection("Squadron")] that allow you in inject via constructor the shared instance.
[Collection("Squadron")]
public class DatabaseTestClass1
{
PostgreSqlResource fixture;
public DatabaseTestClass1(PostgreSqlResource fixture)
{
this.fixture = fixture;
}
}
[Collection("Squadron")]
public class DatabaseTestClass2
{
// ...
In case PostgreSqlResource is not enought and you need a more complex fixture is very easy; you can just create your own fixture around the other.
Of course you need to implement the same interface and delegate implementation to inner member.
class ComplexFixture: IAsyncLifetime
{
private PostgreSqlResource _pg;
public ComplexFixture()
{
_pg = new PostgreSqlResource();
}
// fixture methods
public async Task InitializeAsync()
{
await _pg.InitializeAsync();
}
public async Task DisposeAsync()
{
await _pg.DisposeAsync();
}
}
And refer to ComplexFixture insted of PostgreSqlResource on xunit CollectionFixtures. This approach is not suggested.
In my opinion is better a Plain fixture injected to test class, and than wrapped in a class fixture object if needed.
[Collection("Squadron")]
public class DatabaseTestClass1 : IDisposable
{
// each test lifecycle
private MyComplexFixture _fixture;
// global lifecycle
public DatabaseTestClass1(DatabaseFixture dbFixture)
{
_fixture = new MyComplexFixture(dbFixture)
}
// tests
public Dispose()
{
// this can reset db state for a new test
_fixture.Dispose();
}
}
public class MyComplexFixture : IDisposable
{
public MyComplexFixture (DatabaseFixture dbFixture)
{
// ...
}
public Dispose()
{
// reset logic like DROP TABLE EXECUTION
// Please note that dbFixture shoul no be disposed here!
// xunit will dispose class after all executions.
}
}
So applying this solution to your code can be as follows.
[CollectionDefinition("SquadronSchemaCache")]
public class DatabaseCollection : ICollectionFixture<SchemaCache>
{
}
[Collection("SquadronSchemaCache")]
public class CodesAndGuidelinesTest
{
public readonly SchemaCache schemaCache;
public CodesAndGuidelinesTest(SchemaCache resource)
{
this.schemaCache = schemaCache;
}
[Fact]
public async Task Create_Name_Contains_Expression()
{
IRequestExecutor requestExecutor = await schemaCache.CodesAndGuidelinesExecutor;
.......
}
}
public class SchemaCache : QueryTestBase
{
Task<IRequestExecutor> _codesAndGuidelinesExecutor;
public SchemaCache() : base(new PostgreSqlResource())
{
_codesAndGuidelinesExecutor = CreateDb(CodesAndGuidelinesMockFixture.codeStandardGuidelines);
}
public Task<IRequestExecutor> CodesAndGuidelinesExecutor
{
get { return _codesAndGuidelinesExecutor; }
}
}
public class QueryTestBase : IAsyncLifetime
{
private readonly PostgreSqlResource _resource;
public QueryTestBase(PostgreSqlResource resource)
{
_resource = resource;
}
protected async Task<Func<IResolverContext, IQueryable<T>>> BuildResolverAsync<T>(T[] arrayOfEntities) where T : class
{
var databaseName = Guid.NewGuid().ToString("N");
var options = new DbContextOptionsBuilder<APIDbContext>()
.UseNpgsql(_resource.ConnectionString)
.Options;
.......
.......
return _ => set.AsQueryable();
}
protected async Task<IRequestExecutor> CreateDb<T>(T[] Entities) where T : class
{
Func<IResolverContext, IQueryable<T>> resolver = await BuildResolverAsync(Entities);
return .......
}
public async Task InitializeAsync()
{
await _resource.InitializeAsync();
}
public async Task DisposeAsync()
{
_resource.Dispose()
}
}
We are using Net Core 2 with Entity Framework. Our Sql database consists of many tables, Address, Account, Customer, Sales, etc.
Some tables have corresponding history tables: AddressHistory, CustomerHistory,
Anytime someone changes something in original tables, the corresponding History table should be updated. (cannot use sql temporal tables, since we have custom history logic)
We are trying to apply interfaces, I'm little stuck on the code, can someone provide a quick code example of how to implement this with interfaces? Especially in SaveHistory portion, how can dependency injection be applied? Feel free to rewrite code as needed
public partial class TestDbContext
{
public void AddEntityHistory<IEntityHistory>(IEntityHistory entity) where IEntityHistory: Address
{
// do something 1 etc
}
public void AddEntityHistory<IEntityHistory>(IEntityHistory entity) where IEntityHistory: Customer
{
// do something 2 etc
}
public override int SaveChanges()
{
SaveEntityHistory();
return base.SaveChanges();
}
protected void SaveEntityHistory()
{
var modifiedEntities = ChangeTracker.Entries<IEntityHistory>().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach(var entity in modifiedEntities)
{
AddEntityHistory(entity); // what is the code base here? giving error below, it should call appropriate AddEntityHistory Method for corresponding Entity
}
}
}
Error Above:
Error CS0311 The type 'Interfaces.IEntityHistory' cannot be used as type parameter 'IEntityHistory' in the generic type or method 'PropertyContext.AddEntityHistory(IEntityHistory)'. There is no implicit reference conversion from 'Interfaces.IEntityHistory' to 'Data.Entities.Address'.
Resources :
Trying to utilize similar codebase: for CreateDate, UserId, etc
https://dotnetcore.gaprogman.com/2017/01/26/entity-framework-core-shadow-properties/
Generic form for methods won't work here at all. Reflection is more suitable for your requirement:
public partial class TestDbContext
{
public void AddEntityHistory(Address entity)
{
// do something 1 etc
}
public void AddEntityHistory(Customer entity)
{
// do something 2 etc
}
public override int SaveChanges()
{
SaveEntityHistory();
return base.SaveChanges();
}
protected void SaveEntityHistory()
{
var modifiedEntities = ChangeTracker.Entries<IEntityHistory>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
foreach (var entity in modifiedEntities)
{
var methodInfo = this.GetType().GetMethod(nameof(AddEntityHistory), new[] { entity.Entity.GetType() });
methodInfo.Invoke(this, new[] { entity.Entity });
}
}
}
You can use a Generic Repository and then for each Entity's repository, you can then save its respective history table. Below is the example code.
IGenericRepository
public interface IGenericRepository
{
Task GetByIdAsync(object id, CancellationToken cancellationToken = default);
Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
void UpdateStateAlone(TEntity entityToUpdate);
}
GenericRepository
public class GenericRepository : IGenericRepository
where TEntity : class, new()
{
private readonly YourDbContext context;
internal DbSet dbSet;
public GenericRepository(YourDbContext context)
{
this.context = context;
dbSet = context.Set();
}
public virtual Task GetByIdAsync(object id, CancellationToken cancellationToken = default)
{
return dbSet.FindAsync(id);
}
public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
return dbSet.AddAsync(entity, cancellationToken);
}
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);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void UpdateStateAlone(TEntity entityToUpdate)
{
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
Now, to use the above Generic Repository for your Entities, use the below sample code.
using System.Threading.Tasks;
public interface IAddressRepository: IGenericRepository
{
Task CommitAsync();
public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
}
using System.Threading.Tasks;
public class AddressRepository: GenericRepository, IAddressRepository
{
private readonly YourDbContext _context;
public AddressRepository(YourDbContext context) : base(context)
{
_context = context;
}
public override Task InsertAsync(Address entity, CancellationToken cancellationToken = default)
{
base.InsertAsync(entity,cancellationToken );
//call your history insert here and then the below save. This will save both the record in the main Address table and then your Address's history table.
return _context.SaveChangesAsync();
}
public Task CommitAsync()
{
return _context.SaveChangesAsync();
}
}
For detailed implementation, refer this Implement create, read, update, and delete functionalities using Entity Framework Core
What is a good way of updating the model via context EF core?
I have function like this:
public class ApplicationContext : DbContext { }
public class Repository<T>
{
private readonly ApplicationContext context;
public Repository(ApplicationContext context)
{
this.context = context;
}
public void Update(T entity)
{
context.Update(entity);
}
public async Task SaveAsync()
{
await context.SaveChangesAsync();
}
}
Function in service will call repository like this when it wants to update some model.
public void SomeFunction()
{
var model = repository.GetData();
model.Variable = "changed";
repository.Update(model);
await repository.SaveAsync();
}
Is there any good way to update my model? Also, I want to change my Update function like this:
public void Update(T entity)
{
EntityEntry<T> entity = context.Entry(item);
entity.State = EntityState.Modified;
}
Is this already good?
thanks before