Distinct() in Map/Transform - indexing

I have the following index I'm creating in order to get all the permissions for a specific user. In the transform, roles.SelectMany(x => x.Permissions) could contain duplicates, so I want to put .Distinct() on it. However, when I do, it seems to get translated to Enumerable.Distinct(roles.SelectMany(x => x.Permissions) inside of Raven, which returns no results. If I change the index directly in Raven to use .Distinct() instead of Enumerable.Distinct(...), it works perfectly.
How can this be written so that it gets translated properly in Raven?
public class PermissionsByUser : AbstractIndexCreationTask<User, UserWithPermissions>
{
public override string IndexName
{
get
{
return "Users/PermissionsByUser";
}
}
public PermissionsByUser()
{
Map = users => from user in users
from role in user.Roles
select new {role.Id};
TransformResults = (database, users) => from user in users
let roles = database.Load<Role>(user.Roles.Select(x => x.Id))
select new
{
Id = user.Id,
Username = user.Username,
Password = user.Password,
Roles = user.Roles,
Permissions = roles.SelectMany(x => x.Permissions)//.Distinct()
};
}
}

This was, I think, actually just a problem of stale results. Answered at https://groups.google.com/forum/?fromgroups#!topic/ravendb/0hO8TOQicwc

Related

replace a returned property with another property from a different model linking by Id

Hi I'm not quite sure how to do this.
I have this .NetCore EntityFrame controller that gets a list of characters.
It works when I simply get the list of characters by doing this:
return await _context.Character.ToListAsync();
This returns the name, class, race, age, armorPoints, hitPoints, and spiritAnimalId for each character in the database.
But now, instead of simply returning just the spiritAnimalId, I want to return the actual name of the animal.
So I started writing some code, but quickly lost the ability to figure out how to return the name of the spirit animal.
Here is what I have:
// GET: api/Characters
[HttpGet]
public async Task<ActionResult<IEnumerable<Character>>> GetCharacters()
{
var characters= await _context.Character.ToListAsync();
foreach(Character c in characters)
{
var a = _context.SpiritAnimal.FindAsync(c.spiritAnimalId);
var name = a.Result.Name;
}
return charcters;
}
So I'm lost as to how to return the list of projects and replace the spiritAnimalId with it's name.
NEW CODE:
// GET: api/Characters
[HttpGet]
public async Task<ActionResult<List<Character>>> GetCharacters()
{
var characters = await _context.Character
.Select(x => new
{
x.Id,
x.Name,
x.Race,
x.Class,
x.Age,
SpiritAnimalName = x.SpiritAnimalName.Name
})
.ToListAsync();
return characters;
}
NEW ERROR:
Cannot implicitly convert type 'System.Collections.Generic.List<<anonymous type
to 'Microsoft.AspNetCore.Mvc.ActionResult<System.Collections.Generic.List
Thanks!
If you don't already, you should have a navigation property on your Character class like:
public SpiritAnimal SpiritAnimal { get; set; }
Then, when you make your query, you should include this relationship, so it's joined in the same query:
var characters = await _context.Character.Include(x => x.SpiritAnimal).ToListAsync();
As you're iterating over the characters, you can simply access: character.SpiritAnimal.Name. If you're wanting to only return the name from the query, you'll need to use Select to project into another class or an anonymous object:
var characters = await _context.Character
.Select(x => new
{
CharacterName = x.Name,
SpiritAnimalName = x.SpiritAnimal.Name
}
.ToListAsync();
When the Select expression utilizes a property on a related entity, you don't need to explicitly load it; EF is smart enough to realize it needs to do a join to satisfy the return.

How to get Role Claims?

I've created a user and attached to him a role that has a number of claims. The problem is I don't see a direct way to access retrieve them using Entity Framework Core and Identity integration. Here's what I'd like to do ideally:
return _context.Users
.Include(u => u.Roles)
.ThenInclude(r => r.Role)
.ThenInclude(r => r.Claims)
But there's not Role property, just RoleId. So I can not Include role claims. Of course I get make a separate query to get claims or even use RoleManager:
var user = _context.Users.Single(x => x.Id == ...);
var role = _roleManager.Roles.Single(x => x.Id == user.Roles.ElementAt(0).RoleId);
var claims = _roleManager.GetClaimsAsync(role).Result;
but it looks inefficient and even ugly. There should be a way to make a single query.
My last hope was Controller.User property (ClaimsIdentity). I hoped it somehow smartly aggregates claims from all the roles. But seems like it doesn't...
You can use SQL-like query expressions and get all claims from all roles of a user like this:
var claims = from ur in _context.UserRoles
where ur.UserId == "user_id"
join r in _context.Roles on ur.RoleId equals r.Id
join rc in _context.RoleClaims on r.Id equals rc.RoleId
select rc;
You can add navigation properties.
public class Role : IdentityRole
{
public virtual ICollection<RoleClaim> RoleClaims { get; set; }
}
public class RoleClaim : IdentityRoleClaim<string>
{
public virtual Role Role { get; set; }
}
Then you have to configure your identity db context:
public class MyIdentityDbContext : IdentityDbContext<User, Role, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, RoleClaim, IdentityUserToken<string>>
Usage:
await _context.Roles.Include(r => r.RoleClaims).ToListAsync();
At the end it generates the following query:
SELECT `r`.`Id`, `r`.`ConcurrencyStamp`, `r`.`Name`, `r`.`NormalizedName`, `r0`.`Id`, `r0`.`ClaimType`, `r0`.`ClaimValue`, `r0`.`RoleId`
FROM `roles` AS `r`
LEFT JOIN `role_claims` AS `r0` ON `r`.`Id` = `r0`.`RoleId`
ORDER BY `r`.`Id`, `r0`.`Id`
Source: Identity model customization in ASP.NET Core
Make sure you are adding the roles and claims correctly. Below is an example of how I create a user and add claims and roles.
private async Task<IdentityResult> CreateNewUser(ApplicationUser user, string password = null){
//_roleManger is of type RoleManager<IdentityRole>
// _userManger is of type UserManager<ApplicationUser>
//and both are injected in to the controller.
if (!await _roleManger.RoleExistsAsync("SomeRole")){
await _roleManger.CreateAsync(new IdentityRole("SomeRole"));
}
var result = password != null ? await _userManager.CreateAsync(user, password) : await _userManager.CreateAsync(user);
if(result.Succeeded) {
await _userManager.AddToRoleAsync(user, "SomeRole");
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Name, user.Email));
}
return result;
}
Then you can use the _userManager to get the claims. This is how I get the current user using _userManager. Then you can just call something like this:
var claims = await _userManager.GetClaimsAsync(user);

