How can I fix errors in ProductsContoller.cs and ProductRepository.cs files?
IGenericRepository
namespace API.Core.Interfaces
{
public interface IGenericRepository<T> where T : BaseEntity
{
T Add(T entity);
}
}
GenericRepository
namespace API.Infrastructure.Implements
{
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private readonly StoreContext _context;
public GenericRepository(StoreContext context)
{
_context = context;
}
public T Add(T entity)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
return entity;
}
}
}
Error1
ProductsController
(field) IGenericRepository ProductsController._productAddRepository
CS1061: 'Product' does not contain a definition for 'GetAwaiter' and no accessible extension
method 'GetAwaiter' accepting a first argument of type 'Product' could be found
(are you missing a using directive or an assembly reference?)
namespace API.Controllers
{
public class ProductsController : BaseApiController
{
private IGenericRepository<Product> _productAddRepository;
private readonly IGenericRepository<Product> _productRepository;productRepository,
public ProductsController(IGenericRepository<Product> productRepository,
IGenericRepository<Product> productAddRepository,
IMapper mapper)
{
// _productAddRepository = productAddRepository;
_productRepository = productRepository;
}
[HttpPost("addproduct")]
public async Task<ActionResult<Product>> AddProduct(Product product)
{
**var data = await _productAddRepository.Add(product);**
return Ok();
}
}
}
IProductRepository
namespace API.Core.Interfaces
{
public interface IProductRepository
{
Task<Product> AddProductAsync(Product product);
}
}
ProductRepository
Error2
(awaitable) Task DbContext.SaveChangesAsync([System.Threading. Cancellation Token cancellation Token = default]) (+ 1 overload)
Saves all changes made in this context to the database.
This method will automatically call Microsoft.Entity FrameworkCore.Change Tracking.Change Tracker.DetectChanges() to
discover any changes to entity instances before saving to the underlying database.
This can be disabled via Microsoft.Entity Framework Core.ChangeTracking.ChangeTracker.AutoDetectChangesEnabled.
Multiple active operations on the same context instance are not supported. Use 'await' to
ensure that any asynchronous operations have completed before calling another method on this context.
Returns:
A task that represents the asynchronous save operation. The task result contains the number of state entries written to the database.
Exceptions:
DbUpdateException
DbUpdateConcurrencyException
CS0029: Cannot implicitly convert type 'int' to 'API.Core.DbModels.Product'.
namespace API.Infrastructure.Implements
{
public class ProductRepository : IProductRepository
{
private readonly StoreContext _context;
public ProductRepository(StoreContext context)
{
_context = context;
}
public async Task<Product> AddProductAsync(Product product)
{
var newBrand = new Product()
{
PictureUrl=product.PictureUrl,
Name = product.Name,
Description=product.Description,
ProductType=product.ProductType,
ProductBrand=product.ProductBrand
};
_context.Products.Add(newBrand);
**return await _context.SaveChangesAsync();**
}
}
}
You're call stack looks like:
Task<ActionResult<List<Product>>> AddProduct(Product product)
// you can't await Add()
var data = await _productAddRepository.Add(product);
// Add is not async
T Add(T entity)
You can't await Add() it's not async:
public T Add(T entity)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
return entity;
}
If you want this to work, you need to change the code so that Add() is async:
public async TaskT<T> Add(T entity)
{
_context.Set<T>().Add(entity);
await _context.SaveChangesAsync();
return entity;
}
Async Return Types:
Async methods can have the following return types:
Task, for an async method that performs an operation but returns no value.
Task, for an async method that returns a value.
void, for an event handler.
Any type that has an accessible GetAwaiter method. The object returned by the GetAwaiter method must implement the System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
IAsyncEnumerable, for an async method that returns an async stream.
The return type of Add() is T, which is not one of the previous requirements.
Related
In my asp.net core 6 mvc project, I have a transient service ContentService. This service is inherited from IContentService and added to Program.cs using
builder.Services.AddTransient<IContentService, ContentService>();
Here is the implementation of the service:
public interface IContentService
{
public string GetText(int id);
}
public class ContentService : IContentService
{
private readonly ContentDbContext _context;
private List<string> textList;
public ContentService(ContentDbContext context)
{
_context = context;
ReadTexts();
}
public void ReadTexts()
{
// Reads text from the database and fills the textList.
// THIS CODE MUST RUN ONLY FOR THE FIRST REQUEST.
}
public string GetText(int id)
{
return textList(id);
}
}
The problem is every time ContentService is requested, the constructor runs and ReadTexts() is called, which is a heavy function that must only run for the first request. Is there anyway to implement a transient service with a code block that runs only for the first request? Note that I cannot use AddSingleton.
You have to make textList as static if you want it to hold data which should be read only on first request.
try this.
public class ContentService : IContentService
{
private readonly ContentDbContext _context;
private static List<string> textList = new List<string>();
public ContentService(ContentDbContext context)
{
_context = context;
ReadTexts();
}
public void ReadTexts()
{
if(!textList.Any())
{
// Reads text from the database and fills the textList.
// THIS CODE MUST RUN ONLY FOR THE FIRST REQUEST.
}
}
public string GetText(int id)
{
return textList(id);
}
}
I am trying to understand how MediatR works. Never used this library before. Below code is not actual production code. It is only for understanding purpose.
Lets say I have two RequestHandlers. Each handler takes ProductModel as request but returns different type of response.
public class GetOrdersHandler : IRequestHandler<ProductModel, IEnumerable<Order>>
{
private readonly FakeDataStore _fakeDataStore;
public GetOrdersHandler(FakeDataStore fakeDataStore)
{
_fakeDataStore = fakeDataStore;
}
public async Task<IEnumerable<Order>> Handle(ProductModel request,CancellationToken cancellationToken
{
return await _fakeDataStore.GetAllOrdersForProduct(request);
}
}
public class SaveProductHandler : IRequestHandler<ProductModel, Product>
{
private readonly FakeDataStore _fakeDataStore;
public SaveProductHandler(FakeDataStore fakeDataStore)
{
_fakeDataStore = fakeDataStore;
}
public async Task<Product> Handle(ProductModel request,CancellationToken cancellationToken)
{
return await _fakeDataStore.SaveProduct(request);
}
}
Then in the same controller I have two action methods
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator) => _mediator = mediator;
[HttpPost]
public async Task<ActionResult> GetAllOrders(ProductModel model)
{
var product = await _mediator.Send(model);
return Ok(product);
}
[HttpPost]
public async Task<ActionResult> SaveProduct(ProductModel model)
{
var product = await _mediator.Send(model);
return Ok(product);
}
}
Based on the MediatR code this may not work. Looks like the Send method creates instance of handler based on Request type. It keeps dictionary of RequestType and corresponding handler.
If my assumption is correct then does that mean I will have to create unique request model for each action method that will be using Send method?
I have a standard .Net core Api and want to use a Open Generic IReposistory and decorate that with a DomainEventPublisher for pushing out events to servicsBus after persisting.
However, I have used Simple Injector a lot earlier which I'm a big fan of. But now when using MediatR Im trying to simplify DI by using just .net Core DI together with Scrutor package for decorating.
Problem is an error I get:
"The number of generic arguments provided doesn't equal the arity of the generic type definition." from Scrutor when trying to register decorator in Startup (2nd line below).
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>));
services.Decorate(typeof(IRepository<>), typeof(DomainEventPublisher<>));
I have closed these generic classes/interfaces and then it works. But Im not good with that. I would to i the right way like I used to do in Simpleinjector, and register open generic decorator.
Any suggestions what might be the problem?
public class Repository<TEntity> : IRepository<TEntity>
{
private readonly CosmosClient _client;
private readonly IDataContext<TEntity> _context;
private readonly Container _container;
public Repository(CosmosClient client, IDataContext<TEntity> context)
{
_client = client;
_context = context ?? throw new ArgumentNullException(nameof(context));
_container = _client.GetContainer(_context.GetDatabase(), _context.GetContainer());
}
public virtual async Task Add(TEntity entity)
{
try
{
var response = await _container.CreateItemAsync(entity, new PartitionKey(_context.GetPartitionKey(entity)));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public virtual async Task<TEntity> Get(string id)
{
var response = await _container.ReadItemAsync<TEntity>(id, new PartitionKey(_context.GetPartitionKey(id)));
return response.Resource;
}
public virtual async Task<TEntity> Update(TEntity entity)
{
var response = await _container.UpsertItemAsync(entity, new PartitionKey(_context.GetPartitionKey(entity)));
return response.Resource;
}
public async Task Remove(string id)
{
var response = await _container.DeleteItemAsync<TEntity>(id, new PartitionKey(_context.GetPartitionKey(id)));
}
public class DomainEventPublisher<TEntity> : IRepository<TEntity>
{
private readonly IRepository<TEntity> _decoratedRepository;
private readonly ITopicAdapter _bus;
private readonly IMapper _mapper;
private List<IDomainEvent> _eventsToProcess = new List<IDomainEvent>();
public DomainEventPublisher(IRepository<TEntity> decoratedRepository, ITopicAdapter bus, IMapper mapper)
{
_decoratedRepository = decoratedRepository ?? throw new ArgumentNullException(nameof(decoratedRepository));
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public async Task Add(TEntity entity)
{
// Get all domain events raised by source entity
var events = CollectEvents(entity);
await _decoratedRepository.Add(entity);
await HandleEvents(events);
}
public async Task<TEntity> Get(string id)
{
return await _decoratedRepository.Get(id);
}
public async Task<TEntity> Update(TEntity entity)
{
// Get all domain events raised by source entity
var events = CollectEvents(entity);
var result = await _decoratedRepository.Update(entity);
await HandleEvents(events);
return result;
}
public async Task Remove(string id)
{
await _decoratedRepository.Remove(id);
}
private List<IDomainEvent> CollectEvents(TEntity entity)
{
if (entity is IEntity entityWithEvents)
return entityWithEvents.Events;
return new List<IDomainEvent>();
}
private async Task HandleEvents(List<IDomainEvent> events)
{
// if we ended up on this line we know that repository persisted changes and now send events to bus
foreach (var domainEvent in events)
{
await _bus.Send(_mapper.MapTo(domainEvent));
}
}
}
It's impossible to apply decorators to open-generic registration with Scrutor. This is discussed here on the Scrutor forum. This is due to a limitation of the underlying Microsoft DI Container. This is a limitation that can't be circumvented by Scrutor.
Instead, switch to one of the mature DI Containers that do support this.
I have following code:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IWsApiProvider, WsApiProvider>();
services.AddScoped<IApplicationUserRepository, ApplicationUserRepository>();
...
}
WsApiProvider has following:
public Guid SessionId { get; set; }
public IWSocketProvider WsApi { get; set; }
In Invoke method I'm updating these properties:
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
...
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
...
}
And then I'm going to Controller where I injected Repository:
public AccountController(IApplicationUserRepository applicationUserRepository)
{
this._applicationUserRepository = applicationUserRepository;
}
public ApplicationUserRepository(IWsApiProvider wsApi) : base(wsApi)
{
}
And here I have wsApi object with empty properties. Two questions:
Why in repository constructor I have this object with empty properties?
Is there any way to create one instance of IWsApiProvider for all dependencies per request (non-singleton solution)?
Thank you in advance
UPDATED. The whole middleware class:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
private readonly IWsApiProvider _wsApiProvider;
private const string QisSessionId = "QisSessionId";
public WsApiMiddleware(RequestDelegate next, IWsApiProvider wsApiProvider)
{
_next = next;
this._wsApiProvider = wsApiProvider;
}
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
var sessionId = httpContext.Request.Cookies[QisSessionId];
var sessionGuid = Guid.Empty;
if (!string.IsNullOrEmpty(sessionId))
{
Guid.TryParse(sessionId, out sessionGuid);
}
var connection = ConnectionsPool.GetSocket(sessionGuid);
if (connection == null)
{
connection = new WSocketProvider(null);
var connectTask = Task.Run(async () =>
await connection.Connect(appSettings.Value.WsApiServerEndPointUri, CancellationToken.None)
);
Task.WaitAll(connectTask);
var sessionService = new SessionService(connection);
var sessionOpenTask = Task.Run(async () =>
{
SessionDataState sessionData = null;
//TODO [W-8/6/2017] - think about better solution for situation when sessionId doesn't exist on the server
try
{
sessionData = await sessionService.OpenSession(sessionGuid != Guid.Empty ? (Guid?)sessionGuid : null);
}
catch (Exception ex)
{
sessionData = await sessionService.OpenSession();
}
sessionGuid = sessionData.SessionId;
if (!sessionData.ClientType.HasValue)
{
await sessionService.LoginClient();
}
ConnectionsPool.TryAddConnection(sessionGuid, connection);
httpContext.Response.Cookies.Append(QisSessionId, sessionGuid.ToString());
});
Task.WaitAll(sessionOpenTask);
}
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
return this._next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class WsApiMiddlewareExtensions
{
public static IApplicationBuilder UseWsApiMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<WsApiMiddleware>();
}
}
From the ASP.Net core middleware doc :
Middleware is constructed once per application lifetime. Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request.
And the most important part in you situation:
If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by dependency injection.
Since IWsApiProvider is a scoped service(i.e. per request), it should be passed as an argument to the Invoke method, as follow:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
// no longer passed in the constructor
public WsApiMiddleware(RequestDelegate next)
{
_next = next;
}
// passed as an argument to Invoke, via dependency injection
public Task Invoke(HttpContext httpContext, IWsApiProvider wsApiProvider, IOptions<AppSettings> appSettings)
{
wsApiProvider.SessionId = "SessionId";
wsApiProvider.WsApi = "WsApi";
return this._next(httpContext);
}
}
I am using an ORM to connect to the database it is called dapper. The issue with it is that it's database calls are synchronous and I recently found a way to make it asynchronous by following this short tutorial http://www.joesauve.com/async-dapper-and-async-sql-connection-management/ . My question is how can I bring this BaseRepository into my Controller class ? This is the code on that website and it's the same one I have
BaseRepository- by the way there is no issue in this code
public abstract class BaseRepository
{
private readonly string _ConnectionString;
protected BaseRepository(string connectionString)
{
_ConnectionString = connectionString;
}
protected async Task<T> WithConnection<T>(Func<IDbConnection, Task<T>> getData)
{
try {
using (var connection = new SqlConnection(_ConnectionString)) {
await connection.OpenAsync(); // Asynchronously open a connection to the database
return await getData(connection); // Asynchronously execute getData, which has been passed in as a Func<IDBConnection, Task<T>>
}
}
catch (TimeoutException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
}
catch (SqlException ex) {
throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
}
}
}
and now he brings it in like this
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
The part I am having a problem with is this public class PersonRepository : BaseRepository because Asp.Net Controllers start with public class HomeController: Controller , I need access to the WithConnection method to get this working. My controller looks like this
public class HomeController : Controller
{
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<ActionResult> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return View(topfive);
});
}
}
I obviously can not cover my ActionResult method with the BaseRepository because it gives all types of errors any suggestions ?
Why are you using inheritance instead of composition? What about something like:
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}
public class ConnectionRepository : BaseRepository
{
public ConnectionRepository(string connectionString) : base(connectionString) { }
}
public async Task<List<TopFileClass>> topfive()
{
// I get Error on WithConnection as it can't see the BaseRepository
return await WithConnection(async c =>
{
var topfive = await c.QueryAsync<Streams>("select * from streams ").ToList();
return topfive;
});
}
public class HomeController : Controller
{
private readonly PersonRepository _repo;
public HomeController(PersonRepository repo)
{
_repo = repo;
}
public async Task<ActionResult> TopFive()
{
var top5 = await _repo.topfive();
return View(top5);
}
}
If you are not familiar how to make the repository automatically get injected into the constructor, read up on dependency injection in MVC 6.
you have to intehirt the "BaseRepository" from "Controller". i think that will work for you. then just go with below code:
public abstract class BaseRepository : Controller
{
// do you work
}
public class PersonRepository : BaseRepository
{
public PersonRepository(string connectionString): base (connectionString) { }
public async Task<Person> GetPersonById(Guid Id)
{
return await WithConnection(async c => {
// Here's all the same data access code,
// albeit now it's async, and nicely wrapped
// in this handy WithConnection() call.
var p = new DynamicParameters();
p.Add("Id", Id, DbType.Guid);
var people = await c.QueryAsync<Person>(
sql: "sp_Person_GetById",
param: p,
commandType: CommandType.StoredProcedure);
return people.FirstOrDefault();
});
}
}