Hierarchical layering in Linq - c#

If I have a set of employee data similar to:
var users = new[]
{
new {SupervisorId = "CEO", UserId = "CEO", UserName = "Joe"},
new {SupervisorId = "CEO", UserId = "CIO", UserName = "Mary"},
new {SupervisorId = "CIO", UserId = "XDIR", UserName = "Ed"},
new {SupervisorId = "CIO", UserId = "YDIR", UserName = "Lisa"},
new {SupervisorId = "XDIR", UserId = "AMNGR", UserName = "Steve"},
new {SupervisorId = "AMNGR", UserId = "ASUP", UserName = "Lesley"}
};
Would it be possible to use Linq to add hierarchical layers, in the sense that:
CEO = 1 (top)
CIO = 2 (2nd level)
XDIR and YDIR = 3 (3rd level)
AMNGR = 4 (etc)
ASUP = 5 (etc)
I've been able to group the employees according to SupervisorId, but not sure how to make the "level" happen.
var userGroups = from user in users
group user by user.SupervisorId into userGroup
select new
{
SupervisorId = userGroup.Key,
Level = ??????
Users = userGroup.ToList()
};
foreach (var group in userGroups)
{
Console.WriteLine("{0} - {1} - {2}", group.SupervisorId, group.Level, group.Users.Count);
}
Many thanks.

I would add a ranking to your linq "user object"
public class User{
public string SupervisorId {get;set;}
public string UserId {get;set;}
public string UserName {get;set;}
public int Level {get { return GetRank(SupervisorId ) ; } }
private int GetRank(string userId){
if(string.IsNullOrEmpty(userId)){
//Bad case, probably want to use a very large number
return -1;
}
int level = 0;
switch(userId){
case "CEO":
level = 0;
break;
//insert others here
}
}
}
Then your Linq you would add a join.
var userGroups = from user in users
join super in users on user.SupervisorId equals super.UserId
group user by user.SupervisorId into userGroup
select new
{
SupervisorId = userGroup.Key,
Level = super.Level,
Users = userGroup.ToList()
};

ILookup<string, User> subordLookup = users
.ToLookup(u => u.SupervisorId);
foreach(User user in users)
{
user.Subordinates = subordLookup[user.UserId].ToList();
}
User userHierarchy = user.Single(u => u.UserId == "CEO");
Disclaimers:
Does not handle multiple CEOs.
Preserves circular relationships.
Leaves orphans behind.

Is this what you are looking for?
var levels = new[]
{
new { Level = 1, LevelName = "CEO" },
new { Level = 2, LevelName = "CIO" },
new { Level = 3, LevelName = "XDIR" },
new { Level = 3, LevelName = "YDIR" },
new { Level = 4, LevelName = "AMNGR" },
new { Level = 5, LevelName = "ASUP" }
};
var userGroups = from user in users
join level in levels on
user.UserId equals level.LevelName
group new{ User = user, Level = level.Level } by new { SuperId = user.SupervisorId, Level = level.Level } into userGroup
select new
{
SupervisorId = userGroup.Key.SuperId,
Level = userGroup.Key.Level,
Users = userGroup.ToList()
};

Update
Heres one way to create a lookup table for each level. Its fairly and I dont know how it will scale. Obviously, you'll need to adapt it to pull the rows from your database.
Define a class to hold our lookup table
public class user{
public string SupervisorId;
public string UserId;
public int Level;
}
Then we get a unique list of UserId/SupervisorId combinations and loop through the list calculating the level for each combination by 'walking' up the tree.
var uniqueusers = (new user[]
{
new user {SupervisorId = "CEO", UserId = "CEO"},
new user {SupervisorId = "CEO", UserId = "CIO"},
new user {SupervisorId = "CIO", UserId = "XDIR"},
new user {SupervisorId = "CIO", UserId = "YDIR"},
new user {SupervisorId = "XDIR", UserId = "AMNGR"},
new user {SupervisorId = "AMNGR", UserId = "ASUP"}
}).Distinct();
foreach (var item in uniqueusers)
{
int level = 0;
user CurrentUser = item;
while (CurrentUser.UserId != CurrentUser.SupervisorId){
CurrentUser = uniqueusers.Where(c => c.UserId == CurrentUser.SupervisorId).FirstOrDefault();
level++;
}
item.Level = level;
}
Now you can use the uniqueusers as a lookup table to determine the level for your query. eg
private int GetLevel(string userId){
return uniqueusers.Where(c => c.UserId == userId).FirstOrDefault().Level;
}
You could probably even combine this into a single step with a little effort.

Related

API to stripe make 500

