How to simplify transforming entity framework entities to DTOs - entity-framework

I map to a data transformation object when retrieving items from an ASP.NET Web API like so for a list:
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return await _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
})
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync();
}
And like this for getting a single record:
public async Task<PromotionDTO> GetPromotion(int id)
{
return await _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
})
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync();
}
I'm new to DTO's and I find that I'm using the same DTO transformation code at many places, and was wondering how I can simplify my code to only do this once?

Though it may be enough to map like you've stated, but when your project starts to grow it will just complicated things and cause additional work.
I suggest that you use some kind of mapping library like AutoMapper.
https://github.com/AutoMapper/AutoMapper
static MyRepositoryConstructor()
{
// Define your maps
Mapper.Initialize(cfg => {
cfg.CreateMap<PromotionEntity, PromotionDTO>();
});
}
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return Mapper.Map<IList<PromotionDTO>>(await _context.Promotions
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync()
);
}
public async Task<PromotionDTO> GetPromotion(int id)
{
return Mapper.Map<PromotionDTO>(await _context.Promotions
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync()
);
}

One option is to create a method which returns an IQueryable and then use that in each
Private IQueryable<PromotionDTO> Query()
{
return _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
});
}
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return await Query()
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync();
}
public async Task<PromotionDTO> GetPromotion(int id)
{
return await Query()
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync();
}

Related

Entity Framework not including children in join

I am using inner join to return results with Entity Framework (v6.2.0) and the following code is not returning the RouteWaypoints children (i.e. route.RouteWaypoints is always null). Interestingly, single children are loading (Customer, OriginLocation, etc), but not multiple children:
public List<Route> GetAllForTripWithWaypoints(int tripId)
{
return (
from route in GetAllBaseWithWaypoints()
from tripTask in DbContext.TripTasks.Where(x =>
x.TripId == tripId && x.OriginLocationId == route.OriginLocationId)
select route
).ToList();
}
private IQueryable<Route> GetAllBaseWithWaypoints()
{
return DbContext.Routes
.Include(x => x.Customer)
.Include(x => x.OriginLocation)
.Include(x => x.DestinationLocation)
.Include(x => x.RouteWaypoints.Select(y => y.Location))
.OrderBy(x => x.OriginLocation.Name).ThenBy(x => x.DestinationLocation.Name)
.AsQueryable();
}
This approach does work if I load just the Route entity, but not when I do the join. As a reference, this does load the children successfully:
public Route GetByIdWithWaypoints(int id, bool validateExists = true)
{
var route = GetAllBaseWithWaypoints().FirstOrDefault(x => x.Id == id);
if (validateExists && route == null)
throw new Exception("Route not found for id: " + id);
return route;
}
How can I keep it working when joining?
I did an imperfect workaround by making two calls to the db - slightly less efficient, but it solves the problem:
public List<Route> GetAllForTripWithWaypoints(int tripId)
{
var routeIds = (
from route in GetAllBase()
from tripTask in DbContext.TripTasks.Where(x =>
x.TripId == tripId && x.OriginLocationId == route.OriginLocationId)
select route.Id
).ToList();
return GetAllBaseWithWaypoints().Where(x => routeIds.Contains(x.Id)).ToList();
}

EF Core database filter

I am following this tutorial. I am new to C# and need some help!
I am trying to add a feature to the GetUsers function to be able to get a list of "Users" associated with a "Company" in the else statement. The return type should be of type List<UserDTO> (which it is in the if statement).
However, the else statement returns a List<User>. How do I tell the database query to return a List<UserDTO>?
I get this build error:
Cannot implicitly convert type 'System.Collections.Generic.List<TestProj.Models.User>' to'Microsoft.AspNetCore.Mvc.ActionResult<System.Collections.Generic.IEnumerable<TestProj.Models.UserDTO>>'
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDTO>>> GetUsers(int? companyId)
{
if(companyId == null)
{
return await _context.Users.Select(x => UserToDTO(x)).ToListAsync();
}
else
{
return await _context.Users
.Where(x => x.CompanyId == companyId)
.ToListAsync();
}
}
private static UserDTO UserToDTO(User user) =>
new UserDTO
{
UserId = user.UserId,
Name = user.Name,
Email = user.Email,
CompanyId = user.CompanyId
};
Thanks in advance! :)
you can try to Select to the UserDTO
return await _context.Users
.Where(x => x.CompanyId == companyId)
.Select(x => new UserDTO
{
UserId = x.UserId,
Name = x.Name,
Email = x.Email,
CompanyId = x.CompanyId
}).ToList();

