automapper create.map() to a collection property - c#

How do you map to a collection of property but only interested with the last record of the collection.
Say, something like.
public class ItemDTO <-- destination class
{
public string Name { get; set; }
public decimal PricesPrice { get; set; }
}
public class Item <-- Source class
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Price> Prices { get; set; }
}
public class Price <-- Source class
{
public int Id { get; set; }
public int ItemId { get; set; }
public decimal Price { get; set; }
public virtual Item Item { get; set; }
}
Then I have tried something like this, but doesn't seems to be right.
Mapper.CreateMap<Item, ItemDTO>()
.ForMember(dto => dto.PricesPrice, opt => opt.MapFrom(s => s.Prices.LastOrDefault().Price));
EDIT: And then after that I did a Projection because If I use Mapper.Map() It will return the entire result set which is not what I want, I only want the values I needed. So I did something like this:
Project().To<ItemDTO>()
Well, basically, I want something like this:
from item in SomeDbContext.Items
where item.ItemId == 1
select new ItemDTO
{
Name = item.Name,
PricesPrice = item.Prices.LastOrDefault()
}
Can above code be done using AutoMapper?

I think I understand what you are trying to do now.
Try this
from item in SomeDbContext.Items
where item.ItemId == 1
select Mapper.Map<ItemDTO>(item)
or you could use LINQ
SomeDbContext.Items.Where(i=> i.ItemId == 1).Select(Mapper.Map<ItemDTO>)
Both of these will return you a list of ItemDTO object where the ItemId is 1

Related

How to filter out items that are in a list of a list?

I am a little puzzled to how I can add items depending on the value that is in a list of a list.
I have these models
public class ItemsModel
{
public int OrderItemId { get; set; }
public string Sku { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
}
public class OrdersModel
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public List<ItemsModel> Items { get; set; }
}
public class OrderResultModel
{
public List<OrdersModel> Orders { get; set; }
}
Question
I need to add all orders from an api, but add Items that has SKU only ending with "5".
How can I do this?
How I tried it
I have an API call to get all orders.. List<OrdersModel> GetOrders().
Then I add the data like so:
public ObservableCollection<OrdersModel> Orders { get; private set; }
...
Orders = new ObservableCollection<OrdersModel>();
foreach (var item in GetOrders())
{
Orders.Add(item);
}
Here is how I tried to add values with SKU ending with 5:
var list = GetOrders().Where(x => x.Items.Any(i => i.Sku.EndsWith("5")));
foreach (var item in list)
{
Orders.Add(item);
}
Problem
This still shows me all values.. why? And how I can correct it?
foreach(var order in GetOrders())
{
order.Items = order.Items.Where(i => i.Sku.EndsWith("5")).ToList();
Orders.Add(order);
}
It's showing you all the value because Any() will evaluate to true if any of List of ItemsModels ends with 5.
you should filter on the list first something like
var filteredItems = orders.Items.Where(i => i.sku.EndsWith("5"))

Why can't I get this model to map to this viewmodel?

