The code I have got so far works fine
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
But I want to include 1 table more and cannot use FindAsync
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.Include(i=>i.ItemVerifications).FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
So I am facing this error
Severity Code Description Project File Line Suppression State
Error CS1061 'IQueryable' does not contain a definition for
'FindAsync' and no extension method 'FindAsync' accepting a first
argument of type 'IQueryable' could be found (are you missing a
using directive or an assembly reference?)
Any clue how to fix it?
The simplest is to use FirstOrDefaultAsync or SingleOrDefaultAsync instead:
model.Item = await db.Items.Include(i => i.ItemVerifications)
.FirstOrDefaultAsync(i => i.Id == id.Value);
The reason you are getting the error is because Find / FindAsync methods are defined for DbSet<T>, but the result of Include is IQueryable<T>.
Another way is to combine FindAsync with explicit loading:
model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
await db.Entry(model.Item).Collection(i => i.ItemVerifications).LoadAsync();
If you are using a generic repository and you don't know the PK at runtime, this approach can help:
public interface IGenericRepository<TEntity> where TEntity : class
{
Task<TEntity> Get(int id, string[] paths = null);
}
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly ApplicationDbContext _context;
private readonly DbSet<TEntity> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
public async Task<TEntity> Get(int id, string[] paths = null)
{
var model = await _dbSet.FindAsync(id);
foreach (var path in paths)
{
_context.Entry(model).Reference(path).Load();
}
return model;
}
}
When you program using solid principles and domain design then use generics. The Repository pattern uses a generic class. I pass a lambda express to the GetObjectsQueryable function. I have setup lazy loading to be on, using code first handle bars. However, I am moving away from lazy loading and implement a microservice architecture. The include table is a string and you can use the nameof(xxclass) function to ensure the correct name. The function returns and IQueryable results. The repository class methods can be used by its derived class enhance the method is protected. This is a dotnet.core demonstration.
public class Repository
where T : class
{
public IQueryable<T> GetObjectsQueryable(Expression<Func<T, bool>> predicate, string includeTable="")
{
IQueryable<T> result = _dbContext.Set<T>().Where(predicate);
if (includeTable != "")
result = result.Include(includeTable);
return result;
}
}
Related
I usually use AsNoTracking when I'm not intending to write anything. How should I handle this in my service layer where dbContext is hidden behind it? (I treat EF core as repository because it is repository)
public class SomeService
{
//...
public SomeEntity GetById(int id)
{
return _dbContext.Find(id);
}
public SomeEntity GetReadonlyById(int id)
{
return _dbContext.SomeEntitities.AsNoTracking().SingleOrDefault(e => e.Id == id);
}
public SomeEntity Update(SomeEntity someEntity)
{
_dbContext.Update(someEntity);
_dbContext.SaveChanges();
}
}
public class SomeController
{
private readonly SomeService _someService;
//....
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var someEntity = _someService.GetReadonlyById(id);
if (someEntity == null)
{
return NotFound();
}
return someEntity;
}
[HttpPut("{id}")]
public IActionResult Modify(int id, SomeEntity modified)
{
var someEntity = _someService.GetById(id);
if (someEntity == null)
{
return NotFound();
}
someEntity.Someproperty = modified.Someproperty;
_someService.Update(someEntity);
return Ok(someEntity);
}
}
Is there any better way to do this?
I can also define my service as follows:
public class SomeService
{
//...
public SomeEntity GetById(int id)
{
return _dbContext.AsNoTracking.SingleOrDefault(e => e.Id == id);
}
public SomeEntity Update(int id, SomeEntity someEntity)
{
var entity = _dbContext.SomeEntities.Find(id);
if (entity == null)
{
return null;
}
entity.Someproperty = someEntity.Someproperty;
_dbContext.Update(entity);
_dbContext.SaveChanges();
return entity;
}
}
public class SomeController
{
private readonly SomeService _someService;
//....
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var someEntity = _someService.GetById(id);
if (someEntity == null)
{
return NotFound();
}
return someEntity;
}
[HttpPut("{id}")]
public IActionResult Modify(int id, SomeEntity modified)
{
var someEntity = _someService.Update(id, modified);
if (someEntity == null)
{
return NotFound();
}
return Ok(someEntity);
}
}
What is the better way?
Basically, it is more common problem.
It is often happens that optimized reading methods are not convenient for updating scenarios and convenient reading methods for updating scenarios have unnecessary overhead for reading only scenarios. I see 3 options here:
Ignoring all problems with performance and just use universal GetById from your first approach for reads and updates. Obviously, it is applicable for simple applications and may not be applicable for high-load applications.
Using CQRS. It means you will have completely separate data model for reads and updates. Since reads usually don't require the complex domain logic it allows you to use any optimizations like AsNoTracking method or even use a plain sql in repositories. It is applicable for complex apps and requires more code.
Trying to find some compromise between these two options according to your particular needs.
As noted in comments your SomeService looks like repository. Ideally, domain service should contain only business logic and shouldn't mix it with infrastructure features like AsNoTracking. Whereas repositories can and should contain infrastructure features like AsNoTracking, Include and etc.
No tracking queries are useful when the results are used in a read-only scenario. They are quicker to execute because there is no need to setup change tracking information.
You can swap an individual query to be no-tracking:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.AsNoTracking()
.ToList();
}
You can also change the default tracking behavior at the context instance level:
using (var context = new BloggingContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();
}
Ideally, you should manage the infrastructure stuff in the repository level.
Here is my solution. It works.
BASE CLASS OF ALL SERVICES
public class ServicesBase
{
protected AppDbContext dbcontext { get; }
public ServicesBase(AppDbContext dbcontext)
{
this.dbcontext = dbcontext;
}
public void AsNoTracking()
{
dbcontext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public void SaveChanges()
{
dbcontext.SaveChanges();
}
}
USING IN CONTROLLER
_masterService.AsNoTracking();
var master = _masterService.GetById(1);
master.WagePerSquareMeter = 21;
_masterService.SaveChanges();
//or dbcontext.SaveChanges(), both of them will ot affect the database.
Just to set the question, when I have come to write a unit test I hit the following error:
Error: The source IQueryable doesn't implement
IDbAsyncEnumerable
The problem happens when testing a method which calls ToListAsync(), the unit test is as follows:
[TestMethod]
public async Task Method1CallsCount()
{
//arrange
MockContainer container = new MockContainer();
IQueryable<Entity1DTO> querableentity1DTO = new List<Entity1DTO>().AsQueryable();
container.DefaultQueryFactory.Setup(p => p.Load(It.IsAny<ContextEnums>(), It.IsAny<Expression<Func<Entity1, Entity1DTO>>>(),
It.IsAny<Expression<Func<Entity1, bool>>>(), It.IsAny<int>(), It.IsAny<bool>())).Returns(querableentity1DTO);
var manager = new Manager1(container.DefaultQueryFactory.Object);
//act
var result = await manager.Method1();
//assert
//container.repo1.Verify(x => x.repoMethod(It.IsAny<Expression<Func<Entity1,bool>>>()), Times.Once);
}
And here is the method I am testing:
public async Task<List<Entity1DTO>> Method1()
{
Expression<Func<Entity1, Entity1DTO>> select = (x => new Entity1DTO()
{
...
});
Expression<Func<Entity1, bool>> where = (x => x.Property == "Test");
return await _defaultQueryFactory.Load(ContextEnums.Enum1, select, where).ToListAsync();
}
To help a bit, I've tried mocking up the method that loads the data in the query factory and the error appears because the DTO model doesn't implement IDbAsyncEnumerable, now the method that is getting tested sends off a select statement and a where statement and an entity type which the query factory then uses to generate a query this is then executed with ToListAsync() when it returns from the load Method. The error message shows that the DTO model is the one that doesn't implement the IDbAsync not the DB entity itself.
I understand that there are a few other questions out there that are simular but my difference is that I use a DTO model and the method in question does not use the context itself as it is injected into the load method and not in the place in which ToListAsync() is called.
anyone any ideas?
Error happens because Entity Framework async extension methods does not work with any IQueryable - it should also implement IDbAsyncEnumerable interface. Consider this:
var query = new List<EntityDTO>().AsQueryable();
var result = query.ToListAsync().Result;
This will throw the same exception you observe in your code. EnumerableQuery returned by AsQueryable does not implement required interface, so we need to use some other implementation. We can find one in this article (or just create ourselves since it's not hard):
static class TestExtensions {
public static IQueryable<T> AsAsyncQueryable<T>(this IEnumerable<T> source) {
return new TestDbAsyncEnumerable<T>(source);
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner) {
_inner = inner;
}
public void Dispose() {
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) {
return Task.FromResult(_inner.MoveNext());
}
public T Current => _inner.Current;
object IDbAsyncEnumerator.Current => Current;
}
Now we can do:
IQueryable<Entity1DTO> querableentity1DTO = new List<Entity1DTO>().AsAsyncQueryable();
And EF async methods will execute correctly on it.
There are actually two problems here, firstly you do need to use the solution in the other answer and change the return type to TestDbAsyncEnumerator and the other problem is to do with Moq, the setup needs to have the same parameters called, so in your case you have
return await _defaultQueryFactory.Load(ContextEnums.Enum1, select, where).ToListAsync();
and
container.DefaultQueryFactory.Setup(p => p.Load(It.IsAny<ContextEnums>(), It.IsAny<Expression<Func<Entity1, Entity1DTO>>>(),
It.IsAny<Expression<Func<Entity1, bool>>>(), It.IsAny<int>(), It.IsAny<bool>())).Returns(querableentity1DTO);
notice how you have two extra parameters on the end, try leaving them as null. That should sort out the problem, just to make sure try adding an item to the return list as well.
I have a generic ASP.NET Core WebApi controller like:
public abstract class EntityController<TEntity>
{
public IActionResult Get(string id)
{
var entity = ... //load from database by id
if (entity != null)
return new JsonResult(value, this.SerializerSettings()) {StatusCode 200};
return NotFound();
}
}
and I want to apply following attributes on the Get() method:
[ProducesResponseType(typeof(TEntity), 200)] //this causes compilation error.
[ProducesResponseType(typeof(Object), 404)]
For now, the only work around is to override each method in derived controller and add attributes there:
public class DerivedController :EntityController<MyEntity>
{
[ProducesResponseType(typeof(TEntity), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(Object), (int) HttpStatusCode.NotFound)]
public IActionResult Get(string id)
{
return base.Get(id);
}
}
Ihis is very inconvenient that I should override every REST methods in every controller, just for use the concrete TEntity type in attributes. :-(
Any better work arounds?
Since .NET Core 2.1 instead of using IActionResult, you can use ActionResult<TEntity> as returntype (or Task<ActionResult<TEntity>>) and then swagger will also know the returntype for 200 calls!
Althrough I found no way to use generic type parameter in ProducesResponseTypeAttribute, I found another way to make swagger work:
Use IApplicationModelConvention to update ApplicationModel, which is used by swagger.
public class EntityControllerConversion : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
ActionModel action = ... // finds the controller action
Type viewModelType = ... // get the view type by reflection from the controller
SetResponseUsingHack(action, viewModelType, HttpStatusCode.OK);
}
private void SetResponseUsingHack(ActionModel actionModel, Type responseType, HttpStatusCode statusCode)
{
if (actionModel == null) throw new ArgumentNullException(nameof(actionModel));
if (responseType == null) throw new ArgumentNullException(nameof(responseType));
var writable = (IList<object>)(actionModel.Attributes);
var attribute = FindResponseAttributeUsingHack(writable, statusCode);
if (attribute != null)
{
attribute.Type = responseType;
}
}
private ProducesResponseTypeAttribute FindResponseAttributeUsingHack(IList<object> attributes, HttpStatusCode statusCode)
{
if (attributes == null) return null;
var result = attributes.OfType<ProducesResponseTypeAttribute>()
.Where(x => x.Type == typeof(ProducesResponseStub))
.FirstOrDefault(x => x.StatusCode == (int) statusCode);
return result;
}
}
public abstract class EntityController<TEntity>
{
[HttpGet]
[ProducesResponseType(typeof(ProducesResponseStub), 200)]
public IActionResult Get(string id)
{
}
}
public static class ProducesResponseStub
{
}
NOTE: Swagger won't work correctly if you just add a new ProducesResponseTypeAttribute instance to ActionModel.Attributes, may be it's a bug in swagger or in asp.net core. That why I use ProducesResponseStub in decorating action methods in EntityController and replace them with correct types in EntityControllerConversion.
The DefaultApiDescriptionProvider in package Microsoft.AspNetCore.Mvc.ApiExplorer has been improved so that it will pick up method return type.
Note that the [ProducesResponseType(StatusCodes.Status200OK)] attribute is required when other [ProducesResponseType] attributes are specified as shown below:
[HttpGet, Route("")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetailsResponse))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetailsResponse))]
public async Task<IEnumerable<TResponse>> GetAll(int imoNo)
{
var parameters = await _parameterService.GetAllAsync(imoNo);
return parameters.Select(x => MapToResponse(x));
}
Confirmed to work in Microsoft.AspNetCore.Mvc.ApiExplorer v2.2.0.
Also work with StatusCodes.Status201Created.
I'm writing a simple blog application and trying to establish CRUD operations in my generic repository pattern but I'm getting an error on my update method that says:
'System.Data.Entity.DbSet' does not contain a definition for
'Entry' and no extension method 'Entry' accepting a first argument of
type 'System.Data.Entity.DbSet' could be found (are you missing a
using directive or an assembly reference?)
I followed a post that explained how to 'fake' Entry() by adding additional level of indirection over DbContext. However in MVC 5 we're inheriting from: IdentityDbContext and not DbContext. I did try implementing the authors fix but the error persists.
My Question
How can I add an update method to my repository in Entity Framework 6 using IdentityDbContext? If we aren't supposed to do it this way then how do I update a record with this pattern?
I should note that all other the other methods work as expected.
My generic Repository:
public class BlogEngineRepository<T> : IRepository<T> where T : class
{
protected DbSet<T> DbSet;
public BlogEngineRepository(DbContext dataContext)
{
DbSet = dataContext.Set<T>();
}
#region IRepository<T> Members
public void Insert(T entity)
{
DbSet.Add(entity);
}
public void Delete(T entity)
{
DbSet.Remove(entity);
}
public void Update(T entity)
{
DbSet.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
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
}
Ok, I figured this out. The reason why there isn't an Update method in new repository patterns (Entity Framework 6) is because there's no need for one. You simply fetch your record by id, make your changes and then commit/save.
For example, this is my edit POST method from my postController:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit([Bind(Include = "Id,Title,IntroText,Body,Modified,Author")] Post post)
{
using (UnitOfWork uwork = new UnitOfWork())
{
Post edit = uwork.PostRepository.GetById(post.Id);
edit.Title = post.Title;
edit.IntroText = post.IntroText;
edit.Body = post.Body;
edit.Modified = DateTime.Now;
uwork.Commit();
return RedirectToAction("Index");
}
}
RepositoryPattern looks like this:
public class BlogEngineRepository<T> : IRepository<T> where T : class
{
protected DbSet<T> DbSet;
public BlogEngineRepository(DbContext dataContext)
{
DbSet = dataContext.Set<T>();
}
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);
}
}
Update should look like (expanding on Dan Beaulieu's answer) :
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit([Bind(Include = "Id,Title,IntroText,Body,Modified,Author")] Post post)
{
using (UnitOfWork uwork = new UnitOfWork())
{
post.Modified = DateTime.Now;
uwork.PostRepository.Update(post);
uwork.Commit();
return RedirectToAction("Index");
}
}
RepositoryPattern looks like this:
public class BlogEngineRepository<T> : IRepository<T> where T : class
{
public BlogEngineRepository(DbContext dataContext)
{
DbSet = dataContext.Set<T>();
Context = dataContext;
}
public T Update(T entity)
{
DbSet.Attach(entity);
var entry = Context.Entry(entity);
entry.State = System.Data.EntityState.Modified;
}
}
You can view a full explaination to the answer for Efficient way of updating list of entities for more information on the details of just an update.
Ok, so I have customized ApplicationUser, I need to load up a collection when the user logs in, and I'm just trying to find the best way to do that. I've looked at the examples on the web, but those only customize the ApplicationUser by adding custom properties (not Collections). I need to load up an IList<> when the user logs in. I see a couple of ways to do this, and I'm wondering which would be considered the "best" way (I know this is somewhat subjective, but I think this is somewhat new ground).
Anyway, in the Login method on the AcccountController we have this:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I could add a line after the await SigninAsync() to load my collection, but that seems wasteful, as the UserManager is already going to the database. What I really want is some way to tell the user manager to .Include() my collection when it queries the database for the user. What I'm looking at is inheriting from the UserStore<ApplicationUser> and overriding the FindByNameAsync() function that is called by the UserManager:
public virtual Task<TUser> FindByNameAsync(string userName)
{
this.ThrowIfDisposed();
IQueryable<TUser> entitySet =
from u in this._userStore.EntitySet
where u.UserName.ToUpper() == userName.ToUpper()
select u;
return Task.FromResult<TUser>(entitySet.FirstOrDefault<TUser>());
}
and override it for the customized ApplicationUser that I'm using and load up my properties. Is this the way this should be done, or am I overlooking something in AspNet.Identity somewhere that would handle this more simply?
So that blew up in my face quickly, it would seem you aren't going to inherit from UserStore<TUser> and get access to anything that you might actually want to use when overriding one of these methods, as it's all internal or private. So short of copying and pasting all of UserStore<TUser> and reworking the one method I want are there any suggestions?
Here is one way of doing this:
public class PatientPortalUserStore<TUser> : UserStore<TUser>
where TUser : ApplicationUser
{
public PatientPortalUserStore(DbContext context) : base(context)
{
this._userStore = new EntityStore<TUser>(context);
}
private EntityStore<TUser> _userStore;
public override Task<TUser> FindByNameAsync(string userName)
{
//This is the important piece to loading your own collection on login
IQueryable<TUser> entitySet =
from u in this._userStore.EntitySet.Include(u=>u.MyCustomUserCollection)
where u.UserName.ToUpper() == userName.ToUpper()
select u;
return Task.FromResult<TUser>(entitySet.FirstOrDefault<TUser>());
}
}
//Had to define this because EntityStore used by UserStore<TUser> is internal
class EntityStore<TEntity>
where TEntity : class
{
public DbContext Context
{
get;
private set;
}
public DbSet<TEntity> DbEntitySet
{
get;
private set;
}
public IQueryable<TEntity> EntitySet
{
get
{
return this.DbEntitySet;
}
}
public EntityStore(DbContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
this.Context = context;
this.DbEntitySet = context.Set<TEntity>();
}
public void Create(TEntity entity)
{
this.DbEntitySet.Add(entity);
}
public void Delete(TEntity entity)
{
this.Context.Entry<TEntity>(entity).State = EntityState.Deleted;
}
public virtual Task<TEntity> GetByIdAsync(object id)
{
return this.DbEntitySet.FindAsync(new object[] { id });
}
}