How to get List of Parent Entities with Child Count In Nhibernate QueryOver - nhibernate

I have two classes :
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Child> Childrens { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}
Now through Nhibernate QueryOver I want to get list of all Parent with no of Count of children in single query.
Expected output is ?:
ParentId Name ChildrenCount
1 ABC 10
2 CDE 5
can anyone help me .

Using this DTO for projection:
public class ParentDto
{
public int Id { get; set; }
public string Name { get; set; }
public int ChildrenCount { get; set; }
}
Use this query:
Child childAlias = null;
ParentDto dto = null;
var dtoParents = Session.QueryOver<Parent>()
.JoinAlias(x => x.Childrens, () => childAlias)
.SelectList(list => list
.SelectGroup(x => x.Id).WithAlias(() => dto.Id)
.SelectGroup(x => x.Name).WithAlias(() => dto.Name)
.SelectCount(() => childAlias.Id).WithAlias(() => dto.ChildrenCount))
.TransformUsing(Transformers.AliasToBean<ParentDto>())
.List<ParentDto>();
You can read more about QueryOver projections using DTOs here.

Related

EF Core - Migrate from Table Per Type to Table Per Hierarchy

I'm trying to migrate from using TPT (a table per subclass) to TPH (One table for all subclasses).
This is my starting point for TPT:
entities:
[Serializable]
public abstract class VeganItem<TVeganItemEstablishment> : DomainEntity<int>
{
[Required]
public string Name { get; set; }
[Required]
public string CompanyName { get; set; }
[Required]
public string Description { get; set; }
public string Image { get; set; }
[Required]
public int IsNotVeganCount { get; set; } = 0;
[Required]
public int IsVeganCount { get; set; } = 0;
[Required]
public int RatingsCount { get; set; } = 0;
[Required]
public int Rating { get; set; }
[Required]
public List<Option> Tags { get; set; }
[PropertyName("veganItemEstablishments", Ignore = true)]
public virtual ICollection<TVeganItemEstablishment> VeganItemEstablishments { get; set; }
}
[ElasticsearchType(RelationName = "groceryitem", IdProperty = "Id")]
public class GroceryItem : VeganItem<GroceryItemEstablishment>
{
}
[ElasticsearchType(RelationName = "menuitem", IdProperty = "Id")]
public class MenuItem : VeganItem<MenuItemEstablishment>
{
}
OnModelCreating:
modelBuilder.Entity<GroceryItem>(gi =>
{
gi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
gi.Property(u => u.CreatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
gi.Property(u => u.UpdatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
gi.HasKey(e => e.Id);
gi.HasOne(q => q.UpdatedBy)
.WithMany()
.HasForeignKey(k => k.UpdatedById);
gi.HasOne(q => q.CreatedBy)
.WithMany()
.HasForeignKey(k => k.CreatedById);
gi.Property(e => e.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<Option>>(v, null),
new ValueComparer<IList<Option>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (IList<Option>)c.ToList()));
});
modelBuilder.Entity<MenuItem>(mi =>
{
mi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
mi.Property(u => u.CreatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
mi.Property(u => u.UpdatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
mi.HasKey(e => e.Id);
mi.HasOne(q => q.UpdatedBy)
.WithMany()
.HasForeignKey(k => k.UpdatedById);
mi.HasOne(q => q.CreatedBy)
.WithMany()
.HasForeignKey(k => k.CreatedById);
mi.Property(e => e.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<Option>>(v, null),
new ValueComparer<IList<Option>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (IList<Option>)c.ToList()));
});
public DbSet<GroceryItem> GroceryItems { get; set; }
public DbSet<MenuItem> MenuItems { get; set; }
So I want just one table called VeganItems. What is it that is actually causing there to be 2 tables - GroceryItems and MenuItems? As I have tried a couple of things and they didn't work. EF Core uses TPH by default so I'm unsure why it is using TPT. I'm wondering if it is because my base entity is a generic type.
EF Core uses TPH by default so I'm unsure why it is using TPT. I'm wondering if it is because my base entity is a generic type.
Generic type is one of the problems. The other is that it is not a base entity, but just base class. In order to be considered entity, there must be either DbSet<T>, or ModelBuilder.Entity<T>() call, or applied IEntityTypeConfiguration<T>, or some discorevered entity navigation property (either collection or reference) referring to it - see Including types in the model.
You don't have any of these, so the model is not even TPT (common table containing common properties + single table per each derived entity containing specific properties), but some sort of a TPC (Table-Per-Class, not currently supported by EF Core), where there is no common table - all the data is in concrete tables for each derived entity.
So, in order to use TPT you need to fix both issues. Generic class cannot be used as entity type because its type is not enough to identify it (each generic instantiation is different type, typeof(Foo<Bar>) != typeof(Foo<Baz>)).
Start by extracting the non generic part which will serve as base entity (removed non EF Core annotations for clarity):
// Base class (code/data reuse only, not an entity)
public abstract class DomainEntity<TId>
{
public TId Id { get; set; }
}
// Base entity
public abstract class VeganItem : DomainEntity<int>
{
[Required]
public string Name { get; set; }
[Required]
public string CompanyName { get; set; }
[Required]
public string Description { get; set; }
public string Image { get; set; }
[Required]
public int IsNotVeganCount { get; set; } = 0;
[Required]
public int IsVeganCount { get; set; } = 0;
[Required]
public int RatingsCount { get; set; } = 0;
[Required]
public int Rating { get; set; }
[Required]
public List<Option> Tags { get; set; }
}
// Base class (code/data reuse only, not an entity)
public abstract class VeganItem<TVeganItemEstablishment> : VeganItem
{
public virtual ICollection<TVeganItemEstablishment> VeganItemEstablishments { get; set; }
}
// Derived entity
public class GroceryItem : VeganItem<GroceryItemEstablishment>
{
}
// Derived entity
public class MenuItem : VeganItem<MenuItemEstablishment>
{
}
Then (optionally) add DbSet for it
public DbSet<VeganItem> VeganItems { get; set; }
Finally (mandatory) move the fluent configuration of the base entity members to its own block, and keep in derived only the configuration of the specific members of the derive type:
// Configure base entity
modelBuilder.Entity<VeganItem>(vi =>
{
vi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
vi.Property(u => u.CreatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
vi.Property(u => u.UpdatedDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
vi.HasKey(e => e.Id);
vi.HasOne(q => q.UpdatedBy)
.WithMany()
.HasForeignKey(k => k.UpdatedById);
vi.HasOne(q => q.CreatedBy)
.WithMany()
.HasForeignKey(k => k.CreatedById);
vi.Property(e => e.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<Option>>(v, null),
new ValueComparer<IList<Option>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (IList<Option>)c.ToList()));
});
// Configure derived entities
modelBuilder.Entity<GroceryItem>(gi =>
{
});
modelBuilder.Entity<MenuItem>(mi =>
{
});

Save list of object into table using automapper asp.net core

This is my DTO
public class Part1
{
public Part1()
{
Value = new List<ValueList>();
}
public int Id{ get; set; }
public List<ValueList> Value { get; set; }
}
public class ValueList
{
public int Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
and this is my table structure and I want to save above dto into below dto using automapper
public class Part1
{
public int Id{ get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
Do you want to know how to make the list map to singel string in the new model?
You can check my demo, in mapper profile, define the three values to map:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Part1, Part1DTO>() //Part1DTO is your below model
.ForMember(x => x.Value1, m => m.MapFrom(src => src.Value[0].Value1))
.ForMember(x => x.Value2, m => m.MapFrom(src => src.Value[0].Value2))
.ForMember(x => x.Value3, m => m.MapFrom(src => src.Value[0].Value3));
}
}
Controller:
public IActionResult Index()
{
Part1 part1= new Part1()
{
Id=1,
Value=new List<ValueList> { new ValueList { Value1=1,Value2="b",Value3="c"} }
}; //define old model
var result = _mapper.Map<Part1DTO>(part1); // map to new
return View(result);
}
Don't forget to add this in ConfigureServices:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Result:

ef core may to many update with extra filed

My mode class:
public class AccountCustomer
{
public bool IsMain { get; set; }
public int AccountId { get; set; }
public Account Account { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Account
{
public int Id { get; set; }
public strig No{ get; set; }
..other fields
public ICollection<AccountCustomer> AccCustomer { get; set; } = new List<AccountCustomer>();
}
public class Customer
{
public int Id { get; set; }
public strig Name{ get; set; }
..other fields
public ICollection<AccountCustomer> AccCustomer { get; set; } = new List<AccountCustomer>();
}
I found soluton here and i have implemented same later i found that it is for without extra column
Many to many ef core updaate
Please let meknow how to do update... Am using latest ef 5 preview
My code :
_context.Set<AccountCustomer>().UpdateLinks(ac => ac.AccountId, account.Id,
ac => ac.CustomerId, account.AccCustomer .Select(ac => ac.CustomerId));
Codes of ManyToMany Update Extensions
Remove the unselected item and add new item to list.
public static class Extensions
{
public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
db.Set<T>().RemoveRange(currentItems.ExceptThat(newItems, getKey));
db.Set<T>().AddRange(newItems.ExceptThat(currentItems, getKey));
}
private static IEnumerable<T> ExceptThat<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
return items
.GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
.SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
.Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
.Select(t => t.t.item);
}
}
Codes of Update ViewModel
The update view pass it to action via request.
public class AccountCustomerVM
{
public bool IsMain { get; set; }
public Account Account { get; set; }
public List<int> Customers { get; set; }
}
Codes of Update Action
[HttpPut]
public IActionResult Update(AccountCustomerVM accountCustomerVM)
{
var model = _context.Accounts.Include(x => x.AccCustomer).FirstOrDefault(x => x.Id == accountCustomerVM.Account.Id);
_context.TryUpdateManyToMany(model.AccCustomer, accountCustomerVM.Customers
.Select(x => new AccountCustomer
{
IsMain = accountCustomerVM.IsMain,
CustomerId = x,
AccountId = accountCustomerVM.Account.Id
}), x => x.CustomerId);
_context.SaveChanges();
return Ok();
}

Self Referencing Many-to-Many relations

I have an Ticket entity:
public class Ticket
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Relation> RelatedTickets { get; set; }
}
I want to setup many-to-many self-relations in Entity Framework Core, so i made two one-to-many relations:
public class Relation
{
[Required, ForeignKey("TicketFrom")]
public int FromId { get; set; }
[Required, ForeignKey("TicketTo")]
public int ToId { get; set; }
public virtual Ticket TicketFrom { get; set; }
public virtual Ticket TicketTo { get; set; }
}
I've tried to create the relationship using fluent API:
builder.Entity<Relation>()
.HasKey(uc => new { uc.FromId, uc.ToId });
builder.Entity<Relation>()
.HasOne(c => c.TicketFrom)
.WithMany(p => p.RelatedTickets)
.HasForeignKey(pc => pc.FromId);
builder.Entity<Relation>()
.HasOne(c => c.TicketTo)
.WithMany(p => p.RelatedTickets)
.HasForeignKey(pc => pc.ToId);
But in result i have an error:
Cannot create a relationship between 'Ticket.RelatedTickets' and
'Relation.TicketTo', because there already is a relationship between
'Ticket.RelatedTickets' and 'Relation.TicketForm'. Navigation
properties can only participate in a single relationship.
The possible solution is to add Parent relation directly to TicketEntity:
public class Ticket
{
public int Id { get; set; }
[Required, ForeignKey("ParentRelation")]
public Nullable<int> ParentRelationId { get; set; }
public virtual Ticket ParentRelation {get;set;}
public virtual ICollection<Ticket> RelatedTickets { get; set; }
...
}
With fluent api like this:
modelBuilder.Entity<Ticket> =>
{
entity
.HasMany(e => e.RelatedTickets)
.WithOne(e => e.ParentRelation)
.HasForeignKey(e => e.ParentRelationId );
});
But it looks 'dirty' to store parent relation like this.
What is the right approach?
It's not possible to have just one collection with relations. You need two - one with relations the ticket equals TicketFrom and second with relations the ticket equals TicketTo.
Something like this:
Model:
public class Ticket
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Relation> RelatedTo { get; set; }
public virtual ICollection<Relation> RelatedFrom { get; set; }
}
public class Relation
{
public int FromId { get; set; }
public int ToId { get; set; }
public virtual Ticket TicketFrom { get; set; }
public virtual Ticket TicketTo { get; set; }
}
Configuration:
modelBuilder.Entity<Relation>()
.HasKey(e => new { e.FromId, e.ToId });
modelBuilder.Entity<Relation>()
.HasOne(e => e.TicketFrom)
.WithMany(e => e.RelatedTo)
.HasForeignKey(e => e.FromId);
modelBuilder.Entity<Relation>()
.HasOne(e => e.TicketTo)
.WithMany(e => e.RelatedFrom)
.HasForeignKey(e => e.ToId);
Note that a solution using Parent is not equivalent, because it would create one-to-many association, while if I understand correctly you are seeking for many-to-many.
Here is very good explanation how to make many-to-many relationship in EF Core
Many-to-many self referencing relationship
Every collection or reference navigation property can only be a part of a single relationship. While many to many relationship with explicit join entity is implemented with two one to many relationships. The join entity contains two reference navigation properties, but the main entity has only single collection navigation property, which has to be associated with one of them, but not with both.
builder.Entity<Relation>()
.HasKey(uc => new { uc.FromId, uc.ToId });
builder.Entity<Relation>()
.HasOne(c => c.TicketFrom)
.WithMany() // <-- one of this must be empty
.HasForeignKey(pc => pc.FromId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Relation>()
.HasOne(c => c.TicketTo)
.WithMany(p => p.RelatedTickets)
.HasForeignKey(pc => pc.ToId);
Just make sure that WithMany exactly matches the presence/absence of the corresponding navigation property.
Note that you have to turn the delete cascade off.
#IvanStoev is correct. This is an example of a more general self referencing many to many relationship with many parents and many children.
public class Ticket
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public List<TicketTicket> TicketChildren { get; set; }
public List<TicketTicket> TicketParents { get; set; }
}
public class TicketTicket
{
public int TicketChildId { get; set; }
public Ticket TicketChild { get; set; }
public int TicketParentId { get; set; }
public Ticket TicketParent { get; set; }
}
modelBuilder.Entity<TicketTicket>()
.HasKey(tt => new {tt.TicketChildId, tt.TicketParentId});
modelBuilder.Entity<Ticket>()
.HasMany(t => t.TicketChildren)
.WithOne(tt => tt.ProductParent)
.HasForeignKey(f => tt.ProductParentId);
modelBuilder.Entity<Ticket>()
.HasMany(t => t.TicketParents)
.WithOne(tt => tt.TicketChild)
.HasForeignKey(tt => tt.TicketChildId);

nhibernate child collection limitation

I have these classes:
public class Document
{
public Document()
{
Descriptions = new List<Descriptions>();
}
public virtual int Id { get; set; }
public virtual IList<DocumentDescription> Descriptions { get; set; }
}
public class DocumentDescription
{
public virtual int DocumentId { get; set; }
public virtual int LanguageId { get; set; }
}
and mappings:
public DocumentMap()
{
Id(x => x.Id);
HasMany(x => x.Descriptions).KeyColumn("DocumentId");
}
public DocumentDescriptionMap()
{
CompositeId()
.KeyProperty(x => x.DocumentId)
.KeyProperty(x => x.LanguageId);
}
my query is:
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds)).List();
I need a query over solution to restrict DocumentDescriptions by few languages, which I will get run-time. I don't want to get all DocumentDescriptions for one Document (only few). Is it possible to set filter/limitation for a child collection?
So, I find out how to add additional join clause to my query:
DocumentDescription dd = null;
ICriterion criterion = Restrictions.On<DocumentDescription>(x => x.LanguageId).IsIn(languageIds.ToArray());
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds));
query.Left.JoinQueryOver(x => x.Descriptions, () => dd, criterion);
SQL:
SELECT * FROM tDocument
LEFT OUTER JOIN tDocumentDescription ON tDocumentDescription.DocumentId = tDocument.Id AND tDocumentDescription.LanguageId IN (#languageIds)
WHERE tDocument.Id IN (#documentIds)