I have this entity model for a recursively structured category tree:
public class ProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public ICollection<ProductInCategory> ProductInCategory { get; set; }
public ICollection<FrontPageProduct> FrontPageProduct { get; set; } // Nav.prop. to front page product
// Recursive sorting:
public void RecursiveOrder()
{
Children = Children.OrderBy(o => o.SortOrder).ToList();
Children.ToList().ForEach(r => r.RecursiveOrder());
}
}
... and this, supposedly, matching ViewModel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; } // Used for assigning a product to multiple categories in Product/Edit
// Nav.props:
public ViewModelProductCategory ParentCategory { get; set; } // Nav.prop. to parent
public ICollection<ViewModelProductCategory> Children { get; set; } // Nav.prop. to children
public IEnumerable<ViewModelProduct> Products { get; set; } // Products in this category
public IEnumerable<ViewModelFrontPageProduct> FrontPageProducts { get; set; }
public string ProductCountInfo { get { return Products?.Count().ToString() ?? "0"; } }
}
When I try to populate the viewmodel, like this:
List<ProductCategory> DbM =
await _context.ProductCategories
.Include(c => c.Children)
.Where(x => x.ParentId == null)
.OrderBy(o => o.SortOrder)
.ToListAsync();
foreach (var item in DbM)
{
VMSelectCategories.Add(
new ViewModelProductCategory{
Id = item.Id,
Children = item.Children,
Title = item.Title
});
}
VisualStudio screams at me that it can't implicitly convert ProductCategory to ViewModelCategory. This happens at Children = item.Children.
Why isn't it working? Can't I have additional properties in the viewmodel that I use unrelated to the original entity model? Like Checked and ProductCountInfo?
In this line:
Children = item.Children,
Children is the ViewModelProductCategory.Children property, which is type ICollection<ViewModelProductCategory>, while item.Children is the ProductCategory.Children property, which is type ICollection<ProductCategory>. They are different types and neither inherits or implements the other so why would you expect to be able to assign an object of one type to a property of the other type? Would you expect this to work:
var list1 = new List<int> {1, 2, 3};
List<string> list2 = list1;
Of course not (I hope) because assigning a List<int> object to a List<string> variable would be silly. What you're trying to do is exactly the same. You need to provide some way to convert from one type to the other and then implement that in your code. An option for that might be like this:
Children = item.Children.Select(pc => MapToViewModel(pc)).ToList(),
where MapToViewModel is a method that you write to create a ViewModelProductCategory and populate its properties from a ProductCategory parameter.
You might also look at using something like AutoMapper.

How do you convert several nested loops into lambda or linq when you have a complex object structure?

