Trying to use an async method as I used to do with Repository Pattern to post an entity, but this time I wanted to integrate the Unit Of Work pattern, here is my interface:
public interface IUnitOfWork : IDisposable
{
. . .
void Save();
}
And its implementation:
public class UnitOfWork : IUnitOfWork
{
private readonly DataContext _db;
public UnitOfWork(DataContext db)
{
_db = db;
. . .
}
. . .
public void Dispose()
{
_db.Dispose();
}
public void Save()
{
_db.SaveChanges();
}
}
And here is my method:
[HttpPost]
public async Task<IActionResult> CreateItem(string userId, ItemForCreationDto itemForCreationDto)
{
if (userId != User.FindFirst(ClaimTypes.NameIdentifier).Value)
return Unauthorized();
itemForCreationDto.UserId = userId;
var item = _mapper.Map<Item>(itemForCreationDto);
if (item == null)
return BadRequest("Could not find item");
_uow.Item.Add(item);
if (await _uow.Save()) <--- Error here
{
var itemToReturn = _mapper.Map<ItemToReturnDto>(item);
return CreatedAtRoute("GetItem",
new { userId, id = item.Id }, itemToReturn);
}
throw new Exception("Creating the item failed on save");
}
But I got the following erors:
Can't wait for 'void'
That's because I am trying to call a Save() method which is void from an async HttpPost method, I know that makes no sens, but till now I could not find how to implement that for this special case.
When I tried removing the await I got the following error:
Unable to implicitly convert type 'void' to 'bool'
Any suggestion on how to implement that ?
Either refactor the interface to be async or add an additional member
public interface IUnitOfWork : IDisposable {
//. . .
Task<bool> SaveAsync();
}
that can probably wraps the context's asynchronous API in the implementation if one exists
public async Task<bool> SaveAsync() {
int count = await _db.SaveChangesAsync();
return count > 0;
}
allowing for the desired functionality
//...
if (await _uow.SaveAsync()){
var itemToReturn = _mapper.Map<ItemToReturnDto>(item);
return CreatedAtRoute("GetItem", new { userId, id = item.Id }, itemToReturn);
}
//...
I have API built with .net core 2, and I am trying to implement change log feature.
I have done basic part, but I am not sure if it's a best way for doing this.
Here is my EntityBaseRepository
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
private readonly ApplicationContext context;
public EntityBaseRepository(ApplicationContext context)
{
this.context = context;
}
public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);
public virtual T this[int id] => context.Set<T>().FirstOrDefault(m => m.Id == id);
public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
public virtual T Add(T entity) => Operations(entity: entity, state: EntityState.Added);
public virtual T Update(T entity) => Operations(entity: entity, state: EntityState.Modified);
public virtual T Delete(T entity) => Operations(entity: entity, state: EntityState.Deleted);
public virtual T Operations(T entity, EntityState state)
{
EntityEntry dbEntityEntry = context.Entry<T>(entity);
if (state == EntityState.Added)
{
entity.CreationDateTime = DateTime.UtcNow;
entity.CreationUserId = 1;
context.Set<T>().Add(entity);
dbEntityEntry.State = EntityState.Added;
}
else if (state == EntityState.Modified)
{
entity.LastModificationDateTime = DateTime.UtcNow;
entity.LastModificationUserId = 1;
var local = context.Set<T>().Local.FirstOrDefault(entry => entry.Id.Equals(entity.Id));
if (local != null)
{
context.Entry(local).State = EntityState.Detached;
}
dbEntityEntry.State = EntityState.Modified;
}
else if (state == EntityState.Deleted)
{
entity.DeletionFlag = true;
entity.DeletionUserId = 1;
entity.DeletionDateTime = DateTime.UtcNow;
dbEntityEntry.State = EntityState.Modified;
}
return entity;
}
Here is one of my controller.
[Produces("application/json")]
[Route("api/Item")]
public class ItemController : Controller
{
private readonly IItemRepository repository;
private readonly IChangeLogRepository changeLogRepository;
private readonly IMapper mapper;
public ItemController(IItemRepository repository, IChangeLogRepository _changeLogRepository, IMapper mapper)
{
this.repository = repository;
this.changeLogRepository = _changeLogRepository;
this.mapper = mapper;
}
[HttpPost]
public IActionResult Post([FromBody]ItemDto transactionItemDto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var item = repository.Add(mapper.Map<ItemDto, Item>(source: transactionItemDto));
repository.Commit();
ChangeLog log = new ChangeLog()
{
Log = "New Item Added"
};
changeLogRepository.Add(log);
changeLogRepository.Commit();
return new OkObjectResult(mapper.Map<Item, ItemDto>(source: item));
}
}
if you see in controller, I have added one item, and commited it, then I prepared log for that insertion, added and commited it.
Now, I have few questions, like
I have to commit my transaction twice, is there any way I can optimize it? I don't know if I can handle it on EntityBaseRepository or not.
I also want to check each property, if it gets changed or not. I want to log that to if it's changed. what would be the best way to handle it?
It would be great if anyone can help me with this. really appreciate. thanks.
You can use Action filters for a changelog Like
using System;
public class TrackMyChange : IActionFilter
{
private readonly string _chengeMessage;
private readonly IChangeLogRepository _changeLogRepository;
public TrackMyChange(string changeMessage,IChangeLogRepository changeLogRepository)
{
this._changeLogRepository = changeLogRepository;
this._chengeMessage = chengeMessage;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
ChangeLog log = new ChangeLog()
{Log = this._chengeMessage};
changeLogRepository.Add(log);
changeLogRepository.Commit();
}
}
In your controller, you can use it before actions you want to log like
[TrackMyChange("Your change log here")]
public IActionResult Post()
{
}
Reference :
Action Filter Attributes in .NET core
DbContext is shared between multiple repositories within the same HTTP request scope. You won't need UoW. Just try use one Commit() and see if all changes are saved in one transaction.
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implemenation-entity-framework-core
Use yourDbContext.Entry(your_entity_obj).State to get entity state. Don't log if its state is EntityState.Unchanged.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entitystate?view=efcore-2.1
I want to change all of an object properties using entity framwork.
after searching i got to have this:
Controller,action:
public ActionResult Test()
{
var user = GetCurrentUser();
user.FirstName = "BLAH BLAH";
new UserRepository().UpdateUser(user);
return RedirectToAction("Index");
}
and in my UserRepository:
public bool UpdateUser(ApplicationUser target)
{
using (var db = new AppDatabase())
{
db.Entry(target).State = EntityState.Modified;
db.SaveChanges();
return true;
}
}
but when i try execute i got this error
An entity object cannot be referenced by multiple instances of EntityChangeTracker.
so,any ways to fix or any better way?
using entity framework 6.0.0 and .net 4.5
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
You should use same instance of db context for finding and updating, so you UserRepository can be:
class UserRepository : IDisposable //using IDisposable to dispose db context
{
private AppDatabase _context;
public UserRepository()
{
_context = new AppDatabase();
}
public ApplicationUser Find(string id)
{
return _context.Set<ApplicationUser>().Find(id);
}
public void Update(ApplicationUserentity entity)
{
_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
You can use it in controller:
public ActionResult Test()
{
using (var repository = new UserRepository())
{
var user = repository.Find(User.Identity.GetUserId());
user.FirstName = "BLAH BLAH";
repository.Update(user);
}
return RedirectToAction("Index");
}
I also think using some dependency injection framework would be beneficial for you. So go for it!!
Be sure that all objects came from the same context!
var userContextOne = new MyDbContext();
var user = userContextOne.Users.FirstOrDefault();
var AppDbContextTwo = new MyDbContext();
// Warning when you work with entity properties here! Be sure that all objects came from the same context!
db.Entry(target).State = EntityState.Modified;
AppDbContextTwo.SaveChanges();
The scond problem (not related to the exception!):
db.Entry(target).State = EntityState.Modified;
Why you are doing that?! You dont not have Detached Scenario? did you have disabled your Changetracker? anyway just execute DetectChanges and this method will find the changed data you do not have to do it by your self.
I'm currently writing a C# Web Api in Visual Studio 2015. I'm actually copy pasting quite a lot of code.
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
});
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
}).Where(drones => drones.iddrones == id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
How should I refactor that? At first I thought about moving the var to a class member, but that doesn't seem to be allowed.
I would make a DTO factory method that worked on IQueryable<T> and then the two functions would only be responsible for creating the proper query.
This will position you better in the future when you make these functions async.
public class DroneDTO
{
public int Id { get; set; }
public static IEnumerable<DroneDTO> CreateFromQuery(IQueryable<Drone> query)
{
return query.Select(r=> new DroneDTO
{
Id = r.Id
});
}
}
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = DroneDTO.CreateFromQuery(db.drones);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = DroneDTO.CreateFromQuery(db.drones.Where(d => d.iddrone == id));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
I had the same problem about one year ago and unified the code by few steps:
At First, I separated my business logic from the controller in another classes. It's not one to one separation, I created class for each Entity. The other way is to using CQRS for each query/command. The general case that my business logic always returns one of this models:
public class OutputModel
{
[JsonIgnore]
public OperationResult Result { get; private set; }
public OutputDataModel(OperationResult result)
{
Result = result;
}
#region Initializatiors
public static OutputModel CreateResult(OperationResult result)
{
return new OutputModel(result);
}
public static OutputModel CreateSuccessResult()
{
return new OutputModel(OperationResult.Success);
}
#endregion Initializatiors
}
public class OutputDataModel<TData> : OutputModel
{
public TData Data { get; private set; }
public OutputDataModel(OperationResult result)
: base(result)
{
}
public OutputDataModel(OperationResult result, TData data)
: this(result)
{
Data = data;
}
#region Initializatiors
public static OutputDataModel<TData> CreateSuccessResult(TData data)
{
return new OutputDataModel<TData>(OperationResult.Success, data);
}
public static OutputDataModel<TData> CreateResult(OperationResult result, TData data)
{
return new OutputDataModel<TData>(result, data);
}
public new static OutputDataModel<TData> CreateResult(OperationResult result)
{
return new OutputDataModel<TData>(result);
}
#endregion Initializatiors
}
Operation result is an Enumeration that contains something like StatusCode in a platform independent style:
public enum OperationResult
{
AccessDenied,
BadRequest,
Conflict,
NotFound,
NotModified,
AccessDenied,
Created,
Success
}
It allowed me to handle all web api calls on the same manner and uses my business logic not only in web api but in other clients (for example, I created small WPF app which uses my business logic classes to display operational information).
I created base API controller that handle OutputDataModel to compose response:
public class RikropApiControllerBase : ApiController
{
#region Result handling
protected HttpResponseMessage Response(IOutputModel result, HttpStatusCode successStatusCode = HttpStatusCode.OK)
{
switch (result.Result)
{
case OperationResult.AccessDenied:
return Request.CreateResponse(HttpStatusCode.Forbidden);
case OperationResult.BadRequest:
return Request.CreateResponse(HttpStatusCode.BadRequest);
case OperationResult.Conflict:
return Request.CreateResponse(HttpStatusCode.Conflict);
case OperationResult.NotFound:
return Request.CreateResponse(HttpStatusCode.NotFound);
case OperationResult.NotModified:
return Request.CreateResponse(HttpStatusCode.NotModified);
case OperationResult.Created:
return Request.CreateResponse(HttpStatusCode.Created);
case OperationResult.Success:
return Request.CreateResponse(successStatusCode);
default:
return Request.CreateResponse(HttpStatusCode.NotImplemented);
}
}
protected HttpResponseMessage Response<TData>(IOutputDataModel<TData> result, HttpStatusCode successStatusCode = HttpStatusCode.OK)
{
switch (result.Result)
{
case OperationResult.AccessDenied:
return Request.CreateResponse(HttpStatusCode.Forbidden);
case OperationResult.BadRequest:
return Request.CreateResponse(HttpStatusCode.BadRequest);
case OperationResult.Conflict:
return Request.CreateResponse(HttpStatusCode.Conflict);
case OperationResult.NotFound:
return Request.CreateResponse(HttpStatusCode.NotFound);
case OperationResult.NotModified:
return Request.CreateResponse(HttpStatusCode.NotModified, result.Data);
case OperationResult.Created:
return Request.CreateResponse(HttpStatusCode.Created, result.Data);
case OperationResult.Success:
return Request.CreateResponse(successStatusCode, result.Data);
default:
return Request.CreateResponse(HttpStatusCode.NotImplemented);
}
}
#endregion Result handling
}
Now my api controllers almost did not contain the code! Look at the example with really heavy controller:
[RoutePrefix("api/ShoppingList/{shoppingListId:int}/ShoppingListEntry")]
public class ShoppingListEntryController : RikropApiControllerBase
{
private readonly IShoppingListService _shoppingListService;
public ShoppingListEntryController(IShoppingListService shoppingListService)
{
_shoppingListService = shoppingListService;
}
[Route("")]
[HttpPost]
public HttpResponseMessage AddNewEntry(int shoppingListId, SaveShoppingListEntryInput model)
{
model.ShoppingListId = shoppingListId;
var result = _shoppingListService.SaveShoppingListEntry(model);
return Response(result);
}
[Route("")]
[HttpDelete]
public HttpResponseMessage ClearShoppingList(int shoppingListId)
{
var model = new ClearShoppingListEntriesInput {ShoppingListId = shoppingListId, InitiatorId = this.GetCurrentUserId()};
var result = _shoppingListService.ClearShoppingListEntries(model);
return Response(result);
}
[Route("{shoppingListEntryId:int}")]
public HttpResponseMessage Put(int shoppingListId, int shoppingListEntryId, SaveShoppingListEntryInput model)
{
model.ShoppingListId = shoppingListId;
model.ShoppingListEntryId = shoppingListEntryId;
var result = _shoppingListService.SaveShoppingListEntry(model);
return Response(result);
}
[Route("{shoppingListEntry:int}")]
public HttpResponseMessage Delete(int shoppingListId, int shoppingListEntry)
{
var model = new DeleteShoppingListEntryInput
{
ShoppingListId = shoppingListId,
ShoppingListEntryId = shoppingListEntry,
InitiatorId = this.GetCurrentUserId()
};
var result = _shoppingListService.DeleteShoppingListEntry(model);
return Response(result);
}
}
I added an extension method to get current user credentials GetCurrentUserId. If method parameters contains a class that implements IAuthorizedInput that contains 1 property with USerId then I added this info in a global filter. In other cases I need to add this manually. GetCurrentUserId is depend on your authorization method.
It's just a code style, but I called all input models for my business logic with Input suffix (see examples above: DeleteShoppingListEntryInput, ClearShoppingListEntriesInput, SaveShoppingListEntryInput) and result models with output syntax (it's interesting that you no need to declare this types in controller because it's a part of generic class OutputDataModel<TData>).
I'm also used AutoMapper to map my entities to Ouput-classes instead of tons of CreateFromEntity methods.
I'm using an abstraction for data source. In my scenario it was
Repository but this solution has no English documentation then
the better way is to use one of more common solutions.
I also had a base class for my business logic that helps me to create output-models:
public class ServiceBase
{
#region Output parameters
public IOutputDataModel<TData> SuccessOutput<TData>(TData data)
{
return OutputDataModel<TData>.CreateSuccessResult(data);
}
public IOutputDataModel<TData> Output<TData>(OperationResult result, TData data)
{
return OutputDataModel<TData>.CreateResult(result, data);
}
public IOutputDataModel<TData> Output<TData>(OperationResult result)
{
return OutputDataModel<TData>.CreateResult(result);
}
public IOutputModel SuccessOutput()
{
return OutputModel.CreateSuccessResult();
}
public IOutputModel Output(OperationResult result)
{
return OutputModel.CreateResult(result);
}
#endregion Output parameters
}
Finally my "services" with business logic looks like similar to each other. Lets look an example:
public class ShoppingListService : ServiceBase, IShoppingListService
{
private readonly IRepository<ShoppingList, int> _shoppingListRepository;
private readonly IRepository<ShoppingListEntry, int> _shoppingListEntryRepository;
public ShoppingListService(IRepository<ShoppingList, int> shoppingListRepository,
IRepository<ShoppingListEntry, int> shoppingListEntryRepository)
{
_shoppingListRepository = shoppingListRepository;
_shoppingListEntryRepository = shoppingListEntryRepository;
}
public IOutputDataModel<ListModel<ShoppingListDto>> GetUserShoppingLists(GetUserShoppingListsInput model)
{
var shoppingLists =
_shoppingListRepository.Get(q => q.Filter(sl => sl.OwnerId == model.InitiatorId).Include(sl => sl.Entries));
return SuccessOutput(new ListModel<ShoppingListDto>(Mapper.Map<IEnumerable<ShoppingList>, ShoppingListDto[]>(shoppingLists)));
}
public IOutputDataModel<GetShoppingListOutputData> GetShoppingList(GetShoppingListInput model)
{
var shoppingList =
_shoppingListRepository
.Get(q => q.Filter(sl => sl.Id == model.ShoppingListId).Include(sl => sl.Entries).Take(1))
.SingleOrDefault();
if (shoppingList == null)
return Output<GetShoppingListOutputData>(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output<GetShoppingListOutputData>(OperationResult.AccessDenied);
return
SuccessOutput(new GetShoppingListOutputData(Mapper.Map<ShoppingListDto>(shoppingList),
Mapper.Map<IEnumerable<ShoppingListEntry>, List<ShoppingListEntryDto>>(shoppingList.Entries)));
}
public IOutputModel DeleteShoppingList(DeleteShoppingListInput model)
{
var shoppingList = _shoppingListRepository.Get(model.ShoppingListId);
if (shoppingList == null)
return Output(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
_shoppingListRepository.Delete(shoppingList);
return SuccessOutput();
}
public IOutputModel DeleteShoppingListEntry(DeleteShoppingListEntryInput model)
{
var entry =
_shoppingListEntryRepository.Get(
q => q.Filter(e => e.Id == model.ShoppingListEntryId).Include(e => e.ShoppingList).Take(1))
.SingleOrDefault();
if (entry == null)
return Output(OperationResult.NotFound);
if (entry.ShoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
if (entry.ShoppingListId != model.ShoppingListId)
return Output(OperationResult.BadRequest);
_shoppingListEntryRepository.Delete(entry);
return SuccessOutput();
}
public IOutputModel ClearShoppingListEntries(ClearShoppingListEntriesInput model)
{
var shoppingList =
_shoppingListRepository.Get(
q => q.Filter(sl => sl.Id == model.ShoppingListId).Include(sl => sl.Entries).Take(1))
.SingleOrDefault();
if (shoppingList == null)
return Output(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
if (shoppingList.Entries != null)
_shoppingListEntryRepository.Delete(shoppingList.Entries.ToList());
return SuccessOutput();
}
private IOutputDataModel<int> CreateShoppingList(SaveShoppingListInput model)
{
var shoppingList = new ShoppingList
{
OwnerId = model.InitiatorId,
Title = model.ShoppingListTitle,
Entries = model.Entries.Select(Mapper.Map<ShoppingListEntry>).ForEach(sle => sle.Id = 0).ToList()
};
shoppingList = _shoppingListRepository.Save(shoppingList);
return Output(OperationResult.Created, shoppingList.Id);
}
}
Now all routine of creating DTOs, responses and other nonBusinessLogic actions are in the base classes and we can add features in a easiest and clear way. For new Entity creates new "service" (repository will be created automatically in a generic manner) and inherit it from service base. For new action add a method to existing "service" and actions in API. That is all.
It's just a recommendation that not related with question but it is very useful for me to check routings with auto-generated help page. I also used simple client to execute web api queries from the help page.
My results:
Platform-independent & testable business logic layer;
Map business logic result to HttpResponseMessage in base class in a generic manner;
Half-automated security with ActionFilterAttribute;
"Empty" controllers;
Readable code (code conventions and model hierarchy);
I'd suggest to go with the Repository Pattern. Here you have - IMO - an excellent article about it. This should be one of the easiest refactoring you can do.
Following the guidelines from the indicated article you could refactor the code like the following:
Create the base repository interface
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity Get(TKey id);
void Save(TEntity entity);
void Delete(TEntity entity);
}
Create the specialized repository interface:
public interface IDroneDTORepository : IRepository<DroneDTO, int>
{
IEnumerable<DroneDTO> FindAll();
IEnumerable<DroneDTO> Find(int id);
}
Implement the specialized repository interface:
public class DroneDTORepository : IDroneDTORepository
{
private readonly DbContext _dbContext;
public DroneDTORepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public DroneDTO Get(int id)
{
return _dbContext.DroneDTOs.FirstOrDefault(x => x.Id == id);
}
public void Save(DroneDTO entity)
{
_dbContext.DroneDTOs.Attach(entity);
}
public void Delete(DroneDTO entity)
{
_dbContext.DroneDTOs.Remove(entity);
}
public IEnumerable<DroneDTO> FindAll()
{
return _dbContext.DroneDTOs
.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
})
.ToList();
}
public IEnumerable<DroneDTO> Find(int id)
{
return FindAll().Where(x => x.iddrones == id).ToList();
}
}
Use the repository in the code:
private IDroneDTORepository _repository = new DroneDTORepository(dbContext);
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = _repository.FindAll();
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = _repository.Find(id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
This should be close to the resulting code (obviously something might need changes). Let me know if anything is unclear.
Reusing the Select part (projection) in such scenarios is quite easy.
Let take a look at Queryable.Select method signature
public static IQueryable<TResult> Select<TSource, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector
)
What you call "selection code" is actually the selector parameter. Assuming your entity class is called Drone, then according to the above definition we can extract that part as Expression<Func<Drone, DroneDto>> and reuse it in both places like this
public class APIController : ApiController
{
static Expression<Func<Drone, DroneDto>> ToDto()
{
// The code that was inside Select(...)
return d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
};
}
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.Select(ToDto());
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Where(d => d.iddrones == id).Select(ToDto());
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
Of course these two methods can further be refactored (to become "one liners"), but the above is the minimal refactoring that allows reusing the Select part w/o changing any semantics, executing context or the way you write your queries.
Put your mapping to DTO code into a single method that you reuse then you can just do something like:
var drone = db.drones.Select(d => DroneDto.FromDb(d))
.Where(drones => drones.iddrones == id);
public class DroneDto
{
public int iddrones {get;set;}
// ...other props
public static DroneDto FromDb(DroneEntity dbEntity)
{
return new DroneDto
{
iddrones = dbEntity.iddrones,
//... other props
}
}
}
First, try avoid use db directly in the webapi, move to a service.
And second, if I've understand your question, you want avoid write the conversion. You can use AutoMapper, install via nuget with extensions AutoMapper.QueryableExtensions, and configure the mapping between Drone and DroneDto. Configure the mapper:
Mapper.CreateMap<Drone, Dtos.DroneDTO>();
And use as simple as:
db.Drones
.Where(d => ... condition ...)
.Project()
.To<DroneDto>()
.ToList();
Like ben did, you can put your conversion code into a static method on the DroneDto class as such:
public class DroneDto
{
public int iddrones {get;set;}
public static DroneDto CreateFromEntity(DroneEntity dbEntity)
{
return new DroneDto
{
iddrones = dbEntity.iddrones,
...
};
}
}
However, the problem with Bens approach was that the .Select method was called on the DbSet, and LINQ to Entities do not handle these methods. So, you need to do your queries on the DbSet first, then collect the result. For example by calling .ToList(). Then you can do the conversion.
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.ToList().Select(d => DroneDto.CreateFromEntity(d));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Where(d => d.iddrone == id)
.ToList().Select(d => DroneDto.CreateFromEntity(d));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
Alternatively, if you want to avoid multiple enumerations of the result, have a look at AutoMapper. Specifically the Queryable-Extensions.
The DB Call should be in a separate layer to the web api (reason: separation of concerns: you may want to change the DB technology in the future, and your web API may want to get data from other sources)
Use a factory to build your DroneDTO. If you are using dependency injection, you can inject it into the web api controller. If this factory is simple (is not depended on by other factories) you can get away with making it static, but be careful with this: you don't want to have lots of static factories that depend on each other because as soon as one needs to not be static any more you will have to change all of them.
public class APIController : ApiController
{
private readonly IDroneService _droneService;
public APIController(IDroneService droneService)
{
_droneService = droneService;
}
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage GetDrones()
{
var drones = _droneService
.GetDrones()
.Select(DroneDTOFactory.Build);
return Request.CreateResponse(HttpStatusCode.OK, drones);
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage GetDrones(int id)
{
// I am assuming you meant to get a single drone here
var drone = DroneDTOFactory.Build(_droneService.GetDrone(id));
return Request.CreateResponse(HttpStatusCode.OK, drone);
}
}
public static class DroneDTOFactory
{
public static DroneDTO Build(Drone d)
{
if (d == null)
return null;
return new DroneDTO
{
iddrones = d.iddrones,
//more stuff
};
}
}
Use a separate data access layer. I assumed the GetDrone(int Id) will retrieve one or no drone and used SingleOrDefault(). You can adjust that as needed.
//move all the db access stuff here
public class Db
{
//assuming single drone is returned
public Drone GetDrone(int id)
{
//do SingleOrDefault or Where depending on the needs
Drone drone = GetDrones().SingleOrDefault(drones => drones.iddrones == id);
return drone;
}
public IQueryable<Drone> GetDrones()
{
var drone = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
});
return drone;
}
}
Then from the client:
public class APIController : ApiController
{
//this can be injected, service located, etc. simple instance in this eg.
private Db dataAccess = new Db();
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = dataAccess.GetDrones();
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = dataAccess.GetDrone(int id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
I am using Repository Pattern and Entity Framework to communicate with my database and Core stuff.
When I try to make a change to a user entity (change email address, username etc), it does not commit this change in the database. I realise I have missed some stuff out of the update method in my repositoy base, the trouble I am having is finding what I have missed out. Any ideas what I am missing? Very new to repository pattern.
I have been following the tutorial - https://workspaces.codeproject.com/user-10620241/architecture-guide-asp-net-mvc-framework-n-tier-en
MVC Controller
public ActionResult Details(int id = 0)
{
UserModel user = _userService.GetSingle(u => u.Id == id);
if (user == null)
{
return HttpNotFound();
}
return View(user);
}
[HttpPost]
public ActionResult Details(UserModel model)
{
if (ModelState.IsValid)
{
_userService.Update(model);
return RedirectToAction("Index");
}
return View(model);
}
RepositoryBase.cs
public abstract class RepositoryBase<T> : IRepository<T>
where T: class
{
public RepositoryBase()
: this(new ObRepositoryContext())
{
}
public RepositoryBase(IRepositoryContext repositoryContext)
{
repositoryContext = repositoryContext ?? new ObRepositoryContext();
_objectSet = repositoryContext.GetObjectSet<T>();
}
private IObjectSet<T> _objectSet;
public IObjectSet<T> ObjectSet
{
get
{
return _objectSet;
}
}
#region IRepository Members
public void Add(T entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
this.ObjectSet.AddObject(entity);
}
public void Update(T entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
this._objectSet.Attach(entity);
//TODO: Commit update to database here
}
public void Delete(T entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
this.ObjectSet.DeleteObject(entity);
}
public IList<T> GetAll()
{
return this.ObjectSet.ToList<T>();
}
public IList<T> GetAll(Expression<Func<T, bool>> whereCondition)
{
return this.ObjectSet.Where(whereCondition).ToList<T>();
}
public T GetSingle(Expression<Func<T, bool>> whereCondition)
{
return this.ObjectSet.Where(whereCondition).FirstOrDefault<T>();
}
public void Attach(T entity)
{
this.ObjectSet.Attach(entity);
}
public IQueryable<T> GetQueryable()
{
return this.ObjectSet.AsQueryable<T>();
}
public long Count()
{
return this.ObjectSet.LongCount<T>();
}
public long Count(Expression<Func<T, bool>> whereCondition)
{
return this.ObjectSet.Where(whereCondition).LongCount<T>();
}
#endregion
}
Well, you seem to have left yourself a TODO :)
//TODO: Commit update to database here
You'll need to flag the object as Modified - here's a take at this:
this.ObjectSet.Context.ObjectStateManager.ChangeObjectState(
entity, EntityState.Modified);
You are also going to want to call SaveChanges at some point on your context - it seems the pattern you have encourages multiple changes and one final commit at some point:
repositoryContext.SaveChanges();
Edit
The compile error is because the repo you are using has abstracted ObjectSet to its interface, IObjectSet. You'll need to downcast it again:
_objectSet // Or I guess (this.ObjectSet as ObjectSet<T>)
.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
Note that the pattern you are following was done in 2010 with EF 4.0. A lot has happened with Entity Framework since then, most notably DBContext which closes much of the gap with the repository pattern, IMO.