SQL result to complex object with children - c#

We're looking for a more efficient way to build objects with full parent/child relationships than looping through the results of separate queries and assigning children to parents. It seems to be a big bottleneck in our code right now (we often need to do this with 1,000's to 1,000,000's of entities at a time.
Is there a way to use a query with db.Database.SqlQuery<Parent>("SELECT * FROM xyz) that returns parent/child/grandchildren relationships?
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public List<Grandchild> Grandchildren { get; set; }
}
public class Grandchild
{
public int Id { get; set; }
}

using EF you could do something like this
db.Parent.Include(e=> e.Child).Include(e=> e.Child.SelectMany(s=> s.Grandchild));
using SqlQuery you should ensure the SQL SELECT includes all the necessary columns, using the aliases when necessary in order to match object property names

Related

Mapster - Dynamic property selection or ignore

I have a business need to dynamically select ONLY the properties of a given model that are specified, similar to an OData select clause. I am currently using Mapster's ProjectToType functionality to populate view models from EF Core entities.
Is there any way to tell Mapster to only select a given list of properties in the query that it generates? Or a way to take the full model mapping, and change mappings at runtime in an instance of TypeAdapterConfig to ignore properties that aren't in a given list of properties?
The end solution needs to be generic and work with navigation properties, because it will be applied to all of our entities in the database. We also used DynamicLinq in some cases, not sure if that can be used on top of Mapsters ProjectToType functionality.
Example:
Entities (Some properties omitted for length):
namespace DataAccess.Entities
{
public class Series
{
public Guid Id { get; set; }
public string Description { get; set; }
public long? StackRank { get; set; }
public string EntityId { get; set; }
// Other properties
}
public class Model
{
public Guid Id { get; set; }
public string Description { get; set; }
public string EntityId { get; set; }
public long? StackRank { get; set; }
public Guid SeriesId { get; set; }
public virtual Series Series { get; set; }
// Other properties
}
}
View Models (Some properties omitted for length):
namespace Models
{
public class Model
{
public Guid Id { get; set; }
public string Description { get; set; }
public string EntityId { get; set; }
public long? StackRank { get; set; }
public Guid SeriesId { get; set; }
public virtual Series Series { get; set; }
// Other properties
}
public class Series
{
public Guid Id { get; set; }
public string Description { get; set; }
public long? StackRank { get; set; }
public string EntityId { get; set; }
// Other properties
}
}
Given a rest call to get a list of all Model view models, with this list of properties to include:
var properties = new List<string> {
"Id",
"EntityId"
"Description",
"Series.Id",
"Series.Description",
"Series.EntityId"
}
The results would return some type of dictionary, dynamic, or anonymous object that contained ONLY these properties, and the other properties would not even be included in the final select of the SQL query that gets created.
In the end, I decided to use Arca Artem's suggestion, with a little twist. I used reflection to grab a list of all properties of the model and cache them. After that, I compared the cached properties vs the list of properties to include, and ignored the properties that weren't in both lists. Kinda like this:
var clonedConfig = mapsterInstance.Clone();
clonedConfig.ForType<TSource, TDestination>().Ignore(propertiesToIgnore);
var models = await query.ProjectToType<TDestination>(clonedConfig).ToListAsync();
Maybe not the most elegant solution, but it worked well enough for what I needed. I also set up our json serializer to ignore null values.

Automapper nested relationship "back" mapping

Let's say I have a nested DTO, like this (in reality it is a lot more complex):
public class OrderDTO
{
public int Id { get; set; }
public List<ItemDTO> Item { get; set; }
}
public class ItemDTO
{
public int Id { get; set; }
}
that maps to:
public class Order
{
public int Id { get; set; }
public List<Item> Item { get; set; }
}
public class Item
{
public int Id { get; set; }
public Order Order { get; set; }
}
When mapping from the OrderDTO down to Order, I would like that the Order property from Item references its order, as to make something like var order = item.Order; possible.
Is there any way I can do that when mapping? Also, is it possible to configure that for all my nested mappings?
Using BeforeMap you can save the parent object in context.Items and then use that value in a resolver for the parent reference. If you respect a naming convention, you can use ForAllMaps to apply it wherever you need.

