i am wondering if i can pass table name as Parameter in EF CORE.
This is static example:
var post = await factory.Posts.SingleOrDefaultAsync(x => x.Id == id);
i define table staticlly here as "factory.Posts"
but.. i would want to pass this dynamically in parameter.
This is example (not working)
public async Task Accept(int id,Type type)
{
if (type == typeof(PostEntity))
{
using var factory = _contextFactory.CreateDbContext();
var post = await factory.type.SingleOrDefaultAsync(x => x.Id == id);
post!.IsVerified = true;
await factory.SaveChangesAsync();
}
}
public async Task Accept(int id,Type type)
{
if (type == typeof(PostEntity))
{
using var factory = _contextFactory.CreateDbContext();
var post = await factory.Set<PostEntity>().SingleOrDefaultAsync(x => x.Id == id);
post!.IsVerified = true;
await factory.SaveChangesAsync();
}
}
Or dynamically by using the Find method :
public virtual object Find([NotNull] Type entityType, [CanBeNull] params object[] keyValues)
so in the case :
public async Task Accept(int id,Type type)
{
using var factory = _contextFactory.CreateDbContext();
dynamic post = contextFactory().Find(type,id)
post!.IsVerified = true;
await factory.SaveChangesAsync();
}
The best solution is :
Create a base class BaseEntity (conatins IsVerified , id )
Configure it in the context
use it to retrieve your item
public static async Task<Bureau> getBubreauById(int? idBureau)
{
if (idBureau > 0)
{
using (var dbContext = new AppDbContext(optionsBuilder.Options))
{
return await dbContext.Bureaus.FirstOrDefaultAsync(x => x.id == idBureau);
}
}
return null;
}
And in the MapperInitializer
.ForMember(aud => aud.LibelleBureauEnlevement, o => o.MapFrom(async au => (await SmartTransitHelper.getBubreauById(au.bureau_dedouanement)).libelle)
Here I wanna Map the LibelleBureauEnlevement to the returned libelle value from SmartTransitHelper.getBubreauById(au.bureau_dedouanement)
but I have this error, I wanna know what is wrog, why it's not workin?
I'm working on a unit test and I'd like to verify that a mock object received the proper predicate argument. However, I fail to make it work.
Here's a skeleton of the code:
public interface IRepository<T>
{
Task<T> SingleOrDefaultAsync(Expression<Func<T, bool>> predicate);
}
public class MyClass
{
private readonly IRepository<Foo> _repository
public MyClass(IRepository<Foo> repository)
{
_repository = repository;
}
public Task<bool> MyMethod(int id)
{
var foo = _repository.SingleOrDefaultAsync(x => x.Id == id);
return foo != null;
}
}
and in my test class I have the following method
public async Task MyTestMethod()
{
var repository = Substitute.For<IRepository<Foo>>();
repository.SingleOrDefaultAsync(x => x.Id == 123).Returns(Task.FromResult(new Foo()));
var myClass = new MyClass(repository);
var result = await myClass.MyMethod(123);
result.Should().BeTrue();
}
But as mentioned above, this test fails. I could make it pass by using Arg.Any<Expression<Func<Foo, bool>>, but it doesn't feel right.
Anyone has a suggestion what I'm doing wrong?
Capture the expression passed to the mock and use it in the Returns to verify the expected behavior.
For example
public async Task MyTestMethod() {
//Arrange
var id = 123;
var model = new Foo() {
Id = id;
}
var repository = Substitute.For<IRepository<Foo>>();
repository.SingleOrDefaultAsync(Arg.Any<Expression<Func<Foo, bool>>())
.Returns(args => {
var expression = args.Arg<Expression<Func<Foo, bool>>(); //capture expression
Foo result = expression.Compile()(model) ? model : null; //use to verify behavior
Task.FromResult(result);
});
var myClass = new MyClass(repository);
//Act
var actual = await myClass.MyMethod(id);
//Assert
actual.Should().BeTrue();
}
First the code,
Generic Interface:
public interface IEntityService<TEntity> where TEntity : class
{
IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
Task<TEntity> GetByIDAsync(object id);
Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
}
Generic Class with interface implementation:
public class EntityService<TEntity> : IEntityService<TEntity> where TEntity : class
{
protected IContext IContext;
protected DbSet<TEntity> IDbSet;
public EntityService(IContext context)
{
IContext = context;
IDbSet = IContext.Set<TEntity>();
}
public virtual IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = IDbSet;
if (filter != null)
{
query = query.Where(filter);
}
query = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
if (orderBy != null)
{
return orderBy(query);
}
return query;
}
public virtual async Task<TEntity> GetByIDAsync(object id)
{
return await IDbSet.FindAsync(id);
}
public virtual async Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return await IDbSet.FirstOrDefaultAsync(predicate);
}
}
Specific interface:
public interface ILoginService
{
Task<UserProfileViewModel> GetLoginDetailAsync(string userName);
}
Specific class: Implementing generic class and specific interface
public class LoginService : EntityService<UserAccount>, ILoginService
{
private readonly IContext _iContext;
public LoginService(IContext context): base(context)
{
_iContext = context;
}
async Task<UserProfileViewModel> ILoginService.GetLoginDetailAsync(string userName)
{
var userAcount = await GetFirstOrDefaultAsync(c => c.Username.ToLower() == userName.Trim().ToLower() && c.Active == true);
if (userAcount != null)
{
return Mapper.Map<UserAccount, UserProfileViewModel>(userAcount);
}
return null;
}
}
Now, I am supposed to test LoginService the one and only method it has
Here's the test code
[Test]
public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
{
var userName = "should not exist!";
var userAccount = new List<UserAccount>()
{
new UserAccount
{
ID = 1,
Name = "Test User"
}
}.AsQueryable();
var mockSet = new Mock<DbSet<UserAccount>>();
var userProfileViewModel = new UserProfileViewModel
{
ID = 1,
Name = Guid.NewGuid().ToString().Substring(0, 8)
};
_context.Setup(c => c.Set<UserAccount>()).Returns(mockSet.Object);
loginService = new LoginService(_context.Object);
mockSet.As<IDbAsyncEnumerable<UserAccount>>().
Setup(m => m.GetAsyncEnumerator()).
Returns(new TestDbAsyncEnumerator<UserAccount>(userAccount.GetEnumerator()));
mockSet.As<IQueryable<UserAccount>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<UserAccount>(userAccount.Provider));
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(userAccount.Expression);
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(userAccount.ElementType);
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(userAccount.GetEnumerator());
var result = await ((ILoginService)loginService).GetLoginDetailAsync(userName);
Assert.IsNull(result);
}
Now, these TestDbAsyncEnumerator and TestDbAsyncQueryProvider are taken from msdn to test Async queries in EF.
The problem
The test throws an exception, that Message: System.NotImplementedException : The member 'IQueryable.Provider' has not been implemented on type 'DbSet1Proxy' which inherits from 'DbSet1'. Test doubles for 'DbSet1' must provide implementations of methods and properties that are used. Basically, I have not setup the FirstOrDefaultAsync for mockSet that is getting called in GetLoginDetailAsync (it calls to EntityService, that ends up calling the FirstOrDefaultAsync of IDbSet).
I don't know how can I mock that, because the LoginService doesn't directly inherits it. It inherit's the EntityService which in turn has that generic method FirstOrDefaultAsync. I am stuck at how to set up that.
One another thing that I thought was try this
var loginMock = new Mock<LoginService>(_context.Object);
loginMock.As<ILoginService>().Setup(c => c.GetLoginDetailAsync(It.IsAny<string>())).Returns(Task.FromResult<UserProfileViewModel>(null));
loginMock.As<IEntityService<UserAccount>>().Setup(c => c.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>())).Returns(Task.FromResult(userAccount.First()));
But I don't think this is the correct way to go, as I would only be testing the mock object. Can anyone suggest me how do I get to setup and test/mock this GetFirstOrDefaultAsync, or am I totally going in a wrong direction?
UPDATE AFTER ANSWER:
After the answer from #ODawgG, I am updating this. The test worked fine as specified in answer, but now the other test is failing. I wanted to test, if a particular user exits in the system.
Here's the test code:
[Test]
public async Task Test3()
{
var userAccount = new List<UserAccount>()
{
new UserAccount
{
ID = 1,
Username = "User"
}
}.AsQueryable();
var mockSet = new Mock<DbSet<UserAccount>>();
mockSet.As<IDbAsyncEnumerable<UserAccount>>().
Setup(m => m.GetAsyncEnumerator()).
Returns(new TestDbAsyncEnumerator<UserAccount>(userAccount.GetEnumerator()));
mockSet.As<IQueryable<UserAccount>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<UserAccount>(userAccount.Provider));
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(userAccount.Expression);
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(userAccount.ElementType);
mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(userAccount.GetEnumerator());
AutoMapConfiguration.Configure();
var entityService = new Mock<IEntityService<UserAccount>>();
entityService
.Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
.ReturnsAsync(
(Expression<Func<UserAccount, bool>> predicate) => userAccount.FirstOrDefault(predicate)
);
var loginService = new LoginService(entityService.Object);
// Act
var result = await ((ILoginService)loginService).GetLoginDetailAsync("User");
// Assert
Assert.IsNotNull(result);
}
This test should pass, as it should query on the userAccount but it fails, when I was debugging, and it went inside the LoginService, and I checked _entityService.Get().ToList() it says 0 count, while it should really say count 1, the userAccount that I have setup.
Afaik, the IDbSet is still not setup, and that's why the count is 0, and it's not returning true. How do I setup that? If it is correct, then why is this test failing? Also, I know that moq isn't really good for testing expression, but I got this predicate part of code from here.
I agree with #Fabio. There no need to inherit from EntityService<T> but rather inject into your LogService class.
Refactored your class would look like the following:
public class LoginService : ILoginService
{
private readonly IEntityService<UserAccount> _entityService;
public LoginService(IEntityService<UserAccount> entityService)
{
_entityService = entityService;
}
async Task<UserProfileViewModel> ILoginService.GetLoginDetailAsync(string userName)
{
var userAcount = await _entityService.GetFirstOrDefaultAsync(c => c.Username.ToLower() == userName.Trim().ToLower() && c.Active);
if (userAcount != null)
{
return Mapper.Map<UserAccount, UserProfileViewModel>(userAcount);
}
return null;
}
}
And your test would look like this:
[Test]
public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
{
// Arrange
MapperInitialize.Configure();
var entityService = new Mock<IEntityService<UserAccount>>();
entityService
.Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
.ReturnsAsync(new UserAccount
{
ID = 1,
Name = "Test User"
});
var loginService = new LoginService(entityService.Object);
// Act
var result = await ((ILoginService)loginService).GetLoginDetailAsync(It.IsAny<string>());
// Assert
Assert.IsNotNull(result);
}
Here's the updated test to include testing the expression:
[Test]
public async Task GetLoginDetailAsync_InvalidUsername_ReturnsNull()
{
// Arrange
MapperInitialize.Configure();
var entityService = new Mock<IEntityService<UserAccount>>();
var userAccount = new UserAccount
{
ID = 1,
Username = "Test User",
Active = true
};
var expressionResult = false;
entityService
.Setup(service => service.GetFirstOrDefaultAsync(It.IsAny<Expression<Func<UserAccount, bool>>>()))
.Callback<Expression<Func<UserAccount, bool>>>(expression =>
{
expressionResult = expression.Compile().Invoke(userAccount);
})
.ReturnsAsync(userAccount);
var loginService = new LoginService(entityService.Object);
// Act
var result = await ((ILoginService)loginService).GetLoginDetailAsync("Test User");
// Assert
Assert.IsTrue(expressionResult);
Assert.IsNotNull(result);
}
Why is there no async version of CreateDocumentQuery?
This method for example could have been async:
using (var client = new DocumentClient(new Uri(endpointUrl), authorizationKey, _connectionPolicy))
{
List<Property> propertiesOfUser =
client.CreateDocumentQuery<Property>(_collectionLink)
.Where(p => p.OwnerId == userGuid)
.ToList();
return propertiesOfUser;
}
Good query,
Just try below code to have it in async fashion.
DocumentQueryable.CreateDocumentQuery method creates a query for documents under a collection.
// Query asychronously.
using (var client = new DocumentClient(new Uri(endpointUrl), authorizationKey, _connectionPolicy))
{
var propertiesOfUser =
client.CreateDocumentQuery<Property>(_collectionLink)
.Where(p => p.OwnerId == userGuid)
.AsDocumentQuery(); // Replaced with ToList()
while (propertiesOfUser.HasMoreResults)
{
foreach(Property p in await propertiesOfUser.ExecuteNextAsync<Property>())
{
// Iterate through Property to have List or any other operations
}
}
}
Based on Kasam Shaikh's answer I've created ToListAsync extensions
public static async Task<List<T>> ToListAsync<T>(this IDocumentQuery<T> queryable)
{
var list = new List<T>();
while (queryable.HasMoreResults)
{ //Note that ExecuteNextAsync can return many records in each call
var response = await queryable.ExecuteNextAsync<T>();
list.AddRange(response);
}
return list;
}
public static async Task<List<T>> ToListAsync<T>(this IQueryable<T> query)
{
return await query.AsDocumentQuery().ToListAsync();
}
You can use it
var propertiesOfUser = await
client.CreateDocumentQuery<Property>(_collectionLink)
.Where(p => p.OwnerId == userGuid)
.ToListAsync()
Note that request to have CreateDocumentQuery async is open on github