Linq query with complex model mapping - c#

I am trying to map from my webapi to my solution some ICollection
this is a HttpClient: var currentNotifications = await client.GetUsersAsync();
If I have a model for example:
public class UserNotificationTypeDeliveryChoice
{
public DeliveryType DeliveryType { get; set; }
public NotificationGroup NotificationGroup{ get; set; }
}
public class DeliveryType
{
public byte DeliveryTypeId { get; set; }
public string Name { get; set; }
}
public class NotificationGroup
{
public byte NotificationGroupId { get; set; }
public string Name { get; set; }
}
and model to map to:
public class NotificationListViewModel
{
public DeliveryType DeliveryType { get; set; }
public NotificationGroup NotificationGroup { get; set; }
}
public class DeliveryType
{
public byte DeliveryTypeId { get; set; }
public string Name { get; set; }
}
public class NotificationGroup
{
public int NotificationGroupId { get; set; }
public string Name { get; set; }
}
So, I have an ICollection<UserNotificationTypeDeliveryChoice> currentNotifications from my webapi which I need to map to
List<NotificationListViewModel> notificationListViewModel
Myexample looks like:
var res = currentNotifications.ToList().ForEach(x => notificationListViewModel.Add(new NotificationListViewModel()
{
DeliveryType = new DeliveryType() {DeliveryTypeId = x.DeliveryType.DeliveryTypeId.Value, Name = x.DeliveryType.Name },
NotificationGroup = new NotificationGroup() { NotificationGroupId = x.NotificationGroup.NotificationGroupId.Value, Name = x.NotificationGroup.Name }
}));
But, it reports me an error: CS0815 Cannot assign void to an implicitly-typed variable
Ofc, if you have better solution, without ForEach I would be thankful.

ForEach() does not return anything.
You could do it with a Select() call instead:
var result = currentNotifications.Select(x => new NotificationListViewModel
{
DeliveryType = new DeliveryTypeViewModel
{
DeliveryTypeId = x.DeliveryType.DeliveryTypeId,
Name = x.DeliveryType.Name
},
NotificationGroup = new NotificationGroupViewModel
{
NotificationGroupId = x.NotificationGroup.NotificationGroupId,
Name = x.NotificationGroup.Name
}
});
I added ViewModel suffix to all view model class names to make it more readable.

The issue is here:
var res = currentNotifications.ToList().ForEach(...);
ForEach() returns void and therefore you're trying to assign void to res. That doesn't work.
First store the list, then do the ForEach:
var res = currentNotifications.ToList();
res.ForEach(...);
Or simply drop the res altogether, since you're storing the outcome in notificationListViewModel anyway.
currentNotifications.ToList().ForEach(...);
It depends on whether you still need res or not.
As an aside and further improvement, you're doing a LINQ Select() in a different way. Using the LINQ equivalent:
notificationListViewModel = currentNotifications.Select(x => new NotificationListViewModel() { ... });

Related

Automapper override values which are not in source