That's when i need to use the Stripe API so when i need it, it will go wrong and make mistakes in the Stripe area as you can see here.
i have : v15.6.1 on Stripe.net
Where it goes wrong is here:
planservice.Create(new StripePlanCreateOptions()
to here:
PlanId = abn.PriceValueUnikId };
all the value I get by json eg userid, pric and pricId there is content in them.
[HttpPost]
public IActionResult Post([FromBody] JObject token)
{
var api = Settings.ConstName.StrinpAPIKeyTest;
StripeConfiguration.SetApiKey(api);
var chargeService = new StripeChargeService();
chargeService.ExpandBalanceTransaction = true;
chargeService.ExpandCustomer = true;
chargeService.ExpandInvoice = true;
//StripeCharge stripeCharge = chargeService.Get(api);
var customerSerive = new StripeCustomerService(api);
var subservice = new StripeSubscriptionService(api);
var planservice = new StripePlanService(api);
var pricId = (int)token.GetValue("pricid");
var pric = (int)token.GetValue("pric");
var userid = (int) Userid();
var abn = _dbContext.PriceValue.FirstOrDefault(i => i.PriceValueId == pricId || i.Price == pric);
//Finder information omkring pakken til den enkelte pakke.
var currentUser = _dbContext.Users.FirstOrDefault(i => i.UserId == userid);
if (currentUser != null)
{
if (abn != null)
{
var orderid = Settings.ValueWordsAndNumbers.OrdreValue();//Orderid
var planType = $"OrderId: {orderid} - Pris: {abn.Price} - Mdr: {abn.Months} UserId: {userid}";
planservice.Create(new StripePlanCreateOptions()//error from here
{
Amount = int.Parse(abn.Price.ToString()) * 100,
Nickname = planType,
Currency = "dkk",
Interval = "month",
IntervalCount = abn.Months,
Id = abn.PriceValueUnikId
});
var newCustomer = new StripeCustomerCreateOptions
{
SourceToken = token["id"].ToString(),
Email = token["email"].ToString(),
PlanId = abn.PriceValueUnikId,
};//error to here
var stripeCustomer = customerSerive.Create(newCustomer);
}
}
var planOptions = new StripePlanCreateOptions() {
Product = new StripePlanProductCreateOptions() {
Name = "planType"
},
Amount = int.Parse(abn.Price.ToString()) * 100,
Nickname = planType,
Currency = "dkk",
Interval = "month",
IntervalCount = abn.Months,
};
var planService = new StripePlanService();
StripePlan plan = planService.Create(planOptions);
API version to 2018-02-06 and add support for Product & Plan API
Now Product is REQUIRED.
you need past ID product or dictionary containing fields used to create a service product.
var planOptions = new StripePlanCreateOptions() {
ProductId ="Product Plan id",
Amount = int.Parse(abn.Price.ToString()) * 100,
Nickname = planType,
Currency = "dkk",
Interval = "month",
IntervalCount = abn.Months,
};

No access to properties when inserting to child tables in Entity Framework

enter image description here
[WebMethod]
public void AddEmployementRequest(EmployementRequest emp)
{
EmployeeSkill employeeSkill = new EmployeeSkill
{
Skill = emp.,
Description = emp.
};
EmployeeLanguage employeeLanguage = new EmployeeLanguage
{
Name = emp.,
ConversationLevel = emp.
};
EmployeeCours employeeCours = new EmployeeCours
{
Date =emp. ,
Course = emp.,
Duration = emp.,
Association = emp.,
Description = emp.
};
EmployementRequest employementRequest = new EmployementRequest
{
Name = emp.Name,
Address = emp.Address,
Surnam = emp.Surnam,
Father = emp.Father,
IDNumber = emp.IDNumber,
IDCardNumber = emp.IDNumber,
IDCity = emp.IDCity,
Birthday = emp.Birthday,
Birthplace = emp.Birthplace,
Nationality = emp.Nationality,
Religion = emp.Religion,
Phone = emp.Phone,
Cell = emp.Cell,
EmergencyAddress = emp.EmergencyAddress,
EmergencyName = emp.EmergencyName,
EmergencyPhone = emp.EmergencyPhone,
ParentedPeople = emp.ParentedPeople,
Gender = emp.Gender,
MarriageStatus = emp.MarriageStatus,
Residency = emp.Residency,
InsuranceCode = emp.InsuranceCode,
InsuranceStatus = emp.InsuranceStatus,
VehicleType = emp.VehicleType,
MilitaryServiceStatus = emp.MilitaryServiceStatus,
EducatedFrom = emp.EducatedFrom,
EducationField = emp.EducationField,
EducationGrade = emp.EducationGrade,
ExtraWorkCapability = emp.ExtraWorkCapability,
LeisureTimeHobbies = emp.LeisureTimeHobbies,
Salary = emp.Salary,
IntroducerName = emp.IntroducerName,
IntroductionMethod = emp.IntroductionMethod,
Illness = emp.Illness,
VehicleStatus = emp.VehicleStatus,
PKEmploymentRequest = Guid.NewGuid(),
};
employementRequest.EmployeeLanguages.Add(employeeLanguage);
employementRequest.EmployeeSkills.Add(employeeSkill);
employementRequest.EmployeeCourses.Add(employeeCours);
using (var db = new UKN_DBNAMEEntities())
{
db.EmployementRequests.Add(employementRequest);
db.SaveChanges();
}
}
I want to insert to all parent and child tables at once ,As you can see I can't access the properties in child tables and also there's no intellisense to show the properties unlike the parent
I think I need a Linq query but I have no idea
have you tried
[WebMethod]
public void AddEmployementRequest(EmployementRequest emp) {
emp.PKEmploymentRequest = Guid.NewGuid();
using (var db = new UKN_DBNAMEEntities()) {
db.EmployementRequests.Add(emp);
db.SaveChanges();
}
}
It may/should do, but...
Consider:
using automapper or the like;
use some query to avoid recreation of Language or Skil

How to join Enum with LINQ Query

I have user table (Default ApplicationUser Table from IdentityUser by ASP.CORE)
and I have added additional field for RoleType. There is also an Enum I added to specify Role Definition.
public enum Roles
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
Now I want to show all the users in a view as a table along with role description.
I am unable to make LINQ query with Enum & User table using LINQ join.
To get the list of Roles from the enum use:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
you can then use this in your Linq query, for example:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
var users = from u in ApplicationUser
join r in roles on u.Role equals r.Value
select new {Name = u.Name, RoleId = u.Role, RoleDescription = r.Name} ;
A simpler way without the Enum.GetValues is:
var users = from u in ApplicationUser
select new {Name = u.Name, RoleId = u.Role, RoleDescription = (Roles)r.Role.ToString()} ;
var xx = from u in _context.Users
.Select(x => new ApplicationUserList
{ Firstname = x.Firstname,
RoleType = ((Roles)x.RoleId).ToString()
});
// This join is performed in memory
var results =
from e in Enum.GetValues(typeof(Roles)).Cast<Roles>()
join r in ApplicationUser on e equals r.Roles into rs
from r in rs.DefaultIfEmpty()
select new { Roles = e, Count = r?.Count ?? 0};
If I understand your question, you should first convert enum to dictionary an Join between what you need, here is an example:
static void Main(string[] args)
{
ApplicationUser a = new ApplicationUser();
a.userName = "a";
a.role = 1;
ApplicationUser b = new ApplicationUser();
b.userName = "b";
b.role = 3;
List<ApplicationUser> alist=new List<ApplicationUser>();
alist.Add(a);
alist.Add(b);
Dictionary<int, string> DicRoles = new Dictionary<int, string>();
var vals = Enum.GetValues(typeof(Roles));
foreach (var val in vals)
{
DicRoles.Add((int)val, val.ToString());
}
var result = from t in alist
join x in DicRoles on t.role equals x.Key
select new {t.userName,x.Value };
}
public enum Roles:int
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
}
public class ApplicationUser
{
public string userName { get; set; }
public int role { get; set; }
}

