EF Core not loading related entities - c#

My original code:
public static User GetUser(int userID)
{
_context.Users.Where(x => x.UserId == userID)
.FirstOrDefault();
}
Here user.usergovernments is null.
New code:
public static User GetUser(int userID)
{
using (var _context = new SafetyContext())
{
// find lga for this user
var user_govs = from o in _context.Users
join i in _context.UserGovernments
on o.UserId equals i.UserId
where o.UserId == userID
select new { o, i };
var user = _context.Users
.Where(x => x.UserId == userID)
.FirstOrDefault();
foreach (var lga in user_govs)
{
user.UserGovernments.Add(new UserGovernment { UserId = userID, UserGovernmentId = lga.i.UserGovernmentId, LocalGovId = lga.i.LocalGovId, StateId = lga.i.StateId });
}
return user;
}
}
This time I get duplicate usergovernment records! One is loaded and the other is the one I added!
Model classes:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public UserAccess AccessLevel { get; set; }
public ICollection<UserGovernment> UserGovernments { get; set; }
}
public class UserGovernment
{
public int UserGovernmentId { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int StateId { get; set; }
public State State { get; set; }
public int LocalGovId { get; set; }
public LocalGov LocalGov { get; set; }
}
public class LocalGov
{
public int LocalGovId { get; set; }
public string Name { get; set; }
public string LgaPid { get; set; }
public ICollection<UserGovernment> UserGovernments { get; set; }
}
Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserGovernment>()
.HasKey(bc => new {bc.UserGovernmentId});
modelBuilder.Entity<UserGovernment>()
.HasOne(bc => bc.User)
.WithMany(b => b.UserGovernments)
.HasForeignKey(bc => bc.UserId);
modelBuilder.Entity<UserGovernment>()
.HasOne(bc => bc.LocalGov)
.WithMany(c => c.UserGovernments)
.HasForeignKey(bc => bc.LocalGovId);
}
What am I doing wrong?
LocalGov and User are individual entities while UserGovernments is the many-to-many joining table/entity

Write Query as like bellow.
Include method fill your UserGovermnet property automaticaly according to it's matched userId
_context.Users.Where(x => x.UserId == userID)
.Include(u=>u.UserGoverments)
.FirstOrDefault();

Related

How to return objects from many to many relation Db - entityframework

Im trying to return a List of products of a specific user by user Id, but that seems to not work
My Product class
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public List<User>? Users { get; set; }
}
My user class
public class User
{
[Key]
public Guid Id { get; set; }
[Required] [MaxLength(15)]
public string Username { get; set; }
[Required]
public string Name { get; set; }
public string Surname { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public List<Product>? Products { get; set; }
}
So im adding and Product to Db, this is working
And then im adding the product to Order by this method
Guid id is a user's id
public void AddProduct(Product product, Guid id)
{
var user = _context.Users.First(u => u.Id == id);
var p = _context.Products.First(p => p.Id == product.Id);
if (user.Products == null || p.Users == null)
{
user.Products = new List<Product>();
p.Users = new List<User>();
}
user.Products.Add(p);
p.Users.Add(user);
_context.SaveChanges();
}
And this also seems to work:
image of ProductUser table from db
So how can I return a List of Products which specific user have?
I've tried this:
private Order BuildOrder(Guid id)
{
var user = _context.Users.First(u => u.Id == id);
/*if (user.Products is null)
{
user.Products = new List<Product>();
}*/
var x = _context.Products.Where(p => p.Id == 1);
/*
var products = user.Products.ToList();*/
var order = new Order
{
Products = x.ToList()
};
return order;
But this is returning me null, like Adding Products is not working
Result of this method
Order class:
public class Order
{
public List<Product> Products { get; set; }
}
DbContext:
using Application.Api.Models;
using Microsoft.EntityFrameworkCore;
namespace Application.Api.Data;
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseSerialColumns();
modelBuilder.Entity<User>(eb =>
{
eb.HasMany(u => u.Products).WithMany(p => p.Users);
});
}
public DbSet<User> Users { get; set; }
public DbSet<Product> Products { get; set; }
}
If that's not enough informations comment what I need to add
you need to explicitly include the Products for the user
var speceficUserWithProducts = context.Users.Include(u => u.Products).FirstOrDefault(u => u.Id == id);

