EF Core Accessing Nested-nested Entity - database

I have an entity CallTrackerLog which has many Clients which have a one-many Advices. I am trying to HttpPost a create for the advice:
[HttpPost("{callTrackerId}/{clientId}/advice")]
public IActionResult CreateCTClientAdvice(int callTrackerId, int clientId,
[FromBody] CallTrackerClientAdvice newAdvice)
{
if (newAdvice == null)
return BadRequest();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var ctFromStore = _context.CallTrackers
.Include(log => log.CallTrackerClients)
.ThenInclude(log => log.CallTrackerClientAdvice)
.FirstOrDefault(ct => ct.CallTrackerId == callTrackerId);
var ctAdviceFromStore ctFromStore.CallTrackerClients.CallTrackerClientAdvice
.FirstOrDefault(c => c.CallTrackerClientId == clientId);
// ... add to db
return Ok();
}
The problem is that I cannot access the CallTrackerClientAdvice with the .FirstOrDefault(ct => ct.CallTrackerClientId == clientId) - it gives me a red underline even though I thought I loaded it above.
The error:
How come I am unable to access the CallTrackerClientAdvice?

I suspect that what you want is:
var ctAdviceFromStore = ctFromStore.CallTrackerClients
.FirstOrDefault(c => c.CallTrackerClientId == clientId)?.CallTrackerClientAdvice;

Related

Count of users in AAD group

Is there a Microsoft Graph API to find out the number of users in an AAD group? Currently, here is my code on how I find it out. Curious to know if there is a quicker way?
private async Task<int> GetUserIds(string groupId)
{
List<string> userIds = new List<string>();
var usersFromGroup = await _groupMembersService.GetGroupMembersPageByIdAsync(groupId);
usersFromGroup.AdditionalData.TryGetValue("#odata.nextLink", out object nextLink);
var nextPageUrl = (nextLink == null) ? string.Empty : nextLink.ToString();
userIds.AddRange(usersFromGroup.OfType<Microsoft.Graph.User>().Select(x => x.Id));
while (!string.IsNullOrEmpty(nextPageUrl))
{
usersFromGroup = await _groupMembersService.GetGroupMembersNextPageAsnyc(usersFromGroup, nextPageUrl);
usersFromGroup.AdditionalData.TryGetValue("#odata.nextLink", out object nextLink2);
nextPageUrl = (nextLink2 == null) ? string.Empty : nextLink2.ToString();
userIds.AddRange(usersFromGroup.OfType<Microsoft.Graph.User>().Select(x => x.Id));
}
return userIds.Count;
}
}
public async Task<IGroupTransitiveMembersCollectionWithReferencesPage>GetGroupMembersPageByIdAsync(string groupId)
{
return await this.graphServiceClient
.Groups[groupId]
.TransitiveMembers
.Request()
.Top(this.MaxResultCount)
.WithMaxRetry(this.MaxRetry)
.GetAsync();
}
public async Task<IGroupTransitiveMembersCollectionWithReferencesPage> GetGroupMembersNextPageAsnyc(
IGroupTransitiveMembersCollectionWithReferencesPage groupMembersRef,
string nextPageUrl)
{
groupMembersRef.InitializeNextPageRequest(this.graphServiceClient, nextPageUrl);
return await groupMembersRef
.NextPageRequest
.GetAsync();
}
You can use this graph API to get the count for any Group.
https://graph.microsoft.com/v1.0/groups/{group-object-id}/members/$count
Make sure to add the ConsistencyLevel = Eventual in request headers for this.
Tested this in Graph Explorer for you :

How can I get an item by a string key using web api?

I can't figure out how to hit an endpoint like "api/GetItems/AB123" (AB123 of course being a string) and have it return that item from my data set. I read the docs on the FindAsync() method and it seemed to indicate that it would accept a string by default. Is there something I need to do to 'id' before passing it into FindAsync()? My DB does not have a primary key, if that matters. (I can't change that either, this is legacy data and I have no control over schema)
My db doesn't have a PK ID field. I need to do the next best thing and target a unique string field.
My GET method:
// GET: api/Items/5
[HttpGet("{id}")]
public async Task<ActionResult<Item>> GetItem(string id)
{
var item = await _context.Items.FindAsync(id); // Error happens here: "InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Int64'."
if (item == null)
{
return NotFound();
}
return item;
}
Relevant from my model:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string ItemId { get; set; }
Hello and welcome to the Stack Overflow Community!!
Instead of find you could do .SingleAsync() like the below.
You could also do a .Where(x => x.ItemId == id).SingleAsync(). But this is up to you.
// GET: api/Items/5
[HttpGet("{id}")]
public async Task<ActionResult<Item>> GetItem(string id)
{
var item = await _context.Items.SingleAsync(x => x.ItemId == id);
if (item == null)
{
return NotFound();
}
return item;
}
From your error it is obvious that int is expected in FindById method. Can you check the field type in database but from your model I would say that you don't have correct type.
String can't be used as Identity in this way because SQL Server doesn't know how to generate value for that.
You can check this post for more details on that: DatabaseGeneratedOption.Identity not generating an Id
So to conclude, you should check what do you really have in the database to determine is your model wrong.
If that is not the case and you do have a string in the db you should just retrieve item by using SingleAsync method (note that it will throw exception if the id is wrong).
var item = await _context.Items.SingleAsync(e => e.ItemId == id);
If you don't want an exception if the id doesn't exist you can use:
var item = await _context.Items.SingleOrDefaultAsync(e => e.ItemId == id);
which will return null for non existent id.

How to implement user profile with token auth