Using Linq to select multiple items per iteration?

Per iteration, this query creates an EmailRecipient for each populated address. Can it be done without the multiple iteration?
var addedRecipients = (from oldRecip in oldEmailRecipients
where !string.IsNullOrWhiteSpace(oldRecip.EmailAddress1)
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = oldRecip.EmailAddress1
}
).Union(from oldRecip in oldEmailRecipients
where !string.IsNullOrWhiteSpace(oldRecip.EmailAddress2)
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = oldRecip.EmailAddress2
});
You can use SelectMany extension method:
var addedRecipients = oldEmailRecipients.SelectMany(e=>
{
var result= new List<EmailRecipient>();
if(!string.IsNullOrWhiteSpace(e.EmailAddress1))
{
result.Add(new EmailRecipient
{
UserName = e.UserName,
EmailAddress = e.EmailAddress1
});
}
if(!string.IsNullOrWhiteSpace(e.EmailAddress2))
{
result.Add(new EmailRecipient
{
UserName = e.UserName,
EmailAddress = e.EmailAddress2
});
}
return result;
});
Update
The solution that I show above only works in Linq to Objects. Your comment suggest me you are using EF. A simple solution could be call AsEnumerable method before SelectMany to make the switch to Linq to Objects, but that could end harming your performance if you are not filtering your recipients first.
Another solution could be selecting only data that you need first from you server before of call SelectMany to not load other columns you don't need in this case:
...Where(...)
.Select(r=>new{UserName=r.UserName,
EmailAddress1=r.EmailAddress1,
EmailAddress2=r.EmailAddress2 })
.AsEnumerable()
.SelectMany(...);
Sticking with query syntax, and making sure to only process oldEmailRecipients items who have either a non-null/whitespace EmailAddress1 or a non-null/whitespace EmailAddress2:
var addedRecipients =
from oldEmail in oldEmailRecipients
let hasEmail1 = !string.IsNullOrWhiteSpace(oldEmail.EmailAddress1)
let hasEmail2 = !string.IsNullOrWhiteSpace(oldEmail.EmailAddress2)
where hasEmail1 || hasEmail2
let emailUserNameCombos = hasEmail1 && hasEmail2
? new[]
{
new {Email = oldEmail.EmailAddress1, oldEmail.UserName},
new {Email = oldEmail.EmailAddress2, oldEmail.UserName}
}
: hasEmail1
? new[] {new {Email = oldEmail.EmailAddress1, oldEmail.UserName}}
: new[] {new {Email = oldEmail.EmailAddress2, oldEmail.UserName}}
from emailUsername in emailUserNameCombos
select new EmailRecipient
{
UserName = emailUsername.UserName,
EmailAddress = emailUsername.Email
};
You can build an inline array to add both emails and flatten them out using SelectMany.
var addedRecipients = from oldRecip in oldEmailRecipients
let emails =
new[] {oldRecip.EmailAddress1, oldRecip.EmailAddress2}.Where(e => !string.IsNullOrWhiteSpace(e))
from email in emails
where emails.Any()
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = email
};
When your EmailRecipient has more than two email address then you could do this:
// Just building a pseudo dataclass
List<Recipient> oldEmailRecipients = Enumerable.Range(1, 10).Select(item => new Recipient()
{
Name = "Recipient" + item,
EmailAddress1 = "pseudo" + item + "#gmail.com",
EmailAddress2 = "pseudo" + (item + 1) + "#gmail.com",
//EmailAddress3 = "pseudo" + (item + 2) + "#gmail.com",
EmailAddress3 = "",
EmailAddress4 = "pseudo" + (item + 3) + "#gmail.com",
} ).ToList( )
// create anonymous object for each recipient and a list of valid adresses
var query = from mailRecipients in oldEmailRecipients
select new
{
Name = mailRecipients.Name,
Addresses = new List<string>()
{
mailRecipients.EmailAddress1,
mailRecipients.EmailAddress2,
mailRecipients.EmailAddress3,
mailRecipients.EmailAddress4
}.Where(item => string.IsNullOrEmpty( item ) == false )
};
// create an EmailRecipient for each valid combination
var final = from item in query
from address in item.Addresses
select new EmailRecipient
{
Name = item.Name,
Address = address
};