How should I order nested entities when I load parent entity?

I have entities similar to this:
public class Search
{
public int ID { get; set; }
public bool Complete { get; set; }
public int Processed { get; set; }
public virtual ICollection<PostCode> PostCodes { get; set; }
}
public class PostCode
{
public int ID { get; set; }
public string Value { get; set; }
}
I want to make sure that the PostCode collection is always ordered by its ID field when I load a Search entity.
Is there a way to set an order for nested collection when I load it?
There is many thousand postcodes so it is expensive process to iterate over whole list. In the below code am I iterating over every postcode twice or is this the right way to do it?
Search s = db.Searches.Include("PostCodes").FirstOrDefault(x => x.Complete == false);
s.PostCodes = s.PostCodes.OrderBy(x => x.ID).ToList();
I haven't profiled it but it feels like it's taking longer now.

EF Code First: Trying to get a child property's parameters to appear in the parent class' table

I have a class, "Search". See definition below:
public class Search
{
[Key]
public int SearchID { get; set; }
public int UserID { get; set; }
public SearchParameters SearchParameters { get; set; }
public ICollection<SearchProvider> SearchProviders { get; set; }
public User User;
}
SearchParameters is a class with value types, and a few sub-classes; as defined below:
public class SearchParameters
{
public List<string> SearchTerms { get; set; }
public int MaxRecords { get; set; }
public DistanceParameter Distance { get; set; }
public PriceRangeParameter PriceRange { get; set; }
}
The idea is that I do not want a separate SearchParameters table that has to link to the Search table because every property of the search is always one to one (Except for SearchTerms). Really, what I want EF to do is 'bring up' the child classes' properties so we end up with All the properties of SearchParameter in the SearchTable (and all the parameters of the DistanceParameter and PriceRangeParameter objects themselves). What annotations or other logic would I need for this to work? Thanks!
I think EF Complex Type mapping is what you need, see more here:
http://weblogs.asp.net/manavi/archive/2011/03/28/associations-in-ef-4-1-code-first-part-2-complex-types.aspx

Fluent NHibernate: Map property without foreign key?

I have these two classes:
public class Parent
{
public virtual string Name { get; set; }
public virtual string Family_id { get; set; }
}
public class Children
{
public virtual string Name { get; set; }
public virtual DateTime BirthDate { get; set; }
public virtual string Family_id { get; set; }
}
When I fetch a parent, I also want to fetch the oldest (ordered by BirthDate) children that has the same Family_id as the parent.
There is no foreign key between the parent and the children in the database.
(I do not want to use two different repositories, I want the functionality in the mappings)
Is property-ref something I can use?
One strategy would be to force an Eager Load on a Children collection and create another property to get the oldest child.
Property-Ref is used to join to another table using a column which is not the primary key.
public class Parent
{
public virtual int Id {get; set;}
public virtual string Name { get; set; }
public virtual string Family_id { get; set; }
public virtual Children OldestChild {
get {
return Children.OrderBy(x=>x.BirthDate).FirstOrDefault();
}}
public virtual IList<Children> Children {get; set;}
}
public class ParentMap : ClassMap<Parent>{
public ParentMap(){
Id(x=>x.Id);
Map(x=>x.Name);
HasMany(x=>x.Children).PropertyRef("Family_id").Fetch.Join();
}
}
Another possibility would be to add a column to the Parent table (OldestChild_FK) and then join in that row from the Children table.
I think what you want to do is create a property on the Parent called OldestChild or a list of Oldest Children and ignore that property and write some custom query (HQL or SQL) to return the results you want.
Here is a thread on ignoring properties in FluentNhibernate.

Categories