Linq filtering by class array properties

My schema is as
I am currently getting the products with valiants like so
products = context.Products
.Include(x => x.Skus)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Value);
Now i am trying to add a filter functionality by OptionId and ValueId
The following list holds both the OptionId and the ValueId for every option selected at the UI
List<Filter> filters;
where Filter is
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
How could i add filter functionality on this one?
After using
var v = context.Products.Include(x => x.ProductVariants)
.Where(prod => prod.ProductVariants
.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)));
i got the error
The LINQ expression 'DbSet<ProductVariant>()
.Where(p0 => EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id") != null && object.Equals(
objA: (object)EF.Property<Nullable<int>>(EntityShaperExpression:
EntityType: Product
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
, "Id"),
objB: (object)EF.Property<Nullable<int>>(p0, "ProductId")))
.Any(p0 => __filters_0
.Any(f => f.o == p0.OptionId && f.v == p0.ValueId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Update
While using
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.ValueId)).AsEnumerable();
The error is now
The LINQ expression 'DbSet<ProductVariant>()
.Where(p => __filters_0
.Any(f => f.Oid == p.OptionId && f.Vid == p.ValueId))' could not be translated.
Update
The error persists even with only filter by option
var vv = context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId)).AsEnumerable();
Update The classes used are
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
Update
I have narrowed the error to be related with the Filter class
By using
List<int> ints = new List<int>();
ints.Add(1);
and
var vv = context.ProductVariants
.Where(v => ints.Any(f => f == v.OptionId));
it just works. Should i use an expression or something else?
static Expression<Func<...
It'll likely end up looking like:
.Where(prod => prod.Variants.Any(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId)))
If your ProductVariants entity has no Id (not sure how you set up your entities), you might need f.Oid == v.Option.Id && f.Vid == v.Variant.Id
Unless you actually need to select fields out of Sku, Option and Value they could be dropped from the query; realistically only product and product variant are needed for the filtering
Edit:
Looks like the ORM is struggling to turn the linq into SQL. Starting from product variant might help:
context.ProductVariants
.Where(v => filters.Any(f => f.Oid == v.OptionId && f.Vid == v.VariantId))
If that works out, add your product in
I wasn't able to replicate the problem with this code in a .net core winforms app and ef5:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace WFNetCoreCSWithEF5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var f = new List<Filter>() { new Filter { Oid = 1, Vid = 2 } };
var x = new ProductDbContext().ProductVariant.Where(pc => f.Any(f => f.Oid == pc.OptionId && f.Vid == pc.ValueId));
}
}
public class Filter
{
public int Oid { get; set; } //OptionId
public int Vid { get; set; } //ValueId
}
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
//public IEnumerable<Sku> Skus { get; set; }
}
public enum FilterType
{
CheckBox,
Radio,
Button,
List
}
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
public FilterType FilterType { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
//public int ProductId { get; set; }
public decimal Price { get; set; }
//[ForeignKey("ProductId")]
//public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
public class ProductDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariant> ProductVariant { get; set; }
public DbSet<Sku> Skus { get; set; }
public DbSet<Option> Options { get; set; }
public DbSet<Value> Values { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=c:\temp\new3.mdf;Integrated Security=True;Connect Timeout=30");
}
}
And these DOS commands:
dotnet ef migrations add X
dotnet ef database update
To create the db
Okay, ended up with PredicateBuilder
if (filters.Any())
{
var predicate = PredicateBuilder.False<Product>();
foreach (var filter in filters)
predicate = predicate.Or(p => p.ProductVariants.Any(x => x.OptionId == filter.o & x.ValueId == filter.v));
products = products.AsQueryable().Where(predicate);
}
products = products.AsQueryable()
.Include(x => x.ProductVariants)
.ThenInclude(pv => pv.Option)
.Include(x => x.ProductVariants)
.ThenInclude(x => x.Value);

The filter expression cannot be specified for entity type. A filter may only be applied to the root entity type in a hierarchy

