I'm trying to include a csv string involvedBody in a View that is generated using AutoMapper but I can't work out where to map the string to the model.
I assumed it would be in the CreateMap call in the controller or as a .ForMember function in the mapping but I can't get it to work. I need to call the InvolvedBodies function and pass the string into the model before the model becomes an IOrderedEnumerable.
Is this possible or do I need to try something else?
ViewModel
public partial class FamilyInterventionListViewModel
{
public int Id { get; set; }
public int interventionId { get; set; }
public string interventionCategory { get; set; }
public string interventionType { get; set; }
public string outcome { get; set; }
public string achievement { get; set; }
public string involvedBody { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public string notes { get; set; }
}
Repository
public string InvolvedBodies(int interventionId)
{
var q = (from body in context.tEntity
where body.tIntervention.Any(b => b.interventionID == interventionId)
select body.entityName
);
var ibcsv = string.Join(",", q.ToArray());
return ibcsv;
}
Controller
public ActionResult InterventionType(int Id, string achievement)
{
var model = GetDisplay(Id)
.Where(m => m.achievement == achievement)
.OrderByDescending(m => m.startDate)
;
return PartialView("_interventionType", model);
}
//Automapper for display model
private IEnumerable<FamilyInterventionListViewModel> GetDisplay(int Id)
{
var list = _repo.Get(Id);
Mapper.CreateMap<tIntervention, FamilyInterventionListViewModel>();
IEnumerable<FamilyInterventionListViewModel> viewModel = Mapper.Map <IEnumerable<tIntervention>, IEnumerable<FamilyInterventionListViewModel>>(list);
return viewModel;
}
Mapping
Mapper.CreateMap<tIntervention, FamilyInterventionListViewModel>()
.ForMember(d => d.Id, o => o.MapFrom(s => s.interventionID))
.ForMember(d => d.interventionCategory, o => o.MapFrom(s => s.tOutcome.tInterventionCategory.InterventionCategory))
.ForMember(d => d.interventionType, o => o.MapFrom(s => s.tInterventionType.interventionType))
.ForMember(d => d.outcome, o => o.MapFrom(s => s.tOutcome.outcome))
.ForMember(d => d.achievement, o => o.MapFrom(s => s.tAchievement.achievement))
.ForMember(d => d.involvedBody, o => o.MapFrom(s => s.tEntity.Select(m => m.entityName)))
;
I've not been able to find a solution to this using Automapper so have reverted to manually mapping the csv string after the model is created using:
foreach (var item in model)
{
item.involvedBody = _repo.InvolvedBodies(item.interventionId);
}
I'd be interested to know if this is deemed to be good practice or if there is some other technique I should be using.
Related
Can anyone please assist,i have a model that contains a foreign key(ProductCategory).i am trying to call the values of the foreign key but they are not appearing in the view,when i place a breakpoint in the controller all fields appear successful in the raw results view but it seems they are not being passed to the view.
N.B i am using dev express to generate views
public class Product
{
public int Id { get; set; }
public int productCode { get; set; }
public double price { get; set; }
public int ProductCategoryId { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
public Stock itemNo { get; set; }
}
[HttpGet]
public async Task<IActionResult> Get(DataSourceLoadOptions loadOptions) {
var product = _context.Product.Select(i => new {
i.Id,
i.productCode,
i.price,
i.ProductCategoryId,
i.ProductCategory.categoryDescr
});
return Json(await DataSourceLoader.LoadAsync(product, loadOptions));
}
#{
ViewData["Title"] = "Products";
}
<h2 class="content-block">Products</h2>
#(Html.DevExtreme().DataGrid<DevExtremeAspNetCoreApp1.Models.Product>()
.DataSource(ds => ds.Mvc()
.Controller("Products")
.LoadAction("Get")
.InsertAction("Post")
.UpdateAction("Put")
.DeleteAction("Delete")
.Key("Id")
)
.RemoteOperations(true)
.Columns(columns => {
columns.AddFor(m => m.productCode);
columns.AddFor(m => m.price);
columns.AddFor(m => m.ProductCategoryId);
columns.AddFor(m => m.ProductCategory.categoryDescr);
})
.Editing(e => e.Mode(GridEditMode.Popup)
.AllowAdding(true)
.AllowUpdating(true)
.AllowDeleting(true)
.Popup(p=>p
.Title("Product")
.ShowTitle(true)
.Width(500)
.Height(525)
)
)
.Export(e => e.Enabled(true))
)
Try this:
var product = _context.Product.Select(i => new {
i.Id,
i.productCode,
i.price,
i.ProductCategoryId,
ProductCategoryDesc=i.ProductCategory.categoryDescr
});
.....
columns.AddFor(m => m.ProductCategoryDesc);
turns i was passing an anonymous type,correct approach should be:
Controller
public async Task<IActionResult> Get(DataSourceLoadOptions loadOptions) {
var product = _context.Product.Select(i => new {
i.Id,
i.productCode,
i.price,
i.ProductCategoryId,
i.ProductCategory
});
View:
columns.AddFor(m => m.ProductCategory.Id);
I am frustrated. I have a series of tables where I am trying to display fields from one model into another model's view. This is my table structure -
I am attempting to display Device, Status and Location fields in DeviceLog's detail view. I have created my own device model.
public class DeviceLogIndexData
{
public IEnumerable<DeviceLog> DeviceLogs { get; set; }
public IEnumerable<DeviceStatu> DeviceStatus { get; set; }
public IEnumerable<DeviceLocation> DeviceLocations { get; set; }
public IEnumerable<Location> Locations { get; set; }
public IEnumerable<Status> Status { get; set; }
public IEnumerable<Device> Devices { get; set; }
}
and have made several ways to use this. My latest attempt was -
var devicelog = new DeviceLogIndexData();
devicelog.DeviceLogs = db.DeviceLogs
.Include(d => d.Device)
.Include(d => d.Device.DeviceStatus.Select(s => s.Status))
.Include(d => d.Device.DeviceLocations.Select(x => x.Location))
.OrderBy(d => d.DeviceLogID);
devicelog = devicelog.DeviceLogs.Where(d => d.DeviceLogID == id.Value);
Now I am just totally frustrated and confused on how to use my ViewModel.
You want to project into your view model
var devicelog = db.DeviceLogs
.Include(d => d.Device)
.Include(d => d.Device.DeviceStatus.Select(s => s.Status))
.Include(d => d.Device.DeviceLocations.Select(x => x.Location))
.Where(d => d.DeviceLogID == id.Value)
.Select(d => new DeviceLogIndexData { Name = d.Device.Name, ... }).ToList();
This is all pseudo code. so you may need to play around with it a little.
Where your view model class looks something like this
public class DeviceLogIndexData {
public string Name {get;set;}
public string Status {get;set;}
public string Location {get;set;}
}
Then return an IEnumerable<DeviceLogIndexData> to your view.
I have two class. PRODUCT comes from Entity Framework, and Product in Domain solution: Here are the details:
PRODUCT class (source):
namespace SportsStore.Domain
{
using System;
using System.Collections.Generic;
public partial class PRODUCT
{
public int PRODUCT_ID { get; set; }
public string PRODUCT_NAME { get; set; }
public string PRODUCT_DESCRIPTION { get; set; }
public decimal PRODUCT_PRICE { get; set; }
public string PRODUCT_CATEGORY { get; set; }
}
}
Product class (destination):
namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue = false)]
public int ProductId {get;set;}
public string ProductName {get;set;}
[DataType(DataType.MultilineText)]
public string ProductDescription {get;set;}
public decimal ProductPrice {get;set;}
public string ProductCategory { get; set; }
}
}
Here is the ProductViewModel that used throughout the project:
public class ProductsListViewModel
{
public IEnumerable<Product> Products { get; set; }
public PagingInfo _pagingInfo { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentCategory { get; set; }
}
Here is my mapping in the project, I have a automapper to map these two.
public static class AutoMapperConfig
{
public static void RegisterMappings()
{
AutoMapper.Mapper.CreateMap<PRODUCT, Product>();
AutoMapper.Mapper.CreateMap<PRODUCT, Product>()
.ForMember(dest => dest.ProductCategory,
opts => opts.MapFrom(src => src.PRODUCT_CATEGORY));
AutoMapper.Mapper.CreateMap<PRODUCT, Product>()
.ForMember(dest => dest.ProductDescription,
opts => opts.MapFrom(src => src.PRODUCT_DESCRIPTION));
AutoMapper.Mapper.CreateMap<PRODUCT, Product>()
.ForMember(dest => dest.ProductId,
opts => opts.MapFrom(src => src.PRODUCT_ID));
AutoMapper.Mapper.CreateMap<PRODUCT, Product>()
.ForMember(dest => dest.ProductName,
opts => opts.MapFrom(src => src.PRODUCT_NAME));
AutoMapper.Mapper.CreateMap<PRODUCT, Product>()
.ForMember(dest => dest.ProductPrice,
opts => opts.MapFrom(src => src.PRODUCT_PRICE));
}
}
I call RegisterMappings in the Global.asax.cs's Application_Start.
protected void Application_Start()
{
AutoMapperConfig.RegisterMappings();
}
Under these circumstances, I read the contents from Entity framework, their type is PRODUCT, when I try to assign it to Product, I got error. The point that I get error is here:
public ActionResult List(string category,int page = 1)
{
List<PRODUCT> pr = null;
using(ProductContext pb = new ProductContext())
{
ProductsListViewModel model = new ProductsListViewModel
{
//Error is here
Products = pb.ProductsList.Where(p => category == null || p.PRODUCT_CATEGORY == category).OrderBy(i => i.PRODUCT_ID).Skip((page - 1) * PageSize).ToList().Take(PageSize).ToList(),
_pagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = category == null ? pb.ProductsList.Count() : pb.ProductsList.Where(e => e.PRODUCT_CATEGORY == category).Count()
},
CurrentCategory = category
};
return View(model);
}
}
The error is this:
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)
How can I overcome this? Thanks in advance.
The error says everything you need to know. In somewhere you are trying to put a List of objects to a IEnumerable of the same objects.
It seems to me that you shoud alter the line
Products = pb.ProductsList.Where(p => category == null || p.PRODUCT_CATEGORY == category)
.OrderBy(i => i.PRODUCT_ID)
.Skip((page - 1) * PageSize)
.ToList().Take(PageSize).ToList()
into
Products = pb.ProductsList.Where(p => category == null || p.PRODUCT_CATEGORY == category)
.OrderBy(i => i.PRODUCT_ID)
.Skip((page - 1) * PageSize)
.Take(PageSize).AsEnumerable()
I am trying to map information about the User to several dto's, but I'm getting null exceptions. Basically, the reason why I distributed the information among several classes is because there are common info that I need in several views. So this is what I ended up with:
User entity class
public class User
{
public int Id { get; set; }
public string Nickname { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public float Credits { get; set; }
public float PromotionalCredits { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public double RatingAverage { get; set; }
public string ProfileImage { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
public virtual ICollection<CreditCard> CreditCard { get; set; }
public virtual ICollection<Message> ReceivedMessages { get; set; }
public virtual ICollection<Message> SentMessages { get; set; }
public virtual ICollection<Item> WatchList { get; set; }
public virtual ICollection<Rating> OwnRatings { get; set; }
public virtual ICollection<Rating> RatingsForOthers { get; set; }
}
DTO's and ViewModel calsses
public class UserInfoSummaryViewModel
{
public int Id { get; set; }
public string FullName { get; set; }
public UserDetailedStatus DetailedStatus { get; set; }
}
public class UserDetailedStatus
{
public float TotalCredits { get; set; }
public float Credits { get; set; }
public float PromotionalCredits { get; set; }
public BiddingAndItems BiddingAndItems { get; set; }
public int OngoingListings { get; set; }
public int NewMessages { get; set; }
public float Rating { get; set; }
public int NumberOfRatings { get; set; }
}
public class BiddingAndItems
{
public int TotalBids { get; set; }
public int WinningBids { get; set; }
public int AcquiredItems { get; set; }
public int ItemsAwaitingConfirmation { get; set; }
public List<ItemForUserBids> Items { get; set; }
}
Mappings inside AutoMapperBootStrapper class
Mapper.CreateMap<User, BiddingAndItems>()
.ForMember(m => m.TotalBids, o => o.MapFrom(s => s.TotalActiveBids()))
.ForMember(m=>m.ItemsAwaitingConfirmation, o=>o.MapFrom(s=>s.Items.Count(i=>i.IsAwaitingReceptionConfirmation().Equals(true))))
.ForMember(m=>m.AcquiredItems, o=>o.MapFrom(s=>s.AquiredItems().Count))
.ForMember(m => m.WinningBids,
o => o.MapFrom(s => s.Bids.Where(c => c.Item.CurrentHighestBidderId().Equals(s.Id))));
Mapper.CreateMap<User, UserDetailedStatus>()
.ForMember(m => m.NumberOfRatings, o => o.MapFrom(s => s.OwnRatings.Count()))
.ForMember(m => m.NewMessages, o => o.MapFrom(s => s.TotalUnreadMessages()))
.ForMember(m => m.OngoingListings, o => o.MapFrom(s => s.Items.Where(i => i.IsPublished())))
.ForMember(m => m.Rating, o => o.MapFrom(s => s.RatingAverage))
.ForMember(m => m.TotalCredits, o => o.MapFrom(s => s.TotalCredits()));
Mapper.CreateMap<User, UserInfoSummaryViewModel>();
Call to automapper inside UserController
public ActionResult Summary()
{
var user = _helper.GetUserFromSession();
var viewModel = Mapper.Map<User, UserInfoSummaryViewModel>(user);
return View(viewModel);
}
I thought because I have all the necessary mappings inside the bootstrapper this should, theoretically work, apparently I was wrong... How can I fix that?
UPDATE:
I got my mappings fixed and added a couple of value resolvers. Now I'm not getting a null reference exception but there seems to be something wrong because everytime I run the project it gets stuck and then the local server stops responding... Here's my code:
Mapper.CreateMap<User, BiddingAndItems>()
.ForMember(m => m.TotalBids, o => o.MapFrom(s => s.TotalActiveBids()))
.ForMember(m => m.ItemsAwaitingConfirmation,
o => o.MapFrom(s => s.Items.Count(i => i.IsAwaitingReceptionConfirmation().Equals(true))))
.ForMember(m => m.AcquiredItems, o => o.MapFrom(s => s.AquiredItems().Count))
.ForMember(m => m.WinningBids, o => o.ResolveUsing<WinningBidsResolver>())
.ForMember(m => m.Items, o => o.ResolveUsing<BiddingItemResolver>());
Mapper.CreateMap<User, UserDetailedStatus>()
.ForMember(m => m.NumberOfRatings, o => o.MapFrom(s => s.OwnRatings.Count()))
.ForMember(m => m.NewMessages, o => o.MapFrom(s => s.TotalUnreadMessages()))
.ForMember(m => m.OngoingListings, o => o.MapFrom(s => s.Items.Where(i => i.IsPublished()).Count()))
.ForMember(m => m.Rating, o => o.MapFrom(s => s.RatingAverage))
.ForMember(m=>m.BiddingAndItems, o => o.MapFrom(s=> s))
.ForMember(m => m.TotalCredits, o => o.MapFrom(s => s.TotalCredits()));
Mapper.CreateMap<User, UserInfoSummaryViewModel>()
.ForMember(m => m.DetailedStatus, o => o.MapFrom(s => s));
public class BiddingItemResolver : ValueResolver<User, List<ItemForUserBids>>
{
protected override List<ItemForUserBids> ResolveCore(User source)
{
var items = new List<ItemForUserBids>();
foreach (var bid in source.Bids)
{
var item = bid.Item;
var c = new ItemForUserBids
{
BidValue = bid.Amount,
Description = item.Description,
Id = item.Id,
ItemThumb = item.MainImageLink(),
Status = source.ItemBiddingStatus(item.Id),
TimeLeft = TimeUtility.TimeLeft(item.EndDate),
Title = item.Title
};
items.Add(c);
}
return items;
}
}
public class WinningBidsResolver : ValueResolver<User, int>
{
protected override int ResolveCore(User source)
{
return source.Bids.Where(c => c.Item.CurrentHighestBidderId().Equals(source.Id)).Count();
}
}
The problem is that I'm not getting any exceptions to give me any hints about what's going wrong... It just gets stuck! I suspect that my mappings are going into some sort of an infinite loops or something, but I am not sure what is happening exactly... Is there any way I could debug this problem?
Any help would be appreciated...
Without more information about the exception you receive I can only guess what could go wrong: I guess it's because you're using Linq on uninitialized collections in MapFrom. Try implementing a ValueResolver instead.
I get an cast exception when i am trying to insert an entity in Entity Framework (using code-first).
The cast exception is like "impossible to cast ...Collection'1(Entity) to type (Entity)"
From this code :
public virtual T Insert(T entity)
{
return Context.Set<T>().Add(entity);
}
I can't figure out why. I am pretty sure ive done everything right.
Post entity
public class Post
{
public long PostId { get; private set; }
public DateTime date { get; set; }
[Required]
public string Subject { get; set; }
public User User { get; set; }
public Category Category { get; set; }
[Required]
public string Body { get; set; }
public virtual ICollection<Tag> Tags { get; private set; }
public Post()
{
Category = new Category();
if (Tags == null)
Tags = new Collection<Tag>();
}
public void AttachTag(string name, User user)
{
if (Tags.Count(x => x.Name == name) == 0)
Tags.Add(new Tag {
Name = name,
User = user
});
else
throw new Exception("Tag with specified name is already attached to this post.");
}
public Tag DeleteTag(string name)
{
Tag tag = Tags.Single(x => x.Name == name);
Tags.Remove(tag);
return tag;
}
public bool HasTags()
{
return (Tags.Count > 0);
}
}
Tag entity
public class Tag
{
public long TagId { get; private set; }
public string Name { get; set; }
// Qui a ajouté le tag ?
public User User { get; set; }
}
Mapping
public class PostMap: EntityTypeConfiguration<Post>
{
public PostMap()
{
ToTable("Posts");
HasKey(x => x.PostId);
Property(x => x.Subject)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
Property(x => x.Body)
.HasColumnType("text")
.IsRequired();
HasMany(x => x.Tags);
HasOptional(x => x.Tags);
}
}
class TagMap : EntityTypeConfiguration<Tag>
{
public TagMap()
{
ToTable("Tags");
HasKey(x => x.TagId);
Property(x => x.Name)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
HasRequired(x => x.User);
}
}
Thanks a lots.
Please make sure that you have passed a single element to the Insert method, not a collection containing a single element.
I found the solution.
Here the correct mapping scenario for Post :
public PostMap()
{
ToTable("Posts");
HasKey(x => x.PostId);
Property(x => x.Subject)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
Property(x => x.Body)
.HasColumnType("text")
.IsRequired();
HasRequired(x => x.User);
HasMany(x => x.Tags).WithOptional();
}
It is important to specify the the Tags collection is optional. Which is the case in this scenario. A Post can have zero tags attached to it.
HasMany(x => x.Tags).WithOptional();