i have this domain objects:
public class Societa : EquatableObject<Societa>
{
public virtual int IdSocieta { get; set; }
public virtual string NomeSocieta { get; set; }
}
public class Attivita {
public virtual int IdAttivita { get; set; }
public virtual IEnumerable<ProcessoEsaminato> Processi
}
public class ProcessoEsaminato {
public virtual ProcessoSocieta ProcessoCoperto { get; set; }
public virtual int Anno { get; set; }
}
public class ProcessoSocieta {
public override int Id { get; set; }
public virtual Societa SocietaDiretta { get; set; }
public virtual Societa SocietaService { get; set; }
}
public class Processo {
public virtual int Id { get; set; }
public virtual string NomeProcesso { get; set; }
public virtual IEnumerable<ProcessoSocieta> SocietaAttivate
}
i nedd to extract from db with QueryOver or LinqToNHibernate every Process of an Attivita with NomeProcesso, SocietaDiretta.NomeSocieta and SocietaService.NomeSocieta
So i think:
i have to start from Processo and get those that in their SocietaAttivate has one that is in Processi collection of Attivita, looking at ProcessoCoperto property of every element of this collection
i try this:
public IEnumerable<object> ProcessiPerAttivita (Attivita att) {
ProcessoSocieta ps = null;
var elencoPS = att.Processi.Select(p => p.ProcessoCoperto).ToList<ProcessoSocieta>();
return _session.QueryOver<Processo>()
.JoinAlias(processo => processo.SocietaAttivate, () => ps)
.Where(x => x.SocietaAttivate.IsIn(elencoPS))
.List();
}
but Where(x => x.SocietaAttivate.IsIn(elencoPS)) is not what i nedd, since it wants only a list of id. so first question is how can i do this?
Second question is how can i select only fields i need from different object, coming from different level of aggregation?
EDIT:
now i try
_session.QueryOver<Processo>()
.JoinAlias(processo => processo.SocietaAttivate, () => ps)
.Where(x => x.SocietaAttivate.Any(p => elencoPS.Contains(p)) != null)
.List();
but i get variable 'x' of type 'ProcessoSocieta' referenced from scope '', but it is not defined
Try this:
public IEnumerable<Processo> ProcessiPerAttivita (Attivita att) {
ProcessoSocieta ps = null;
var elencoPS = att.Processi.Select(p => p.ProcessoCoperto).ToList<ProcessoSocieta>();
return _session.QueryOver<Processo>()
.JoinAlias(processo => processo.SocietaAttivate, () => ps)
.WhereRestrictionOn(processo => ps.Id).IsIn(elencoPS.Select(el => el.Id).ToList())
.List<Processo>();
}
You must use the 'ps' alias!
Edit: you can use
.List<Processo>(); and return an IEnumerable<Processo>
Related
I have an entity as Plan with multiple sub-plans (children), each of which could be null.
For the PlanDto, I am trying to load up a list of all children rather than having a separate property for each child like the entity.
I have already achieved it manually through a foreach loop but now I am trying to do it via AutoMapper, which is failing for some reason.
Entities:
public class Plan
{
public virtual int Id { get; set; }
public DateTime Date { get; set; }
public virtual PlanDetail PlanChild1 { get; set; }
public virtual ObservationCare PlanChild2 { get; set; }
}
public class PlanDetail
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
public class ObservationCare
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
DTOs:
public class PlanDto: EntityDto
{
public DateTime Date { get; set; }
public IEnumerable<ChildPlan> ChildPlan { get; set; }
}
public class ChildPlan : EntityDto
{
public ChildPlanType Type { get; set; }
}
public enum ChildPlanType
{
PlanDetail,
ObservationCare
}
AutoMapper config:
configuration.CreateMap<Plan, PlanDto>();
configuration.CreateMap<PlanDetail, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.PlanDetail));
configuration.CreateMap<ObservationCare, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.ObservationCare));
Mapping attempt:
var output = new List<PlanDto>();
var plans = await _planRepository.GetAll().ToList();
foreach (var plan in plans)
{
output.Add(ObjectMapper.Map<PlanDto>(plan));
}
I do not know why ChildPlan DTOs in the output list are always null!
You have to specify the mapping for PlanDto.ChildPlan:
configuration.CreateMap<Plan, PlanDto>()
.ForMember(dto => dto.ChildPlan,
options => options.MapFrom(
p => new object[] { p.PlanChild1, p.PlanChild2 }.Where(c => c != null)));
If you are using Entity Framework Core, you have to use eager-loading:
var plans = await _planRepository.GetAll()
.Include(p => p.PlanChild1)
.Include(p => p.PlanChild2)
.ToList();
There's also a simpler and more efficient way to map a list:
var output = ObjectMapper.Map<List<PlanDto>>(plans);
How would one sort nested lists in place:
List of JobCollection
List of Jobs (sort by string Sponsor)
List of Items (sort by int Order)
Data model class structure:
public class JobCollection
{
public string Collection { get; set; }
public virtual TrulyObservableCollection<Job> Jobs { get; private set; } = new TrulyObservableCollection<Job>();
}
public class Job
{
public virtual JobCollection JobCollection { get; set; }
public string JobGUID { get; set; } = Guid.NewGuid().ToString();
public string Sponsor { get; set; }
public virtual TrulyObservableCollection<Item> Items { get; private set; } = new TrulyObservableCollection<Item>();
}
public class Item
{
public virtual Job Job { get; set; }
[Key]
public string ItemGUID { get; set; } = Guid.NewGuid().ToString();
public int Order { get; set; }
}
I've tried various ways but not getting anywhere with it such as:
EntireCollection.SelectMany(o => o.Jobs).ToList().ForEach(d => d.Sponsor = d.Items.OrderBy(e => e.Order).ToList());
elem => elem.Jobs.OrderBy(
job => job.Items.OrderBy(
item => item.Order
)
Argh!
The easiest way to do it, but not suggested.
jobCollection.ForEach( (jobCol) =>
{
jobCol.Jobs.ForEach( (job) =>
{
job.Items.OrderBy( item => item.Order );
});
jobCol.Jobs.OrderBy( (job) => job.Sponsor );
});
public class Module
{
public int id { get; set; }
public string moduleName { get; set; }
//navigation property
public virtual HashSet<Policy> policies { get; set; }
}
public class Policy
{
public int id { get; set; }
//foreign keys
public int subscriberId { get; set; }
//navigation properties
public virtual Subscriber subscriber { get; set; }
}
public class Subscriber
{
public int id { get; set; }
public string name { get; set; }
public int subscriptionId { get; set; }
// Navigation property
public virtual HashSet<Policy> policies { get; set; }
}
I have 3 related objects.
Module - Policy - Subscriber
A module has multiple policies
A policy has one subscriber
I need to list all the policies and subscribers under a certain module in JSON format. Due to the posts that I found on web I created this query:
return db.modules
.Where(m => m.id == id)
.Include (m => m.policies.Select(p => p.subscriber))
.Select(m => new {
m.id,
m.moduleName,
m.policies
}) ;
This only gives the result below. As you can see the details of Subscriber entity under policies are not present (NULL) :( What is wrong?
[{"id":1,"moduleName":"module1",
"policies":[{"id":1,"subscriberId":1,"subscriber":null}]}]
Since you are using dynamics in your Select method, you have to build it all out like this:
return db.modules
.Where(m => m.id == id)
.Include (m => m.policies.Select(p => p.subscriber))
.Select(m => new {
m.id,
m.moduleName,
policies = m.policies.Select(p => new
{
p.id,
p.subscriberId,
subscriber = new
{
p.subscriber.id,
p.subscriber.name,
p.subscriber.subscriptionId,
}
}
});
I typically use real Dto classes so if the Dto ever needs to be updated, refactoring will work properly. I would also consider using a DtoFactory to handle the construction, but you could do it with linq like this:
public class ModuleDto
{
public int id { get; set; }
public string moduleName { get; set; }
public IEnumerable<PolicyDto> policies { get; set; }
}
public class PolicyDto
{
public int id { get; set; }
public int subscriberId { get; set; }
public SubscriberDto subscriber { get; set; }
}
public class SubscriberDto
{
public int id { get; set; }
public string name { get; set; }
public int subscriptionId { get; set; }
}
...other code here...
return db.modules
.Where(m => m.id == id)
.Include (m => m.policies.Select(p => p.subscriber))
.Select(m => new ModuleDto {
m.id,
m.moduleName,
policies = m.policies.Select(p => new PolicyDto
{
p.id,
p.subscriberId,
subscriber = new SubsciberDto
{
p.subscriber.id,
p.subscriber.name,
p.subscriber.subscriptionId,
}
}
});
It gets a little messy to read the linq statement. This is why I typically use a DtoFactory to generate the Dtos from the models.
Im having some problems trying to do this filtering and im sure it can be done better than what im doing. I will show my classes and how im solving it but i was wondering if I could use Linq to filter this. My Classes:
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public virtual List<FeatureType> Features { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<FeatureType> FeatureTypes { get; set; }
public override string ToString()
{
return Name;
}
}
public class FeatureType
{
public int Id { get; set; }
public string Name { get; set; }
public Section Section { get; set; }
public virtual List<ItemType> ItemTypes { set; get; }
}
I'm trying to get all Sections, and filter Features by an ItemTypeID, so only the FeatureTypes of that ItemTypes are listed. What Im doing now its just getting all sections, and just do a for and just add the ones that work for me in other:
public ItemTypeFeatureViewModel(int myItemTypeId, IUnitOfWork myUnitOfWork)
{
ItemTypeId = myItemTypeId;
unitOfWork = myUnitOfWork;
Sections = unitOfWork.SectionRepository.Get(includeProperties: "Features")
.ToList();
foreach (var item in Sections)
{
var x = new List<FeatureType>();
foreach (var feature in item.Features)
{
foreach (var itemType in feature.ItemTypes)
{
if (itemType.Id == ItemTypeId)
{
x.Add(feature);
break;
}
}
}
item.Features = x;
}
}
Can i improve this and avoid all this foreach?
You can't filter out included collection on server side, but you can replace two inner loops with:
item.Features = item.Features
.Where(f => f.ItemTypes.Any(i => i.Id == ItemTypeId))
.ToList();
That will select only those features which have at least one item type with id you provided.
Try the following:
Sections
.ForEach(x => x.Features = x.Features.Where(y => y.Any(z => z.Id == ItemTypeId))
.ToList());
I created this query with NHibernate:
public IList<Category> GetCategoriesByUsername(string username)
{
Category cAlias = null;
User uAlias = null;
Keyword kAlias = null;
var categories = SessionFactory.GetCurrentSession().QueryOver<Category>(() => cAlias)
.JoinAlias(() => cAlias.User, () => uAlias)
.Where(() => uAlias.Username == username)
.Future<Category>();
var keywords = SessionFactory.GetCurrentSession().QueryOver<Keyword>(() => kAlias)
.JoinAlias(c => c.Category, () => cAlias)
.Where(() => cAlias.Id == kAlias.Category.Id)
.Future<Keyword>();
IList<Category> list = (List<Category>)categories.ToList();
return list;
}
This works fine and gives me a list of categories where each category has its own keywords. In my service layer I try to convert it to a ViewModel with Automapper which works but not as expected. For every keyword in the category list it creates a new query (N+1). It doesn't use the already populated keywords in each category in the list.
These are my Models and ViewModels:
public class Category
{
public virtual Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
public virtual User User { get; set; }
}
public class CategoryView
{
public int Id { get; set; }
public string Name { get; set; }
public IList<KeywordSummaryView> Keywords { get; set; }
}
public class Keyword
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Category Category { get; set; }
}
public class KeywordSummaryView
{
public int Id { get; set; }
public string Name { get; set; }
}
My mapping:
public class AutoMapperBootStrapper
{
public static void ConfigureAutoMapper()
{
Mapper.CreateMap<Category, CategoryView>();
Mapper.CreateMap<Keyword, KeywordSummaryView>();
}
}
public static class CategoryMap
{
public static IList<CategoryView> ConvertToCategoryView(this IList<Category> category)
{
return Mapper.Map<IList<Category>, IList<CategoryView>>(category);
}
}
From Model to ViewModel:
IList<Category> categories = _categoryRepository.GetCategoriesByUsername(request.Username);
response.Categories = categories.ConvertToCategoryView();
It doesn't use the already populated keywords in each category in the list but instead creates a new query for each keyword (N+1). Is there something I'm doing wrong?
I guess both of these should prevent N+1 select
public IList<Category> GetCategoriesByUsername(string username)
{
User uAlias = null;
var categories = SessionFactory.GetCurrentSession().QueryOver<Category>(() => cAlias)
.Fetch(x => x.Keywords ).Eager
.JoinAlias(() => cAlias.User, () => uAlias)
.Where(() => uAlias.Username == username);
.TransformUsing(Transformers.DistinctRootEntity)
.List<Category>();
return categories ;
}
public IList<Category> GetCategoriesByUsername(string username)
{
User uAlias = null;
Keyword kAlias = null;
var categories = SessionFactory.GetCurrentSession().QueryOver<Category>(() => cAlias)
.JoinAlias(() => cAlias.User, () => uAlias)
.JoinAlias(x => x.Keywords , () => kAlias, JoinType.LeftOuterJoin)
.Where(() => uAlias.Username == username);
.TransformUsing(Transformers.DistinctRootEntity)
.List<Category>();
return categories;
}
Hope this will help