I am having trouble with this error when Adding new migration.
The filter expression 'e => Not(e.IsDeleted)' cannot be specified for entity type 'Babysitter'. A filter may only be applied to the root entity type in a hierarchy.
What I am doing is that I have 2 classes Babysitter and Parent that both need to be ApplicationUsers, because they have different properties. So I made them inherit the ApplicationUser class and extended them.
This is the ApplicationUser class.
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
}
// Audit info
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
// Deletable entity
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
}
These are the Babysitter and Parent classes.
public class Babysitter : ApplicationUser
{
public Babysitter()
{
this.Appointments = new HashSet<Appointment>();
this.Comments = new HashSet<Comment>();
}
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
public DateTime DateOfBirth { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public decimal WageRate { get; set; }
public string Address { get; set; }
public decimal Rating { get; set; }
public ICollection<Comment> Comments { get; set; }
public ICollection<Appointment> Appointments { get; set; }
}
public class Parent : ApplicationUser
{
public Parent()
{
this.Comments = new HashSet<Comment>();
this.Kids = new HashSet<Kid>();
this.Appointments = new HashSet<Appointment>();
}
public string Name { get; set; }
public string ImageUrl { get; set; }
public decimal Rating { get; set; }
public string Address { get; set; }
public ICollection<Comment> Comments { get; set; }
public ICollection<Kid> Kids { get; set; }
public ICollection<Appointment> Appointments { get; set; }
}
And so when I try to Add-Migration Initial I get this error: The filter expression 'e => Not(e.IsDeleted)' cannot be specified for entity type 'Babysitter'. A filter may only be applied to the root entity type in a hierarchy.
This is the ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
private static readonly MethodInfo SetIsDeletedQueryFilterMethod =
typeof(ApplicationDbContext).GetMethod(
nameof(SetIsDeletedQueryFilter),
BindingFlags.NonPublic | BindingFlags.Static);
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Babysitter> Babysitters{ get; set; }
public DbSet<Parent> Parents { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Kid> Kids{ get; set; }
public DbSet<Appointment> Appointments { get; set; }
public DbSet<Setting> Settings { get; set; }
public override int SaveChanges() => this.SaveChanges(true);
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
this.ApplyAuditInfoRules();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) =>
this.SaveChangesAsync(true, cancellationToken);
public override Task<int> SaveChangesAsync(
bool acceptAllChangesOnSuccess,
CancellationToken cancellationToken = default)
{
this.ApplyAuditInfoRules();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
protected override void OnModelCreating(ModelBuilder builder)
{
// Needed for Identity models configuration
base.OnModelCreating(builder);
ConfigureUserIdentityRelations(builder);
EntityIndexesConfiguration.Configure(builder);
var entityTypes = builder.Model.GetEntityTypes().ToList();
// Set global query filter for not deleted entities only
var deletableEntityTypes = entityTypes
.Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
foreach (var deletableEntityType in deletableEntityTypes)
{
var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType);
method.Invoke(null, new object[] { builder });
}
// Disable cascade delete
var foreignKeys = entityTypes
.SelectMany(e => e.GetForeignKeys().Where(f => f.DeleteBehavior == DeleteBehavior.Cascade));
foreach (var foreignKey in foreignKeys)
{
foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
}
}
private static void ConfigureUserIdentityRelations(ModelBuilder builder)
{
builder.Entity<ApplicationUser>()
.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Logins)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Roles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
}
private static void SetIsDeletedQueryFilter<T>(ModelBuilder builder)
where T : class, IDeletableEntity
{
builder.Entity<T>().HasQueryFilter(e => !e.IsDeleted);
}
private void ApplyAuditInfoRules()
{
var changedEntries = this.ChangeTracker
.Entries()
.Where(e =>
e.Entity is IAuditInfo &&
(e.State == EntityState.Added || e.State == EntityState.Modified));
foreach (var entry in changedEntries)
{
var entity = (IAuditInfo)entry.Entity;
if (entry.State == EntityState.Added && entity.CreatedOn == default)
{
entity.CreatedOn = DateTime.UtcNow;
}
else
{
entity.ModifiedOn = DateTime.UtcNow;
}
}
}
}
So you're trying to add your filter by convention;
// Set global query filter for not deleted entities only
var deletableEntityTypes = entityTypes
.Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
foreach (var deletableEntityType in deletableEntityTypes)
{
var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType);
method.Invoke(null, new object[] { builder });
}
But that is matching against all three types; Babysitter, Parent, ApplicationUser. The error message is telling you that in a table hierarchy, only apply filters to base types;
.Where(et => et.ClrType != null
&& typeof(IDeletableEntity).IsAssignableFrom(et.ClrType)
&& et.BaseType == null)