I'm using out-of-the-box auth with Individual User Accounts that comes with the Visual Studio template for Web Api. I consume the api in an Angular.js front end.
What is the 'canonical' way of providing user profile to the front end?
Are getting the token and getting user profile (email, first and last name, roles) separate activities or should /Token provide the token and at least the roles and maybe first and last name so the UI can display it?
I'm looking for a general guidance about architecture/flow for apps using a token for auth as well as ASP.Net Web Api + Angular.js specific info.
For the record this is how I implemented it.
TL;DR
I decided to use claims, because 'GivenName', 'Surname' already exists which suggests that it's an OK place to store this info.
I found it very awkward to edit claims.
Details
Here's my Add/UpdateUser method. I hate the way claims are handled, but I couldn't find a better way.
[HttpPost]
[Authorize(Roles = "admin")]
public async Task<IHttpActionResult> Post(AccountModelDTO model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var transaction = Request.GetOwinContext().Get<ApplicationDbContext>().Database.BeginTransaction())
{
ApplicationUser user;
if( string.IsNullOrEmpty(model.Id) )
{//Add user
user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult resultAdd = await UserManager.CreateAsync(user); //Note, that CreateAsync this sets user.Id
if (!resultAdd.Succeeded)
{
return GetErrorResult(resultAdd);
}
} else
{//Update user
user = await UserManager.FindByIdAsync(model.Id);
if( user == null )
{
throw new HttpResponseException(Request.CreateResponse(System.Net.HttpStatusCode.BadRequest, "Unknown id"));
}
user.UserName = model.Email;
user.Email = model.Email;
//Remove existing claims
var claims = user.Claims.Where(c=>c.ClaimType == ClaimTypes.GivenName).ToList();
foreach( var claim in claims)
{
await UserManager.RemoveClaimAsync(user.Id, new Claim(ClaimTypes.GivenName, claim.ClaimValue));
}
claims = user.Claims.Where(c => c.ClaimType == ClaimTypes.Surname).ToList();
foreach (var claim in claims)
{
await UserManager.RemoveClaimAsync(user.Id, new Claim(ClaimTypes.Surname, claim.ClaimValue));
}
claims = user.Claims.Where(c => c.ClaimType == ClaimTypes.Role).ToList();
foreach (var claim in claims)
{
await UserManager.RemoveClaimAsync(user.Id, new Claim(ClaimTypes.Role, claim.ClaimValue));
}
}
var result = await UserManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.GivenName, model.FirstName));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
await UserManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.Surname, model.LastName));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
foreach (var role in model.Roles)
{
result = await UserManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.Role, role));
}
if (!result.Succeeded)
{
return GetErrorResult(result);
}
transaction.Commit();
return Ok();
}
}

MVC - how to retrieve multiple records in a controller

I have the following action:
public ActionResult Details(string id)
{
MyRecordContext rc = new MyRecordContext();
MyRecord r = rc.MyRecords.Single(x => x.RecordID == _id);
return View(r);
}
But turns out there are multiple records with the same id (table's primary key is a composite key). So I need to retrieve a List of type MyRecord, so I changed the code to:
public ActionResult Details(string id)
{
MyRecordContext rc = new MyRecordContext();
List<MyRecord> rl = rc.MyRecords.Any(x => x.RecordID == id);
return View(rl);
}
But the above is clearly incorrect since method Any returns bool. Can someone help me correct my code?
public ActionResult Details(String id)
{
MyRecordContext rc = new MyRecordContext();
List<MyRecord> rl = rc.MyRecords.Where(x => x.RecordID == id).ToList();
return View(rl);
}
That will return all matches with RecordID == id, then pass this list off to your view. Just make sure you update the Details view as well to accept List<MyRecord> instead of MyRecord (now that you're passing a collection).
In Linq, Any just returns a true/false is any of the values match. You are looking for a simple Where:
List<MyRecord> rl = rc.MyRecords.Where(x => x.RecordID == id);

how to do Rx Observable on Silverlight Webclient

I would like to use Rx in my SL app. I want to set up an observable on my REST requests to my webserver. I dont see how to wire up Observable.FromEvent or Observable.FromAsync. My best guess is to make Webclient completion fire an event and then do Observable.FromEvent. IS there a better way?
Here you go, this is the best way to make a web request in Rx.
public IObservable<WebResponse> MakeWebRequest(
Uri uri,
Dictionary<string, string> headers = null,
string content = null,
int retries = 3,
TimeSpan? timeout = null)
{
var request = Observable.Defer(() =>
{
var hwr = WebRequest.Create(uri);
if (headers != null)
{
headers.ForEach(x => hwr.Headers[x.Key] = x.Value);
}
if (content == null)
{
return Observable.FromAsyncPattern<WebResponse>(hwr.BeginGetResponse, hwr.EndGetResponse)();
}
var buf = Encoding.UTF8.GetBytes(content);
return Observable.FromAsyncPattern<Stream>(hwr.BeginGetRequestStream, hwr.EndGetRequestStream)()
.SelectMany(x => Observable.FromAsyncPattern<byte[], int, int>(x.BeginWrite, x.EndWrite)(buf, 0, buf.Length))
.SelectMany(_ => Observable.FromAsyncPattern<WebResponse>(hwr.BeginGetResponse, hwr.EndGetResponse)());
});
return request.Timeout(timeout ?? TimeSpan.FromSeconds(15)).Retry(retries);
}
Here's how to use it:
MakeWebRequest(new Uri("http://www.google.com"))
.Subscribe(
x => Console.WriteLine("Response is {0}", x),
ex => Console.WriteLine("Someone Set Us Up The Bomb: {0}", ex.Message));

Resources