Cannot be constructed in a LINQ to Entities query. Error

public ActionResult CreateApp(Guid id)
{
SMICParkingLotApplicationEntities1 dbb = new SMICParkingLotApplicationEntities1();
ApplicationDATA applicationData = (from a in dbb.ApplicationDATAs
where a.ApplicationID == id
select new ApplicationDATA
{
ApplicationID = a.ApplicationID,
BrandModel = a.BrandModel,
CrNo = a.CrNo,
OrNo = a.OrNo,
DatePosted = a.DatePosted,
PoR = a.PoR,
PlateNo = a.PlateNo,
VehicleType = a.VehicleType
}).FirstOrDefault();
ApplicationSlotViewModel applicationSlotViewModel = new ApplicationSlotViewModel
{
ApplicationDatas = applicationData,
Application = new Application()
};
return View(applicationSlotViewModel);
Dunno what to do it always shows this error Cannot be constructed in a LINQ to Entities query. Error Help Plss..
If the type of ApplicationDatas in your ViewModel is ApplicationDATA, you can set it directly with the result of your query:
var applicationData =dbb.ApplicationDATAs.FirstOrDefault(a=>a.ApplicationID == id);
ApplicationSlotViewModel applicationSlotViewModel = new ApplicationSlotViewModel
{
ApplicationDatas = applicationData,
Application = new Application()
};
You can't project the result of your query using an existing entity type. Check the post that I quote in my comment
Remove className after new keyword. Try below code.
var applicationData = (from a in dbb.ApplicationDATAs
where a.ApplicationID == id
select new
{
ApplicationID = a.ApplicationID,
BrandModel = a.BrandModel,
CrNo = a.CrNo,
OrNo = a.OrNo,
DatePosted = a.DatePosted,
PoR = a.PoR,
PlateNo = a.PlateNo,
VehicleType = a.VehicleType
}).FirstOrDefault();

Categories

Resources