I try to figure out how to flatten a collection of Merchants, each containing a collection of Orders to a flat List of OrderViewModels.
Here my DTO:
public class Merchant
{
public string MerchantName { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public string OrderId { get; set; }
}
And Here's the view model:
public class OrderViewModel
{
public string MerchantName { get; set; }
public string OrderId { get; set; }
}
My Goal is to flatten a List<Merchant> to a List<OrderViewModel> whereas the following test structure should result in 6 view models:
var myMerchants = new List<Merchant>
{
new Merchant
{
MerchantName = "Merchant X",
Orders = new List<Order>
{
new Order { OrderId = "Order 1"},
new Order { OrderId = "Order 2"},
new Order { OrderId = "Order 3"}
}
},
new Merchant
{
MerchantName = "Merchant Y",
Orders = new List<Order>
{
new Order { OrderId = "Order 4"},
new Order { OrderId = "Order 5"},
new Order { OrderId = "Order 6"}
}
}
};
var models = Mapper.Map<List<OrderViewModel>>(myMerchants);
Because the cardinality of the root objects isn't 1:1, (i.e. 2 root Merchants need to map to 6 OrderViewModels), you may need to resort to a custom TypeConverter and operate at the collection level, where you can use .SelectMany to do the flattening:
public class MyTypeConverter : ITypeConverter<IEnumerable<Merchant>, List<OrderViewModel>>
{
public List<OrderViewModel> Convert(ResolutionContext context)
{
if (context == null || context.IsSourceValueNull)
return null;
var source = context.SourceValue as IEnumerable<Merchant>;
return source
.SelectMany(s => s.Orders
.Select(o => new OrderViewModel
{
MerchantName = s.MerchantName,
OrderId = o.OrderId
}))
.ToList();
}
}
Which you can then bootstrap:
Mapper.CreateMap<IEnumerable<Merchant>, List<OrderViewModel>>()
.ConvertUsing<MyTypeConverter>();
And then mapped as such:
var models = Mapper.Map<List<OrderViewModel>>(myMerchants);
An interesting finding is that, only do the below is enough to achieve the goal without automapper.
var models = myMerchants.SelectMany(s => s.Orders.Select(o => new OrderViewModel { MerchantName = s.MerchantName, OrderId = o.OrderId })).ToList();
Old question but thought would be helpful for newer versions.
I am using .Net core 2 with automapper. I prefer to do the ProjectTo extension of queryable
queryable
.SelectMany(outterClass => outterClass.innerList)
.AsQueryable()
.ProjectTo<OutterClassDto>();
Then, configure like so:
config.CreateMap<OuterClass, OuterClassDto>();
It's also possible to combine SelectMany with AutoMapper and do the mapping for each item in the flattened collection. You need two mappings for this Merchant -> OrderViewModel and Order -> OrderViewModel
var models = myMerchants.SelectMany(s => s.Orders.Select(o =>
{
var mappedItem = Mapper.Map<OrderViewModel>(s);
Mapper.Map(o, mappedItem);
return mappedItem;
})).ToList();
Related
I am developing an ecommerce website and I use three tables for the management of products and categories.
I would need one or more linq queries with entity framework to get the list of mother categories, daughters and the list of related products.
There is a Category table, like:
Id
NameCat
ParentId
There is a products table, like:
Id
Name
Live
Info, etc.
There is a product-category table, like:
Id
categoryId
productId
sortOrder
This is my viewmodel
public class CatViewModel
{
public int IdCat { get; set; }
public string NameCat { get; set; }
public int ParentId { get; set; }
public List<CatViewModel> Children { get; set; }
public List<Product> Products { get; set; }
}
Clearly each parent category must have the complete list of products of all its child categories
You are on the right track. In the method that is supposed to return view with this CatViewModel you can do this:
var mainObject = Context.Category.FirstOrDefault(x => x.IdCat == "YOUR PARAMETER OF ID HERE");
var viewModel = new CatViewModel
{
IdCat = mainObject.IdCat,
NameCat = mainObject.NameCat,
ParentId = mainObject.ParentId,
Children = Context.Children(query here).ToList(),
Products = Context.Products(query here).ToList()
};
Solution 2:
You can first create:
var listChildern = Context.Children(query here).ToList();
var listProducts = Context.Products(query here).ToList();
Then return your viewModel which you fill just like this:
var mainObject = Context.Category.FirstOrDefault(x => x.IdCat == "YOUR PARAMETER OF ID HERE");
var viewModel = new CatViewModel
{
IdCat = mainObject.IdCat,
NameCat = mainObject.NameCat,
ParentId = mainObject.ParentId,
Children = listChildern ,
Products = listProducts
};
I have three tables.
I need a CatViewModel list type model, I tried this:
var cats = db.Cats.AsEnumerable() // <-- Force full execution (loading) of the above
.Where(e => e.ParentId == null) // <-- then apply the root filter
.ToList();
var myList = cats.Select(c => new CatViewModel {
IdCat = c.IdCat,
NameCat = c.Nomecat,
Children = c.Children,
Products = ???
}).ToList();
i have ravendb document called Orders like this
{
"MyOrders": [
"S1",
"S2"
],
"Id": "6666"
}
I will query this document and pass order string for example S1
Then it should return me that document as S1 matches it. I am pretty new to this ravendb. I am unable to find way. I have written only this so far
public class MyOrderIndexes: AbstractIndexCreationTask<Order>
{
public MyOrderIndexes()
{
Map = Orders => from Order in Orders
select new
{
ID= Order.Id
};
Index(x => x.Id, FieldIndexing.NotAnalyzed);
}
}
can someone help me
To query that, you don't need to create an index. Just do a query, Raven will create it for you.
This query should work just fine:
var hasS2 = session.Query<Orders>()
.Where(o => o.MyOrders.Contains("S2"))
.ToList();
One problem is the FieldIndexing on Id, and also that you need to map MyOrders to perform query on it.
Index:
public class MyOrderIndexes: AbstractIndexCreationTask<Order>
{
public MyOrderIndexes()
{
Map = Orders => from Order in Orders
select new
{
Id = Order.Id,
MyOrders = Order.MyOrders
};
}
}
Query:
var results = session
.Query<Order, MyOrderIndexes>()
.Where(x => x.MyOrders.Any(l => l == "S1"))
.ToList();
Model:
public class Order
{
public string Id { get; set; }
public ICollection<string> MyOrders { get; set; }
}
I'm using jqueryui autocomplete to assist user in an item selection. I'm having trouble selecting the correct items from the objects' subcollections.
Object structure (simplified) is
public class TargetType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubCategory> SubCategories { get; set; }
public TargetType()
{
SubCategories = new HashSet<SubCategory>();
}
}
public class SubCategory
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubTargetType> SubTargetTypes { get; set; }
public SubCategory()
{
SubTargetTypes = new HashSet<SubTargetType>();
}
}
Currently I'm doing this with nested foreach loops, but is there a better way?
Current code:
List<SubTargetResponse> result = new List<SubTargetResponse>();
foreach (SubCategory sc in myTargetType.SubCategories)
{
foreach (SubTargetType stt in sc.SubTargetTypes)
{
if (stt.Name.ToLower().Contains(type.ToLower()))
{
result.Add(new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
}
}
}
You can do using Linq like this
var result = myTargetType.SubCategories
.SelectMany(sc => sc.SubTargetTypes)
.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))
.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
The above query doesn't work. The following should work, but I'd not recommend that as that'd not be faster or more readable.
var result = myTargetType.SubCategories
.Select(sc => new Tuple<int, IEnumerable<SubTargetType>>
(sc.Id,
sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))))
.SelectMany(tpl => tpl.Item2.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = tpl.Item1,
Name = stt.Name }));
Actually there are 2 different questions.
LINQ select all items of all subcollections that contain a string
Solutions:
(A) LINQ syntax:
var result =
(from sc in myTargetType.SubCategories
from stt in sc.SubTargetTypes.Where(t => t.Name.ToLower().Contains(type.ToLower()))
select new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
(B) Method syntax:
var result =
myTargetType.SubCategories.SelectMany(
sc => sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower())),
(sc, stt) => new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
Currently I'm doing this with nested foreach loops, but is there a better way?
Well, it depends of what do you mean by "better". Compare your code with LINQ solutions and answer the question. I personally do not see LINQ being better in this case (except no curly braces and different indentation, but a lot of a hidden garbage), and what to say about the second LINQ version in this answer - if that's "better" than your code, I don't know where are we going.
I am trying to make a select from the database using the entity framework 5.0.
I have a table called Persons that is referenced by PersonsImages, so basically one record from Persons can have many PersonsImages.
I've made a select statement that gives the Persons, but I would also like to get the PersonsImages as a List<PersonsImages>, and put them in a custom object.
This is the code that I have so far:
var person = new Persons();
using (var context = new PersonEntities())
{
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages // there's an error here
})
.FirstOrDefault();
}
The Persons and the PersonsImages classes look like that (they are copies of the ones generated by the entity framework):
public partial class Persons
{
public Persons()
{
this.PersonsImages = new HashSet<PersonsImages>();
}
public string personName { get; set; }
public string personLastName { get; set; }
public virtual ICollection<PersonsImages> PersonsImages { get; set; }
}
public partial class PersonsImages
{
public string imageName { get; set; }
public byte[] image { get; set; }
public virtual Persons Persons { get; set; }
}
I know I can make a second select and "manually" find them, but isn't it possible to do it in one, just as what the entity framework normally does?
Assuming your error is "can't construct an object in a LINQ to Entities query" - project into an anonymous type, call the ToArray() method to enumerate the results, then project into new instances of Persons:
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.ToArray() // data now local
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.FirstOrDefault();
I have a requirement where I need to get a List of Model1 (List) using Linq, the Model1 have List of Model2 (List) in it and I need to fetch that also. For this I have created a Linq but m getting following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1 [OurCourse]
ToList[OurCourse](System.Collections.Generic.IEnumerable1
[OurCourse])' method, and this method cannot be translated into a
store expression.
Please refer below for detail:
I have two tables Colleges and Courses, with following columns:
College: ID, Name, Contact, City, Address
Cource: ID, CollegeID, Name, Years
My project have two view models for them, as follows:
public class OurCollege
{
public string Name { get; set; }
public string Contact { get; set; }
public List<OurCourse> MyCourses { get; set; }
}
public class OurCourse
{
public string Name { get; set; }
public int NumberOfYears { get; set; }
}
Here the the query query which I have prepared but I am getting the error:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
How about doing like this:
MyCourses = from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}
Your complete query will look now:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = (from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
Actually LINQ to Entities converts your query into SQL.It doesn't know how to translate ToList() in SQL
An alternate is to change you List<T> to IEnumerable<T> and remove ToList() frim your original code:
public IEnumerable<OurCourse> MyCourses { get; set; }
and in query:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
})
}).ToList();
For more details visit this Entity Framework ToList() in nested type (LINQ to Entities does not recognize the method)
A foreach loop will work, you can write the query something like this
var colleges = db.Colleges
.Select(x => new OurCollege()
{
CollegeId = x.CollegeId,
Name = x.Name,
Contact = x.Contact
}).ToList();
foreach (var college in colleges)
{
college.MyCourse = db.Course.Where(x => x.CollegeId == college.CollegeId)
.Select(x => new OurCourse()
{
Name = x.Name,
NumberOfYears = x.Years
}).ToList()
}