Hi have a problem with auomapper where i try to use the Automapper.Mapper(src, dest) without loosing the values in the dest after doing the mapping.
I have a class which has a list of objects like below
public class UpdateShipmentDetailDto
{
public bool IsDocument { get; set; }
public List<UpdateItemDetailDto> ItemDetails { get; set; } = new();
}
which i want to map to
public class SCS_OUT_Manifest
{
public Guid ManifestId { get; set; }
public ICollection<SCS_OUT_ManifestItem> SCS_OUT_ManifestItems { get; set; } = new List<SCS_OUT_ManifestItem>();
}
The UpdateItemDetailDto class looks like this
public class UpdateItemDetailDto
{
public Guid ItemId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
And the SCS_OUT_ManifestItem class looke like
public class SCS_OUT_ManifestItem
{
public Guid ItemId { get; set; }
public Guid ManifestId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
Im performing a maaping like below, which map from ItemDetails (which is a list) to SCS_OUT_ManifestItems (which is also a ICollection).
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.SCS_OUT_ManifestItems);
The problem after mapping is done the properties which in the destination collection are set to the default values.
for example the ManifestId inthe SCS_OUT_ManifestItem manifest.SCS_OUT_ManifestItems which is not in updateShipmentDetailDto.ItemDetails is set to its default Guid value 00000000-0000-0000-0000-000000000000.
But if i run this in a loop like below it works.
foreach (var item in manifest.SCS_OUT_ManifestItems)
{
_mapper.Map(updateShipmentDetailDto.ItemDetails.Single(s => s.ItemId == item.ItemId), item);
}
Please help! thanks in advance
Try map your lists and your itens and use the same name ÏtemDetails" in both lists.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<UpdateItemDetailDto, SCS_OUT_ManifestItem>();
cfg.CreateMap<UpdateShipmentDetailDto, SCS_OUT_Manifest>();
});
var _mapper = config.CreateMapper();
var updateShipmentDetailDto = new UpdateShipmentDetailDto();
var updateItemDetailDto = new UpdateItemDetailDto();
var manifest = new SCS_OUT_Manifest();
updateItemDetailDto.ItemId = Guid.NewGuid();
updateItemDetailDto.UnitsId = Guid.NewGuid();
manifest.ManifestId = Guid.NewGuid();
updateItemDetailDto.ItemDescription = "test";
updateItemDetailDto.Qty = 10;
updateItemDetailDto.ItemValue = 25.50M;
updateShipmentDetailDto.ItemDetails = new List<UpdateItemDetailDto>();
updateShipmentDetailDto.ItemDetails.Add(updateItemDetailDto);
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.ItemDetails);
Console.WriteLine($"DTO Guid: {updateShipmentDetailDto.ItemDetails[0].ItemId}, Desc: {updateShipmentDetailDto.ItemDetails[0].ItemDescription}");
foreach (var item in manifest.ItemDetails)
{
Console.WriteLine($"Guid: {item.ItemId}, Desc: {item.ItemDescription}");
}
Console.WriteLine($"Guid Manifest: {manifest.ManifestId}");

EF Core return specific columns from child tables

I don't want to use .include to get the entire child tables. I just need select columns.
public class ProjectTypeDTO {
public string Type { get; set; }
}
public class CourseDTO {
public string CourseCode { get; set; }
public string CourseTitle { get; set; }
}
public class ProjectDTO {
public int Id { get; set; }
public ProjectTypeDTO ProjectType { get; set; }
public CourseDTO Course { get; set; }
public string StartTerm { get; set; }
public DateTime SignOff { get; set; }
public DateTime StartDateTime { get; set; }
}
[HttpGet("getallprojects")]
public IActionResult GetAllProjects()
{
var projects = _context.Projects
.Select(p => new ProjectDTO
{
Id = p.Id,
ProjectType = { Type = p.ProjectType.Type },
Course = { CourseCode = p.Course.CourseCode, CourseTitle = p.Course.CourseTitle },
StartTerm = p.StartTerm,
SignOff = p.SignOff,
StartDateTime = p.StartDateTime,
}).ToList();
return Ok(projects);
}
This is throwing a "NotImplementedException: The method or operation is not implemented." error.
I've tested it as an anonymous function and it works.
var projects = _context.Projects
.Select(p => new
{
p.Id,
p.ProjectType.Type,
p.SignOff,
p.StartDateTime,
p.Course.CourseCode,
p.Course.CourseTitle,
p.StartTerm
}).ToList();
An anonymous type won't work for my app, since I need to be able to make changes to this data before it gets returned.
Based on other examples I've seen here and other sites, this looks correct. Could it be a bug?
I haven't seen that syntax for sub-objects before. eg:
ProjectType = { Type = p.ProjectType.Type }
I believe that should be:
ProjectType = new ProjectTypeDTO{ Type = p.ProjectType.Type }

Automapper nested Collections without setter