.Net core EF, saving records to many-to-many type table

I am new to EF and .Net core and I'm having trouble with many-to-many relationship in my project. I used microsoft documentation to setup the relationship, but i have trouble inserting any data. Project is a kanban board and i am trying to set up relations between users and tasks. Both of them already exist. The goal is to have a table with userId and taskId. Here are my models:
KanbanTask Model:
public class KanbanTask : Entity
{
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Status { get; set; }
public int ProgressStatus { get; set; }
public List<UserTask> UserTask { get; set; }
}
User Model:
public class User : Entity
{
[Required]
public string Name { get; set; }
[Required]
public string Surname { get; set; }
public List<UserTask> UserTask { get; set; }
}
Entity Model:
public class Entity
{
public int Id { get; set; }
}
UserTaskModel:
public class UserTask
{
public int UserId { get; set; }
public User User { get; set; }
public int KanbanTaskId { get; set; }
public KanbanTask KanbanTask { get; set; }
}
My DbContex:
public DbSet<KanbanTask> KanbanTasks { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserTask>()
.HasKey(t => new { t.UserId, t.KanbanTaskId });
modelBuilder.Entity<UserTask>()
.HasOne(pt => pt.User)
.WithMany(p => p.UserTask)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserTask>()
.HasOne(pt => pt.KanbanTask)
.WithMany(t => t.UserTask)
.HasForeignKey(pt => pt.KanbanTaskId);
}
}
My function in service:
public async Task<ResultDTO> AssignTaskToUser(int taskId, int userId)
{
var result = new ResultDTO()
{
Response = null
};
try
{
var user = await _repo.GetSingleEntity(x => x.Id == userId);
var kanbanTask = await _taskrepo.GetSingleEntity(y => y.Id == taskId);
if (user != null && kanbanTask != null)
{
var usertask = new UserTask()
{
KanbanTaskId = taskId,
UserId = userId
};
kanbanTask.UserTask.Add(usertask);
user.UserTask.Add(usertask);
await _repo.Patch(user);
}
else
result.Response = "Task or user not found";
}
catch (Exception e)
{
result.Response = e.Message;
return result;
}
return result;
}
My repository:
public async Task Patch(T entity)
{
_dbSet.Update(entity);
await _context.SaveChangesAsync();
}
Like this
var usertask = new UserTask()
{
KanbanTaskId = taskId,
UserId = userId
};
db.UserTasks.Add(usertask);
db.SaveChanges();
What you need to is to make sure that your middle entity (UserTask) always saves the Keys of both entities so I strongly suggest to add that logic in UserTask constructor.
public class UserTask
{
public int UserId { get; set; }
public User User { get; set; }
public int KanbanTaskId { get; set; }
public KanbanTask KanbanTask { get; set; }
public UserTask() { }
public UserTask(User user, KanbanTask kanbanTask)
{
KanbanTask = kanbanTask;
KanbanTaskId = kanbanTask.Id;
User = user;
UserId = userId;
}
}
//
var usertask = new UserTask(user, kanbanTask);
kanbanTask.UserTask.Add(usertask);
user.UserTask.Add(usertask);
await _repo.Patch(user);
//
I have wrote an example for this common problem. Here https://github.com/salsita18/ManyToManyNetCore you can check the approach I took, using a single generic MiddleEntity class.
I also added it to nuget in order to reuse it, but you can just make your own implementation following the pattern

Why does Automapper and NHibernate result in N+1?

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

Categories

Resources