RavenDB - Adding Index breaks unrelated queries

When I add the index below to my raven database a simple query like
return Session.Query<R>().FirstOrDefault(x => x.RId == Id);
Always returns null. Only after forcing Raven to remove my custom index does desired functionality return. Why is this?
The Index with side effects:
public class RByLatestCommentIndex : AbstractIndexCreationTask<R>
{
public RByLatestCommentIndex()
{
SetMap();
}
void SetMap()
{
Map = r => r.Select(x => new
{
Id = x.Id,
TimeStamp = x.Comments.Count() > 0 ? x.Comments.Max(u => u.Created)
: x.Created
}).OrderByDescending(y => y.TimeStamp).Select(r => new { Id = r.Id });
}
}
public class RIdTransformer : AbstractTransformerCreationTask<R>
{
public RIdTransformer()
{
TransformResults = ids => ids.Select(x => LoadDocument<R>(x.Id));
}
}
EDIT:
In response to Ayende Rahien's comment:
There's a query in the DB which would otherwise be used (Auto/R/ByRID) but the index used looks like this, puzzling enough:
from doc in docs.Rs select new { Images_Count__ = doc.Images["Count()"], RId = doc.RId }
What explains this behaviour? And, will I have to add a static index to be able to query R by RId ?

Filtering navigation property in eager loading with NHibernate with LINQ

