How can I do a count in Linq with many to many - linq

So I have
Business
Jobs (many per business)
JobApplications (many per job)
I just want to count the total number of Applications for a business.
But I dont seem to be able to go
Business.Jobs.JobApplications
if I use
Business.Jobs.SelectMany(l => l.JobApplications
is accessible from there but I dont understand how to connect and count.

How about:
Business.Jobs.SelectMany(c => c.JobApplications).Count();

You will have to replace a bit the names.
bussiness
.Select
(b => new
{
id = b.id,
applications = b.jobs
.Select (j => j.jobapplications.Count ()).Sum ()
}
)
The above has worked for the following structure:
void Main()
{
var bussiness = new List<Bussiness>()
{
new Bussiness
{
id = 1,
jobs = new List<Jobs>()
{
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications()
}
},
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications()
}
},
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications()
}
}
}
},
new Bussiness
{
id = 2,
jobs = new List<Jobs>()
{
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications()
}
},
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications()
}
},
new Jobs
{
jobapplications = new List<JobApplications>()
{
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications(),
new JobApplications()
}
}
}
}
};
//bussiness.Dump();
bussiness.Select (b => new {id = b.id, applications = b.jobs.Select (j => j.jobapplications.Count ()).Sum ()})
.Dump();
}
public class Bussiness
{
public int id {get;set;}
public IEnumerable<Jobs> jobs {get;set;}
}
public class Jobs
{
public IEnumerable<JobApplications> jobapplications {get;set;}
}
public class JobApplications
{
}
You would need to apply it on your structure.

Related

Retrieve Custom ClaimsIdentity object from Principle's Identities

I have wired up my Okta-OIDC-SSO and verified I'm able to log in properly. I have set up my claims and also was able to get a thumbnail of the user through Active Directory. Now, I wanted to have access to my actual business user model on my website whenever needed. I tried to smuggle it as a claim but claims are the only types of string and not complex objects. So I created my own Identity:
Custom ClaimsIdentity:
public class SystemUserIdentity : ClaimsIdentity
{
private SystemUserModel CurrentUser { get; set; }
public SystemUserIdentity(SystemUserModel systemUser, params Claim[] claims) : base(claims) => CurrentUser = systemUser;
public SystemUserIdentity(SystemUserModel systemUser, IEnumerable<Claim> claims) : this(systemUser,claims.ToArray()) { }
}
Auth setup in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
...
RegisterAuth(services);
}
private void RegisterAuth(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option =>
{
option.AccessDeniedPath = "/AccessDenied";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ResponseType = OpenIdConnectResponseType.Code;
options.ClientId = Configuration["SSO:ClientId"];
options.ClientSecret = Configuration["SSO:ClientSecret"];
options.Authority = Configuration["SSO:Issuer"];
options.GetClaimsFromUserInfoEndpoint = true;
options.CallbackPath = "/authorization-code/callback";
options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true };
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async ctx =>
{
var service = ctx.HttpContext.RequestServices.GetRequiredService<IUserService>();
var ssoId = ctx.Principal!.FindFirstValue("preferred_username");
var staff = await service.GetUserByIdAsync(ssoId ?? string.Empty);
if (staff != null)
{
staff.Thumbnail = GetUserThumbFromActiveDirectory(WebHost.IsDevelopment() ? "DAPreview.ae": "dca.com", "UserPrincipalName", ssoId, "thumbnailPhoto");
var claims = new List<Claim>
{
new(nameof(staff.Id), staff.Id),
new(nameof(staff.Username), staff.Username),
new(ClaimTypes.Name, "DACRLClaim"),
new(ClaimTypes.Email, staff.Email),
new(ClaimTypes.GivenName, staff.EmployeeName)
};
claims.AddRange(staff.Roles.Select(role => new Claim(ClaimTypes.Role, $"{role.Id}-{role.Name}")));
var appIdentity = new SystemUserIdentity(staff, claims);
ctx.Principal?.AddIdentity(appIdentity);
}
}
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("SuperAdminPolicy", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c => c.Type == "Role" && c.Value.Contains("Super Admin"))));
});
}
Usage in the target MVC view:
#inject IHttpContextAccessor HttpContextAccess
#{
var currentUser = HttpContextAccess.HttpContext?.User;
var temp = currentUser?.Identities.SingleOrDefault(w => w.Name == "DACRLClaim");
var reqModel = (SystemUserIdentity)temp;
}
So, basically, I need the custom ClaimsIdentity (SystemUserIdentity) that I added in Startup as it has my actual user object and everything.