I am running into trouble when trying to convert my multiple nested loops into a lambda or linq expression. I think I am having a hard time understanding how to properly access the properties when using the .All or .Contains methods. In any case, help is greatly appreciated. (I have read several other posts on this subject but am still struggling to make it work.)
Here are what the classes look like:
public class RecipeSearch
{
public List<Recipe> Recipe { get; set; }
public List<Meal> MealSettings { get; set; }
public List<Ingredient> MainIngredient { get; set; }
}
public class Meal
{
public int Id { get; set; }
public bool Value { get; set; }
public string DisplayName { get; set; }
}
public class MainIngredient
{
public int Id { get; set; }
public bool Value { get; set; }
public string DisplayName { get; set; }
}
Here's the nested loop:
IEnumerable<Recipe> recipeList = dbContext.Recipes
.OrderBy(r => r.name)
.Where(r => r.name.Contains(name) || string.IsNullOrEmpty(name))
.ToList();
//Model object is of type RecipeSearch
IEnumerable<Meal> selectedMeals = model.MealSettings.Where(x => x.Value == true);
IEnumerable<MainIngredient> selectedIngredients = model.MainIngredient.Where(x => x.Value == true);
foreach (var selected in recipeList) //loop through the master list
{
foreach (var item in selectedMeals) //loop through selected meal categories
{
if (selected.mealCategoryId == item.Id) //passed the meal category check (i.e. it exists)
{
foreach (var ingredient in selectedIngredients) // selected master ingredients
{
if (selected.Ingredients.Any(x => x.SubCategory.mainCategoryid == ingredient.Id))
{
recipe.Recipe.Add(selected);
break;
}
}
}
}
}
I suppose it should be noted that the loop works completely as expected. I just think that it lambda/linq is more clean to read.
EDIT: Here are the other objects:
public partial class Recipe
{
public Recipe()
{
Directions = new HashSet<Direction>();
Images = new HashSet<Image>();
Ingredients = new HashSet<Ingredient>();
Nutritions = new HashSet<Nutrition>();
Ratings = new HashSet<Rating>();
}
public int recipeId { get; set; }
//Removed other properties that are not relevant
public virtual ICollection<Ingredient> Ingredients { get; set; }
public virtual MealCategory MealCategory { get; set; }
public virtual RecipeStatus RecipeStatus { get; set; }
}
public partial class Ingredient
{
public int ingredientId { get; set; }
public int? recipeId { get; set; }
public int? subCategoryId { get; set; }
public int measurementId { get; set; }
public int amount { get; set; }
public virtual Recipe Recipe { get; set; }
public virtual SubCategory SubCategory { get; set; }
public virtual Measurement Measurement { get; set; }
}
public partial class SubCategory
{
public SubCategory()
{
Ingredients = new HashSet<Ingredient>();
}
public int subCategoryId { get; set; }
[Required]
[StringLength(255)]
public string name { get; set; }
public int? mainCategoryid { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
public virtual Maincategory Maincategory { get; set; }
}
Would this work?
var query = from selected in receipeList
join item in selectedMeals on selected.MealCategoryId equals item.Id
where selected.Ingredients.Select(x => x.SubCategory.mainCategoryid.Value)
.Intersect(selectedIngredients.Select(s => s.Id)).Count() > 0
select selected;
foreach(var sel in query)
recipe.Recipe.Add(sel);
I can't see where you are getting recipe.Recipe from though.
Basically in order to help you translate that into linq and adjust as you want:
This:
foreach (var selected in recipeList) //loop through the master list
{
foreach (var item in selectedMeals) //loop through selected meal categories
{
if (selected.mealCategoryId == item.Id) //passed the meal category check (i.e. it exists)
{
}
}
}
Translates into join like so:
from selected in receipeList
join item in selectedMeals on selected.MealCategoryId equals item.Id
Also, these lines:
if (selected.Ingredients.Any(x => x.SubCategory.mainCategoryid == ingredient.Id))
{
recipe.Recipe.Add(selected);
break;
}
Can be translated into:
where selected.Ingredients.Select(x => x.SubCategory.mainCategoryid.Value)
.Intersect(selectedIngredients.Select(s => s.Id)).Count() > 0
select selected;
//and then
foreach(var sel in query)
recipe.Recipe.Add(sel);
Notice the following part,
IEnumerable<Recipe> recipeList = dbContext.Recipes
.OrderBy(r => r.name)
.Where(r => r.name.Contains(name) || string.IsNullOrEmpty(name))
.ToList();
2 things smell here:
First, you should swap the or-condition to check String.IsNullOrEmpty first, and
second put the where before the orderby to reduce items that need to be ordered.
IEnumerable<Recipe> recipeList = dbContext.Recipes
.Where(r => string.IsNullOrEmpty(name) || r.name.Contains(name))
.OrderBy(r => r.name)
.ToList();
Depending on the ItemCount this might give you quite some "boost".

Find object with in class using LINQ

I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}

Query some properties on relation with NHibernate

I have a mapped model with Nhibernate like this:
public class A
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
/* and other 20 properties... */
}
public class B
{
public virtual long Id { get; set; }
public virtual A ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
I would like to create a query (queryover, linq, hql etc... anyway) to get a List<B> and fill ItemA property with just Id and Number properties because I need only this properties (I have lots of properties in A class and I will not use it on my results). Is there any way to do this or should I create a ViewModel? If I need to create a ViewModel, how can I do this with QueryOver?
Thank you!
Yes, you have to create a view model, like following:
public class BViewModel
{
public virtual long Id { get; set; }
public virtual AViewModel ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
public class AViewModel
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
}
Now you oculd query it with linq like this:
var listOfB = session.Query<B>()
.Select(b => new BViewModel
{
Id = b.Id,
Name = b.Name,
Date = b.Date,
ItemA = new AViewModel
{
Id = b.ItemA.Id,
Number = b.ItemA.Number,
},
}).ToList();
using QueryOver, you can use JoinAlias to join A with B, and SelectList to retrieve only the properties that you want.
Then use TransformUsing to transform the result into whatever format you want.
Something along the lines of:
A aTemplate = null;
s.QueryOver<B>().
.JoinAlias(() => aTemplate)
.Where (/*whatever conditions*/)
.SelectList(list => list
.Select(() => aTemplate.Id)
.Select(() => aTemplate.Number)
.TransformUsing(Transformers.AliasToBeanTransformer)
.List<A/*or some DTO*/>();

Categories

Resources