Moq MongoDB UpdateOneAsync method for unit testing

I want to moq update method that is using mongodbContext. here is what i am trying to do but its not working. how to pass UpdateResult return type .ReturnsAsync<UpdateResult>(). I am very new to Mongodb database Unit Testing with .net core.
public void UpdateEventAsync_Test()
{
//Arrange
var eventRepository = EventRepository();
var pEvent = new PlanEvent
{
ID = "testEvent",
WorkOrderID = "WorkOrderID",
IsDeleted = false,
IsActive = true,
EquipmentID = "EquipmentID"
};
////Act
mockEventContext.Setup(s => s.PlanEvent.UpdateOneAsync(It.IsAny<FilterDefinition<PlanEvent>>(), It.IsAny<UpdateDefinition<Model.EventDataModel.PlanEvent>>(), It.IsAny<UpdateOptions>(), It.IsAny<System.Threading.CancellationToken>())).ReturnsAsync<UpdateResult>();
var result = eventRepository.UpdateEventAsync(pEvent);
////Assert
result.Should().NotBeNull();
Assert.AreEqual(true, result);
}
below is the code for which i want to write Moq Test
public async Task<bool> UpdateEventAsync(Model.EventDataModel.PlanEvent eventobj)
{
var filter = Builders<Model.EventDataModel.PlanEvent>.Filter.Where(f => f.ID == eventobj.ID);
// TO Do: Use replace instead of update.
var updatestatement = Builders<Model.EventDataModel.PlanEvent>.Update.Set(s => s.IsDeleted, eventobj.IsDeleted)
.Set(s => s.EquipmentID, eventobj.EquipmentID)
.Set(s => s.PlannedStartDateTime, eventobj.PlannedStartDateTime)
.Set(s => s.PlannedEndDatetime, eventobj.PlannedEndDatetime)
.Set(s => s.WorkOrderID, eventobj.WorkOrderID)
.Set(s => s.ResourceID, eventobj.ResourceID)
.Set(s => s.LastUpdatedBy, eventobj.LastUpdatedBy)
.Set(s => s.EventComment, eventobj.EventComment)
.Set(s => s.SiteID, eventobj.SiteID)
.Set(s => s.LastUpdatedDateTime, DateTime.UtcNow.ToString());
UpdateResult updateResult = await _eventContext.PlanEvent.UpdateOneAsync(filter, updatestatement);
return updateResult != null && updateResult.IsAcknowledged && updateResult.ModifiedCount > 0;
}
Either create an instance or mock UpdateResult and return that from the setup
public async Task UpdateEventAsync_Test() {
//...omitted for brevity
var mockUpdateResult = new Mock<UpdateResult>();
//Set up the mocks behavior
mockUpdateResult.Setup(_ => _.IsAcknowledged).Returns(true);
mockUpdateResult.Setup(_ => _.ModifiedCount).Returns(1);
mockEventContext
.Setup(_ => _.PlanEvent.UpdateOneAsync(It.IsAny<FilterDefinition<PlanEvent>>(), It.IsAny<UpdateDefinition<Model.EventDataModel.PlanEvent>>(), It.IsAny<UpdateOptions>(), It.IsAny<System.Threading.CancellationToken>()))
.ReturnsAsync(mockUpdateResult.Object);
//Act
var result = await eventRepository.UpdateEventAsync(pEvent);
//Assert
result.Should().Be(true);
}
Also note that the test needs to be made async to be exercised accurately.

EF Core 2.1 Updating related entites query efficiency