I use this in EF
var users = Context.CreateSet<User>()
.Select(u => new {
User = u,
Salary = u.Salaries.Where(s => !s.Deleted)
})
.AsEnumerable()
.Select(a => a.User);
There is a Linq way to do this in NHibernate and i want it work for navigation property load by lazy load too
NHibernate will not fill the collection with filtered results this way and it is not a good idea because you get a broken model. The best options i see
var users = session.Query<User>()
.Fetch(u => u.Salaries)
.Select(u => new
{
User = u,
ActiveSalaries = u.Salaries.Where(s => !s.Deleted)
});
or
public virtual IEnumerable<Salary> ActiveSalaries
{
get { return Salaries.Where(s => !s.Deleted); }
}
var users = session.Query<User>()
.Fetch(u => u.Salaries)
or
// ctor
ActiveSalaries = Salaries.Where(s => !s.Deleted);
// property
public virtual IEnumerable<Salary> ActiveSalaries { get; private set; }
// mapping almost same as that of Salaries
HasMany(x => x.ActiveSalaries).Table("Salaries").Where("Deleted=0");
var users = session.Query<User>()
.Fetch(u => u.ActiveSalaries);

NHibvernate 3.2 many to many not populating join table

I have two classes, user and role, defined as:
public class User : Entity
{
// other properties ...
public virtual string Username
public virtual ICollection<Role> Roles { get; set; }
}
public class Role : Entity
{
public virtual string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In my mapping code, I have the following:
mapper.Class<User>(map =>
{
map.Bag(x=>x.Roles,
cm=>
{
cm.Table("UserRole");
cm.Cascade(Cascade.All);
cm.Key(k => k.Column("[User]"));
},
em=>
{
em.ManyToMany(mm =>
{
mm.Column("[Role]");
});
});
});
mapper.Class<Role>(map =>
{
map.Bag(x=>x.Users,
cm=>
{
cm.Inverse(true);
cm.Table("UserRole");
cm.Key(k=>k.Column("[Role]"));
},
em =>
{
em.ManyToMany(mm =>
{
mm.Column("[User]");
});
});
});
The mappings generate the expected schema, but the join table is never populated. Adding a new user with a new Role in its collection persists the role and then the user to the appropriate tables, but the join table is left empty. Why?
Edit: I still have not made any progress on this. I'm absolutely sure the mapping is correct, and the correct schema is generated, but the join table simply isn't populated. For test purposes, I'm generating entities using NBuilder like so:
var roles = new Role[]
{
new Role("Admin"),
new Role("Manager"),
new Role("User")
};
var users = Builder<User>.CreateListOfSize(10)
.TheFirst(1)
.Do(x =>
{
x.Roles.Add(roles[0]);
x.Roles.Add(roles[1]);
roles[0].Users.Add(x);
roles[1].Users.Add(x);
})
.All()
.With(x => x.Id = 0)
.And(x => x.Version = 0)
.And(x => x.Username = "test user")
.And(x => x.Password = "test password")
.Do(x =>
{
x.Roles.Add(roles[2]);
roles[2].Users.Add(x);
}
.Build();
foreach (var u in users) session.Save(u);
The User and Role entities are persisted correctly, but the join table remains empty. This means I cannot effective query the roles for a given user later, which nullifies the point.
Make sure you have both classes referencing each other.
I think that code, similar to one below, should work for you:
role.Users.Add(user);
user.Roles.Add(role);
session.Save(user); // NH only saves user and role, so it can get auto-generated identity fields
session.Flush(); // NH now can save into cross-ref table, because it knows required information (Flush is also called inside of Transaction.Commit())
I found a good answer to a question about many-to-many with lot of explanations and quotes from NH documentation. I think it worth to read it.
[EDIT]
In answer to this somewhat similar question there is discussion in which need for explicit transaction to save into cross-table is mentioned.
I also edited code above with adding session.Flush() to reflect my findings.
I ended up downloading the NHibernate source and referencing that directly so I could step through it. It turns out that it had something to do with the fact that my code for generating the test data was not wrapped in an explicit session transaction. Once I added that, it was fine. I'd love to see some kind of explanation on this, as I wasn't able to follow the code very clearly, but I'm at least satisfied that the problem is solved.