asp.net core3 error: not all code paths return a value

I have two tables with similar data for body insurance and third party car insurance ... I have used enum in the model to separate the insurances and I want to do the creation operation for it .... There are two modes for each insurance. One case when that car does not have insurance yet and the second case when we want to extend it.
I wrote this code to create the form, but it encounters the following error
I also get an error on the name of the Create function.error = not all code paths return a value.
Please advise
public async Task<IActionResult> Create(int id, int type)
{
InsuranceViewModel model;
ViewBag.Type = type;
var companies = await _context.InsuranceCompany
.Where(e => e.IsActice)
.ToListAsync();
ViewData["CompanyList"] = new SelectList(companies, "Id", "CompanyName");
if ((InsuranceType)type == InsuranceType.Body)
{
var bodyInsurance = await _context.BodyInsurance
.Include(e => e.InsuranceCompany)
.FirstOrDefaultAsync(e => e.Id == id);
if (bodyInsurance == null)
{
model = new InsuranceViewModel
{
CompanyId = bodyInsurance.InsuranceCompanyId,
CompanyName = bodyInsurance.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(bodyInsurance.IssueDate).Ticks,
ExpireDate = new DateTime(bodyInsurance.ExpireDate).Ticks,
VehicleInformationId = id
};
}
else
{
var lastBody = await _context.BodyInsurance.Include(e => e.InsuranceCompany)
.Where(e => e.VehicleInformationId == id)
.OrderBy(e => e.ExpireDate)
.LastAsync();
model = new InsuranceViewModel
{
ExpireDate = new DateTime(lastBody.ExpireDate).AddYears(1).AddDays(1).Ticks,
CompanyId = lastBody.InsuranceCompanyId,
CompanyName = lastBody.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(lastBody.ExpireDate).AddDays(1).Ticks,
VehicleInformationId = id
};
}
}
else
{
if ((InsuranceType)type == InsuranceType.Thirdpart)
{
var thirdParty = await _context.ThirdPartyInsurance
.Include(e => e.InsuranceCompany)
.FirstOrDefaultAsync(e => e.Id == id);
if (thirdParty == null)
{
model = new InsuranceViewModel
{
CompanyId = thirdParty.InsuranceCompanyId,
CompanyName = thirdParty.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(thirdParty.IssueDate).Ticks,
ExpireDate = new DateTime(thirdParty.ExpireDate).Ticks,
VehicleInformationId = id
};
}
else
{
var lastThirdParty = await _context.ThirdPartyInsurance.Include(e => e.InsuranceCompany)
.Where(e => e.VehicleInformationId == id)
.OrderBy(e => e.ExpireDate)
.LastAsync();
model = new InsuranceViewModel
{
ExpireDate = new DateTime(lastThirdParty.ExpireDate).AddYears(1).AddDays(1).Ticks,
CompanyId = lastThirdParty.InsuranceCompanyId,
CompanyName = lastThirdParty.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(lastThirdParty.ExpireDate).AddDays(1).Ticks,
VehicleInformationId = id
};
}
}
return View(model);
}

LINQ - Group By child property

I have a list of users, each user has string array property called Tags. I am trying to get a unique list of tags and a total count, any idea what I a missing here? I am using LinqPad to write my test query, please see the example code:
void Main()
{
List<User> users = new List<User>(){
new User {Id = 1, Tags = new string[]{"tag1", "tag2"}},
new User {Id = 2, Tags = new string[]{"tag3", "tag7"}},
new User {Id = 3, Tags = new string[]{"tag7", "tag8"}},
new User {Id = 4, Tags = new string[]{"tag1", "tag4"}},
new User {Id = 5 },
};
var uniqueTags = users.Where(m=>m.Tags != null).GroupBy(m=>m.Tags).Select(m=> new{TagName = m.Key, Count = m.Count()});
uniqueTags.Dump();
// RESULT should BE:
// tag1 - Count(2)
// tag2 - Count(1)
// tag3 - Count(1)
// tag4 - Count(1)
// tag7 - Count(2)
// tag8 - Count(1)
}
public class User{
public int Id {get;set;}
public string[] Tags {get;set;}
}
You can flatten to IEnumerable<string> before grouping:
var uniqueTags = users.SelectMany(u => u.Tags ?? new string[0])
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } );
LINQPad C# Expression version:
new[] {
new { Id = 1, Tags = new[] { "tag1", "tag2" } },
new { Id = 2, Tags = new[] { "tag3", "tag7" } },
new { Id = 3, Tags = new[] { "tag7", "tag8" } },
new { Id = 4, Tags = new[] { "tag1", "tag4" } },
new { Id = 5, Tags = (string[])null }
}
.SelectMany(u => u.Tags ?? Enumerable.Empty<string>())
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } )

Convert Lambda Expression into an Expression tree

I have that lambda:
var Ids = profileExample.CostCenters
.Where(CostCentre => CostCentre != null)
.Select(CostCentre => CostCentre.Id);
Then i convert to that expression tree
static IEnumerable<Int64> AboveLambdaConvertedToExpressionTree(Profile profileExample)
{
//Begin var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
var property = profileExample.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != "Id").First();
var collection = ((IEnumerable)property.GetValue(profileExample, null)).AsQueryable();
var collectionType = property.PropertyType.GetGenericArguments()[0];
var collectionTypeName = collectionType.Name;
var keyType = typeof(Int64);
var keyName = "Id";
//BeginWhere
var parameter = Expression.Parameter(collectionType, collectionTypeName);
var profileExampleWhere = Expression.Lambda(
Expression.NotEqual(parameter, Expression.Constant(null)),
parameter);
var profileExampleWhereCall = Expression.Call(typeof(Enumerable),
"Where",
new Type[] { collectionType },
collection.Expression,
profileExampleWhere);
//EndWhere
//BeginSelect
var profileExampleSelect = Expression.Lambda(Expression.PropertyOrField(parameter, keyName),
parameter);
var profileExampleSelectCall = Expression.Call(typeof(Enumerable),
"Select",
new Type[] { collectionType, keyType },
profileExampleWhereCall,
profileExampleSelect);
var Ids = Expression.Lambda(profileExampleSelectCall).Compile().DynamicInvoke();
//EndSelect
//End var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
return ((IEnumerable)Ids).Cast<Int64>();
}
Now i want to do the same with bellow lambda
var result = Set.AsQueryable()
.Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id)
.Any(Id => Ids.Contains(Id))).ToList();
But i stuck in .Any(Id => Ids.Contains(Id))....
var id = Expression.Parameter(typeof(long), "Id");
var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
var profile = Expression.Parameter(typeof(Profile), "Profile");
var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
var selectCall = Expression.Call(typeof(Enumerable),
"Select",
new Type[] { typeof(CostCentre), typeof(long) },
Expression.PropertyOrField(profile, "CostCenters"),
selectLambda);
How can i call Any from selectCall and call Ids.Contains...
Full code to run as console application bellow:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTrees
{
class Program
{
static void Main(string[] args)
{
var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
Ids = AboveLambdaConvertedToExpressionTree(profileExample);
var result = Set.AsQueryable().Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id))).ToList();
//Expression<Func<Profile, bool>> lambda = (Profile) => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id));
var id = Expression.Parameter(typeof(long), "Id");
var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
var profile = Expression.Parameter(typeof(Profile), "Profile");
var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
var selectCall = Expression.Call(typeof(Enumerable),
"Select",
new Type[] { typeof(CostCentre), typeof(long) },
Expression.PropertyOrField(profile, "CostCenters"),
selectLambda);
}
static IEnumerable<Int64> AboveLambdaConvertedToExpressionTree(Profile profileExample)
{
// I show that as example of what i need to do
var keyType = typeof(Int64);
var keyName = "Id";
//Begin var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
var property = profileExample.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != keyName).First();
var collection = ((IEnumerable)property.GetValue(profileExample, null)).AsQueryable();
var collectionType = property.PropertyType.GetGenericArguments()[0];
var collectionTypeName = collectionType.Name;
//BeginWhere
var parameter = Expression.Parameter(collectionType, collectionTypeName);
var profileExampleWhere = Expression.Lambda(
Expression.NotEqual(parameter, Expression.Constant(null)),
parameter);
var profileExampleWhereCall = Expression.Call(typeof(Enumerable),
"Where",
new Type[] { collectionType },
collection.Expression,
profileExampleWhere);
//EndWhere
//BeginSelect
var profileExampleSelect = Expression.Lambda(Expression.PropertyOrField(parameter, keyName),
parameter);
var profileExampleSelectCall = Expression.Call(typeof(Enumerable),
"Select",
new Type[] { collectionType, keyType },
profileExampleWhereCall,
profileExampleSelect);
var Ids = Expression.Lambda(profileExampleSelectCall).Compile().DynamicInvoke();
//EndSelect
//End var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
return ((IEnumerable)Ids).Cast<Int64>();
}
public partial class Profile
{
public virtual Int64 Id { get; set; }
public virtual ICollection<CostCentre> CostCenters { get; set; }
}
public partial class CostCentre
{
public virtual Int64 Id { get; set; }
}
public static Profile profileExample
{
get
{
return new Profile()
{
Id = 1,
CostCenters = new List<CostCentre>() { new CostCentre() { Id = 2 } }
};
}
}
public static IList<Profile> Set
{
get
{
return new List<Profile>() { new Profile() { Id = 1,
CostCenters = new List<CostCentre>() { new CostCentre() { Id = 1 },
new CostCentre() { Id = 2 } }
},
new Profile() { Id = 2,
CostCenters = new List<CostCentre>() { new CostCentre() { Id = 2 },
new CostCentre() { Id = 3 } }
},
new Profile() { Id = 3,
CostCenters = new List<CostCentre>() { new CostCentre() { Id = 3 } }
} };
}
}
}
}
Since Any is a Generic Method you need to create it for a specific type. The method below gets the Any<T> method from the Enumerable type.
public static MethodInfo GetAnyExtensionMethod(Type forType)
{
MethodInfo method =
typeof(Enumerable).GetMethods()
.First(m => m.Name.Equals("Any") &&
m.GetParameters().Count() == 2);
return method.MakeGenericMethod(new[] { forType });
}
Its solved with help of Mads from MS
class Program
{
static void Main(string[] args)
{
//var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
var Ids = AboveLambdaConvertedToExpressionTree(profileExample);
//var result = Set.AsQueryable().Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id))).ToList();
var id = Expression.Parameter(typeof(long), "Id");
var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
var profile = Expression.Parameter(typeof(Profile), "Profile");
var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
var selectCall = Expression.Call(typeof(Enumerable),
"Select",
new Type[] { typeof(CostCentre), typeof(long) },
Expression.PropertyOrField(profile, "CostCenters"),
selectLambda);
//var id2 = Expression.Parameter(typeof(long), "Id");
var containsCall = Expression.Call(typeof(Enumerable),
"Contains",
new Type[] { typeof(long) },
Expression.Constant(Ids),
id);
var anyLambda = Expression.Lambda(containsCall, id);
var anyCall = Expression.Call(typeof(Enumerable),
"Any",
new Type[] { typeof(long) },
selectCall,
anyLambda);
var whereLambda = Expression.Lambda(anyCall, profile);
var callExpression = Expression.Call(typeof(Queryable),
"Where",
new Type[] { typeof(Profile) },
Set.AsQueryable().Expression,
whereLambda);
var result = Expression.Lambda(callExpression).Compile().DynamicInvoke();
}

MVC3 select with condition give strange behavior

I'm trying to implement a result filter with MVC3 and face a problem like this:
public ActionResult Index(int? SubID)
{
var product = db.Product.Where(s => s.SubID == SubID).Include(t => t.SubCategory);
if (SubID.HasValue)
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN", SubID);
}
else
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN");
}
return View(product);
}
This one above works fine, but the following one always give me result of the whole table, despite what condition I put on it:
public ActionResult Index(int? SubID)
{
var product = db.Product.Include(t => t.SubCategory);
if (SubID.HasValue)
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN", SubID);
product.Where(s => s.SubID == SubID);
}
else
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN");
}
return View(product);
}
And even this one doesn't work too:
public ActionResult Index(int? SubID)
{
var product = from m in db.Product
select m;
if (SubID.HasValue)
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN", SubID);
product.Where(s => s.SubID == SubID);
}
else
{
ViewBag.SubID = new SelectList(db.SubCategory, "SubID", "SubNameVN");
}
product.Include(t => t.SubCategory);
return View(product);
}
So please tell me what is the difference between these 3 approaches, and please explain to me why #2 and #3 don't work?
product.Where(s => s.SubID == SubID);
...does not add a condition on product, it just creates an IEnumerable with the condition applied, and immediately throws it away. What you want is probably;
product = product.Where(s => s.SubID == SubID);

Resources