I'm having a student model in which i have list of phone numbers and addresses.When i update the student the related data(phone and address) needs to be updated. I have written a PUT action in my student controller for that. It works fine, but I'm concerned about the efficiency of the query. Please check the code and suggest me improvisation if any. Thanks
public async Task<IActionResult> Put(long id, [FromBody] Student student)
{
var p = await _Context.Students
.Include(t => t.PhoneNumbers)
.Include(t => t.Addresses)
.SingleOrDefaultAsync(t => t.Id == id);
if (p == null)
{
return NotFound();
}
_Context.Entry(p).CurrentValues.SetValues(student);
#region PhoneNumber
var existingPhoneNumbers = p.PhoneNumbers.ToList();
foreach (var existingPhone in existingPhoneNumbers)
{
var phoneNumber = student.PhoneNumbers.SingleOrDefault(i => i.Id == existingPhone.Id);
if (phoneNumber != null)
_Context.Entry(existingPhone).CurrentValues.SetValues(phoneNumber);
else
_Context.Remove(existingPhone);
}
// add the new items
foreach (var phoneNumber in student.PhoneNumbers)
{
if (existingPhoneNumbers.All(i => i.Id != phoneNumber.Id))
{
p.PhoneNumbers.Add(phoneNumber);
}
}
#endregion
#region Address
var existingAddresses = p.Addresses.ToList();
foreach (var existingAddress in existingAddresses)
{
var address = student.Addresses.SingleOrDefault(i => i.Id == existingAddress.Id);
if (address != null)
_Context.Entry(existingAddress).CurrentValues.SetValues(address);
else
_Context.Remove(existingAddress);
}
// add the new items
foreach (var address in student.Addresses)
{
if (existingAddresses.All(i => i.Id != address.Id))
{
p.Addresses.Add(address);
}
}
#endregion
await _Context.SaveChangesAsync();
return NoContent();
}
Searching over small in-memory collections is not normally something you would worry about. So if a Student has dozens or hundreds of addresses, the repeated lookups are not going to be significant, especially compared with the time required to write to the database.
If you did want to optimize, you can copy the students Addresses to a Dictionary. Like this:
var existingAddresses = p.Addresses.ToList();
var studentAddresses = student.Addresses.ToDictionary(i => i.Id);
foreach (var existingAddress in existingAddresses)
{
if (studentAddresses.TryGetValue(existingAddress.Id, out Address address))
{
_Context.Entry(existingAddress).CurrentValues.SetValues(address);
}
else
{
_Context.Remove(existingAddress);
}
}
A query over an in-memory collection like this:
var address = student.Addresses.SingleOrDefault(i => i.Id == existingAddress.Id);
Will simply iterate all the student.Addresses comparint the Ids. A Dictionary<> acts like an index, providing very fast lookups.

EF send Includes list to repository via parametr

I at this moment I have repository filled with multiple gets methods.
E.q. Get1() => cxt.Entites.Include(e => e.obj1);
Get2() => cxt.Entities.Include(e => e.obj1).Include(e => e.obj2)
And so on.
Is there good method, pattern to have one GET method where I can send inclues via parameter?
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
See repository pattern in msdn
You can use
_sampleRepostiory.Get(h=>h.Id>1,null,"Employees.Departments");
Including same with lambda
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
Consume it like this
var query = context.Customers
.Get(x=>x.Id>1,null,
c => c.Address,
c => c.Orders.Select(o => o.OrderItems));
Similar SO question
I did the following in my projects:
public Entity[] GetAll(bool includeObj1, bool includeAllOthers) {
IQueryable<Entity> entity = ctx.Entities;
if (includeObj1)
entity = entity.Include(e => e.obj1);
if (includeAllOthers) {
entity = entity
.Include(e => e.obj2)
.Include(e => e.obj3)
.Include(e => e.obj4)
.Include(e => e.obj5);
}
return entity.ToArray();
}
Providing arguments like includeObj1 and includeObj2 separates a consumer of repository from implementation and encapsulates any data access logic.
Passing direct "include these properties" orders to a repository means that you know how repository works and assume that it is some sort ORM which blurs abstractions.