How to correctly handle computed properties in EF model?
My try bellow will fail because of "The entity or complex type 'Invoice' cannot be constructed in a LINQ to Entities query."
Consider method "GetInvoice" as WebApi method with allowed querystring.
static void Main(string[] args)
{
var invs = GetInvoice();
invs.FirstOrDefault();
}
public static IQueryable<Invoice> GetInvoice()
{
var model = new Model();
IQueryable<Invoice> inv = model.Invocies.Include(t => t.Items).SelectInvoiceData();
return inv;
}
public static class ExtHelper
{
public static IQueryable<Invoice> SelectInvoiceData(this IQueryable<Invoice> item)
{
return item.Select(c => new Invoice
{
LatestItemName = c.Items.FirstOrDefault().Name
});
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public DateTime CreatedAt { get; set; }
public string Issuer { get; set; }
[NotMapped]
public string LatestItemName { get; set; }
private ICollection<Item> _items;
public virtual ICollection<Item> Items
{
get { return _items ?? (_items = new Collection<Item>()); }
set { _items = value; }
}
}
EntityFramework 6 does not support creating partial entities like this. Either use anonymous type:
return item.Select(c => new
{
LatestItemName = c.Items.FirstOrDefault().Name
});
Or some DTO class that does not belong to context:
return item.Select(c => new InvoiceDTO
{
LatestItemName = c.Items.FirstOrDefault().Name
});
However in EF Core it is possible to create entities like in your example.
Related
I am trying to list some food items with a controller. I use Repository pattern with UnitOfWork for the data in another assembly and referenced it in a BaseApiController. The Data property is my UnitOfWork instance.
var result = Data.Food
.FindAll()
.Select(FoodItemViewModel.Create);
return result;
and here is my ViewModel:
public static Expression<Func<FoodItem, FoodItemViewModel>> Create
{
get
{
return fi => new FoodItemViewModel
{
Id = fi.Id,
Description = fi.Description,
DiaryEntries = fi.DiaryEntries
.Select(s => new DiaryEntityViewModel()
{
Id = s.Id,
Quantity = s.Quantity
}
};
}
}
But all I get is:
"The specified type member 'DiaryEntries' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported."
My DiaryEntries member in the ViewModel is
IEnumerable<DiaryEntityViewModel>
and my DiaryEntries member in the Data instance is
IRepository<DiaryEntry>
and DiaryEntry is my model class
and here is my FoodItem model class:
public class FoodItem
{
private IEnumerable<Measure> measures;
private IEnumerable<DiaryEntry> diaryEntries;
public FoodItem()
{
this.measures = new HashSet<Measure>();
this.diaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual IEnumerable<DiaryEntry> DiaryEntries
{
get
{
return this.diaryEntries;
}
set
{
this.diaryEntries = value;
}
}
public virtual IEnumerable<Measure> Measures
{
get
{
return this.measures;
}
set
{
this.measures = value;
}
}
}
Change you FoodItem class to the one below, IEnumerable<T> is not supported as a type for a navigation collection :
public class FoodItem
{
public FoodItem()
{
this.Measures = new HashSet<Measure>();
this.DiaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<DiaryEntry> DiaryEntries
{
get;
set;
}
public virtual ICollection<Measure> Measures
{
get;
set;
}
}
Been struggling with this for a day now to no avail. I am new to Automapper and I am trying to map a EF domain object with a viewModel but I receive the following exception:
Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nCatalogueDefinitionFile -> CatalogueDefinitionFileViewModel\r\nDigital.PriceBuilder.Core.Domain.CatalogueDefinitionFile -> Digital.PriceBuilder.Web.Models.CatalogueDefinitionFileViewModel"}
The domain POCO for CatalogueDefinitionFile is:
public class CatalogueDefinitionFile : BaseEntity
{
public CatalogueDefinitionFile()
{
this.ProductDefinitions = new List<ProductDefinition>();
}
public string TargetApplication { get; set; }
public virtual IList<ProductDefinition> ProductDefinitions { get; set; }
}
Base entity:
public abstract class BaseEntity
{
public BaseEntity()
{
this.CreatedDate = DateTime.Now;
this.UpdatedDate = DateTime.Now;
this.IsActive = true;
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDate { get; set; }
public string UpdatedBy { get; set; }
}
I've created a Profile:
public class DomainToViewModelMappingProfile : Profile
{
public override string ProfileName
{
get
{
return "DomainToViewModelMappings";
}
}
public DomainToViewModelMappingProfile()
{
ConfigureMappings();
}
/// <summary>
/// Creates a mapping between source (Domain) and destination (ViewModel)
/// </summary>
private void ConfigureMappings()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<ProductDefinition, ProductDefinitionViewModel>().ReverseMap();
cfg.CreateMap<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>().ReverseMap();
});
IMapper mapper = config.CreateMapper();
mapper.Map<ProductDefinition, ProductDefinitionViewModel>(new ProductDefinition());
mapper.Map<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>(new CatalogueDefinitionFile());
}
}
The Profile is reference within a AutoMapperConfiguration class which is then referenced in Global.asax:
public class AutoMapperConfiguration
{
public static void Configure()
{
// Create Automapper profiles
Mapper.Initialize(m =>
{
m.AddProfile<DomainToViewModelMappingProfile>();
m.AddProfile<ViewModelToDomainMappingProfile>();
});
}
}
The viewModel looks like this:
public class CatalogueDefinitionFileViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string TargetApplication { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDate { get; set; }
public string UpdatedBy { get; set; }
public virtual IList<ProductDefinition> ProductDefinitions { get; set; }
}
Then in my controller I have this:
public ActionResult Index()
{
IEnumerable<CatalogueDefinitionFileViewModel> viewModel = null;
IEnumerable<CatalogueDefinitionFile> files;
files = _catalogueDefinitionFileService.GetCatalogueDefinitionFiles();
viewModel = Mapper.Map<IEnumerable<CatalogueDefinitionFile>, IEnumerable<CatalogueDefinitionFileViewModel>>(files);
return View(viewModel);
}
The exception is thrown on
viewModel = Mapper.Map<IEnumerable<CatalogueDefinitionFile>, IEnumerable<CatalogueDefinitionFileViewModel>>(files);
Can someone help me understand why this is happening please?
Thanks in advance.
Your profile doesn't do anything:
public class DomainToViewModelMappingProfile : Profile
{
// etc ...
private void ConfigureMappings()
{
// You are just creating a local mapper config/instance here and then discarding it when it goes out of scope...
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<ProductDefinition, ProductDefinitionViewModel>().ReverseMap();
cfg.CreateMap<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>().ReverseMap();
});
// I assume this is just test code
IMapper mapper = config.CreateMapper();
mapper.Map<ProductDefinition, ProductDefinitionViewModel>(new ProductDefinition());
mapper.Map<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>(new CatalogueDefinitionFile());
}
}
Try this:
public class DomainToViewModelMappingProfile : Profile
{
public override string ProfileName
{
get
{
return "DomainToViewModelMappings";
}
}
public DomainToViewModelMappingProfile()
{
ConfigureMappings();
}
/// <summary>
/// Creates a mapping between source (Domain) and destination (ViewModel)
/// </summary>
private void ConfigureMappings()
{
CreateMap<ProductDefinition, ProductDefinitionViewModel>().ReverseMap();
CreateMap<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>().ReverseMap();
}
}
The Profile type your are inheriting probably relates to a map configuration object (hence having similar/same local methods).
Disclaimer: I've not used Automapper for a while, but the above appears to be your issue.
I just tested and things work fine. The following mapping passes:
Mapper.CreateMap<CatalogueDefinitionFile, CatalogueDefinitionFileViewModel>();
var obj = Mapper.Map<IEnumerable<CatalogueDefinitionFileViewModel>>(new List<CatalogueDefinitionFile>{
new CatalogueDefinitionFile
{
Id = 101,
Name = "test",
TargetApplication = "test",
IsActive = false,
CreatedBy = "test",
CreatedDate = DateTime.Now,
UpdatedBy = "test",
UpdatedDate = DateTime.Now,
ProductDefinitions = new List<ProductDefinition> { new ProductDefinition { MyProperty = 100 } }}
});
I have this situation:
// Core Business classes
public class Invoice
{
public Guid Id { get; protected set; }
public int Number { get; set; }
public IList<InvoiceItem> Items { get; protected set; }
}
public class InvoiceItem
{
public Guid Id { get; protected set; }
public string Product { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
// MVC Models
public class InvoiceModel
{
public Guid Id { get; set; }
public int Number { get; set; }
public IList<InvoiceItemModel> Items { get; set; }
}
public class InvoiceItemModel
{
public Guid Id { get; set; }
public string Product { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
The automapper configuration
public class MyProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceModel>();
Mapper.CreateMap<InvoiceItem, InvoiceItemModel>();
}
}
Then when I want to pass a model to my view, for example to edit an Invoice object, I do:
...
var invoice = Repository.Get<Invoice>(id);
return View("Update", Mapper.Map<InvoiceModel>(invoice));
...
And then I can iterate the Items collection with InvoiceItemModels.
The issue is when I want to retrieve a bunch of Invoices, for example in an index.
...
var invoices = Repository.ListAll<Invoice>();
return View("Index", invoices.Select(Mapper.Map<InvoiceModel>).ToList());
...
I don't want the "Items" to be loaded. A better configuration for this case will be:
public class MyFlatProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceModel>()
.ForMember(m => m.Items, opt => opt.Ignore());
Mapper.CreateMap<InvoiceItem, InvoiceItemModel>();
}
}
But I have no idea how to switch between "Profiles".
Is there a way to "pick" a particular configuration of mapping?
Unfortunately, you have to create separate Configuration objects, and create a separate MappingEngine for each.
First, declear a static class to hold the mappers
public static class MapperFactory
{
public static MappingEngine NormalMapper()
{
var normalConfig = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
normalConfig.CreateMap<Invoice, InvoiceModel>();
normalConfig.CreateMap<InvoiceItem, InvoiceItemModel>();
var normalMapper = new MappingEngine(normalConfig);
return normalMapper;
}
public static MappingEngine FlatMapper()
{
var flatConfig = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
flatConfig.CreateMap<Invoice, InvoiceModel>()
.ForMember(m => m.Items, opt => opt.Ignore());
flatConfig.CreateMap<InvoiceItem, InvoiceItemModel>();
var flatMapper = new MappingEngine(flatConfig);
return flatMapper;
}
}
Then you can call the MappingEngine to do the mapping (The syntax is the same as Mapper object).
return View("Update", MapperFactory.FlatMapper().Map<InvoiceModel>(invoice));
return View("Update", MapperFactory.NormalMapper().Map<InvoiceModel>(invoice));
I am developing a MVC Project with Entity framework and i have a category table like this :
public partial class Categories
{
public Categories()
{
this.Categories1 = new HashSet<Categories>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public Nullable<int> RelatedCategoryId { get; set; }
public virtual ICollection<Categories> Categories1 { get; set; } //Children
public virtual Categories Categories2 { get; set; } //Parent
}
When i get table data with EF, it gives me the object i want. Parents with children.
class Program
{
static Entities db = new Entities();
static void Main(string[] args)
{
List<Categories> categories = db.Categories.Where(item => item.RelatedId == null).ToList();
}
}
With relatedId == null part, i get the main categories which has no parent.
There is no problem this far. But i want to cast categories object which ef returned to another class which is :
public class NewCategories
{
public int Id { get; set; }
public string Name { get; set; }
private List<NewCategories> _subCategories;
public NewCategories()
{
_subCategories= new List<NewCategories>();
}
public List<NewCategories> SubCategories { get { return _subCategories; } }
}
And i want new List<NewCategories> newCategories object.
How can i accomplish that?
Thanks.
I think you have to create a recursive method to convert Categories to NewCategories, something like this (I'm not sure if it works, but it's worth trying):
public NewCategories ConvertToNewCategories(Categories cat){
NewCategories nc = new NewCategories {Id = cat.CategoryId, Name = cat.CategoryName};
nc.SubCategories.AddRange(cat.Categories1.Select(c=>ConvertToNewCategories(c)));
return nc;
}
//Then
List<NewCategories> categories = db.Categories.Where(item => item.RelatedId == null)
.Select(item=>ConvertToNewCategories(item))
.ToList();
i try to serialize a class into xml.
after the serialization , i would like to update the statusid.
I'm able to find the first order and alter the statusId , when goes to the 2nd orderid,here is where i encountered the ambiguous match found error.
here is the main method:
using (var Context = new Context())
{
var orderRepo = new OrderRepository(Context);
foreach (var orderId in orderIds)
{
var order = orderRepo.Find(orderId);
order.orderStatusID = 5;
}
orderRepo.Save();
}
in the OrderRepository.cs
public Order Find(int id)
{
return _context.Orders.Find(id);
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception ex)
{
_logger.Error(ex);
}
}
order.cs:
[XmlRoot("Orders")]
[NotMapped]
public class OrderCollection
{
public OrderCollection() { Orders = new List<Order>(); }
[XmlElement("Order")]
[NotMapped]
public List<Order> Orders { get; set; }
}
[Serializable()]
public class Order
{
public int id { get; set; }
[XmlElement("date")]
public DateTime createdDate
{
get
{
return (_createdDate == default(DateTime))
? DateTime.Now
: _createdDate;
}
set { _createdDate = value; }
}
private DateTime _createdDate = default(DateTime);
public string firstName { get; set; }
public string lastName { get; set; }
[XmlIgnore]
public int orderStatusID { get; set; }
}
turn out to be an entity had two members with the same name and different casing. One was a field from the database, and one was a navigation property. Just renaming one of the two solved the problem.