I have this code snippet running on LinqPad (C# program) with Automapper Nuget package 6.1.1 already included:
void Main()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Top, TopDto>().ReverseMap();
});
Mapper.AssertConfigurationIsValid();
var source = new TopDto
{
Id = 1,
Name = "Charlie",
Nicks = new List<string> { "Fernandez", "Others" }
};
var destination = Mapper.Map<Top>(source);
destination.Dump();
}
// Define other methods and classes here
public class Top
{
public Top()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; }
}
public class TopDto
{
public TopDto()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; set; }
}
And as you can see we have problems setting the nested Collection (without Setter at all). In theory this should be running fine but it is not adding any element to the Collection.
If we change the collection property adding a public setter, then all is fine.
How can I get a nested collection without adding a public setter or a setter at all?
Thanks to #LucianBargaoanu (in the comments) this is solved now, in this way:
void Main()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Top, TopDto>().ReverseMap()
.ForMember(d => d.Nicks, o=>
{
o.MapFrom(s => s.Nicks);
o.UseDestinationValue();
});
});
Mapper.AssertConfigurationIsValid();
var source = new TopDto(new List<string> { "Fernandez", "Others" })
{
Id = 1,
Name = "Charlie"
};
var destination = Mapper.Map<Top>(source);
destination.Dump();
}
// Define other methods and classes here
public class Top
{
public Top()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; }
}
public class TopDto
{
public TopDto(List<string> nicks)
{
Nicks = nicks;
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; private set; }
}
Regards.

Convert to DTO list property

I have the following DTO:
public class QuestionGroupDTO : IBaseDTO
{
public string Description { get; set; }
public string Header { get; set; }
public Guid Id { get; set; }
public int Order { get; set; }
public IEnumerable<Services.Forms.Models.RelationForm_QuestionGroupDTO> RelationForms_QuestionGroups { get; set; }
public IEnumerable<RelationQuestionGroup_QuestionDTO> RelationQuestionGroups_Questions { get; set; }
}
I have problem with the RelationQuestionGroups_Questions while converting.
Here Is how my RelationQuestionGroup_QuestionDTO looks like
public class RelationQuestionGroup_QuestionDTO
{
public int Order { get; set; }
[Required]
public Guid QuestionGroupId { get; set; }
[Required]
public Guid QuestionId { get; set; }
public virtual QuestionGroupDTO QuestionGroup { get; set; }
public virtual QuestionDTO Question { get; set; }
}
Here Is how I convert:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList()
};
return dto;
}
As you can see, I'm trying to just assign It and make a list of It, but I got a casting error here. I'm not sure how to do this.
I get the following error:
Cannot implicity convert type Generic List to System.Collections.Generic.IEnumerble
You're having a great start at mapping, but at RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList(), you're trying to assign a List<Entity> to List<Dto>. You can't do that.
You need to map any non-primitive properties as well. You can do that like this:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
// ...
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions
.Select(ToDTO)
.ToList()
};
return dto;
}
Then you add a method to map RelationQuestionGroups_Question to RelationQuestionGroups_QuestionDTO:
public RelationQuestionGroups_QuestionDTO ToDTO(RelationQuestionGroups_Question entity)
{
return new RelationQuestionGroups_QuestionDTO
{
Order = entity.Order,
// ...
};
}
And then you'll go look at AutoMapper to automate this.
You forgot to map RelationQuestionGroups_Questions to RelationQuestionGroup_QuestionDTO.
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.Select(rq => new RelationQuestionGroup_QuestionDTO
{
Order = rq.Order,
QuestionGroupId = rq.QuestionGroupId,
QuestionId = rq.QuestionId
}).ToList()
};
return dto;
}

Entity framework with Web Api returning object

Hi I am new to entity framework. I have following objects
public partial class lr
{
public lr()
{
this.lr_history = new HashSet<lr_history>();
this.people = new HashSet<person>();
}
public int _id { get; set; }
public string name { get; set; }
public string notes { get; set; }
public virtual ICollection<lr_history> lr_history { get; set; }
public virtual ICollection<person> people { get; set; }
In my Web Api controller I have following code
public class lrController : ApiController
{
private CTM db = new CTM();
// GET api/addL
public IEnumerable<lr> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable();
}
In above Get method it returns lr but i want to return my own object like
lr.id
lr.name
lr_history.description
lrh_history.rate
Can anyone show me how to achieve this? Than
Create a new model:
class HistoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public string HistoryDescription { get; set; }
public string HistoryRate { get; set; }
}
and return that:
public IEnumerable<HistoryModel> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable()
.Select(x => new HistoryModel
{
Id = x.id,
Name = x.name,
HistoryDescription = x.history.description,
HistoryRate = x.history.rate
});
}
Instade of return db.lr.AsEnumerable();
try this
return db.lr.ToList();

Categories

Resources