Iterate through nested collection and form org structure with Parent - c#

I have a batch of users defined with that model.
I need to iterate inside Manager nesting while manager is not null and create Departments entities with parents relation. I have an example of code what I already have, so I post it here.
What I already have:
public class AdUserModel
{
public string UserName { get; set; }
public AdUserModel Manager { get; set; }
}
...
List<UserDepartment> userDepartmentsToAdd = new List<UserDepartment>();
List<Department> newDeps = new List<Department>();
RecurseDepartments(adUser);
var userDepartment = new UserDepartment
{
User = user,
PositionName = adUser.PositionName,
Department = newDeps.FirstOrDefault(x => x.Name == adUser.DepartmentName),
IsHeadUser = user.SubordinateUsers?.Any() ?? false
};
userDepartmentsToAdd.Add(userDepartment);
void RecurseDepartments(AdUserModel model)
{
var department = new Department();
var existDep = newDeps.FirstOrDefault(x => x.Name ==
model.DepartmentName);
if (existDep == null)
{
department.Name = model.DepartmentName;
}
if (model.Manager is not null)
{
if (newDeps.FirstOrDefault(x => x.Name == model.Manager.DepartmentName) == null)
{
var parentDepartment = new Department
{
Name = model.Manager.DepartmentName
};
department.ParentDepartment = existDep ?? department;
if (existDep == null)
{
newDeps.Add(department);
}
newDeps.Add(parentDepartment);
}
if (model.Manager.DepartmentName != model.DepartmentName)
{
RecurseDepartments(model.Manager);
}
}
}
Thanks for any help in advance, being stuck here for some reason.

Related

Cross context communication EF Core

I have Student and Backpack entities:
internal class Student
{
public Guid Guid { get; set; }
public string Name { get; set; }
ICollection<Backpack> backpacks;
public virtual ICollection<Backpack> Backpacks
{
get
{
if (backpacks == null && LazyLoader != null)
{
backpacks = LazyLoader.Load(this, ref backpacks);
}
return backpacks;
}
set
{
backpacks = value;
}
}
ILazyLoader LazyLoader;
public Student(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
public Student()
{
}
}
internal class Backpack
{
public Backpack()
{
}
ILazyLoader LazyLoader;
public Backpack(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
public Guid Guid { get; set; }
public string Name { get; set; }
Student student;
public virtual Student Student
{
get
{
if (student == null && LazyLoader != null)
{
student = LazyLoader.Load(this, ref student);
}
return student;
}
set
{
student = value;
}
}
}
When an entity is changed and saved from one context and is used in another context, I want to update them. For example:
Scenario 1: when changing primitive properties and saved, I am updating the entry using reload:
var context1 = new MainContext();
var studentDilshodFromContext1 = context1.Set<Student>().First(s => s.Name == "Mike");
var context2 = new MainContext();
var studentMikeFromContext2 = context2.Set<Student>().First(s => s.Name == "Mike");
studentMikeFromContext1.Name = "Jake";
context1.SaveChanges();
context2.Entry(studentMikeFromContext2).Reload();
Console.WriteLine(studentMikeFromContext2.Name); //Nice! it is Jake after reloading
Scenario 2: now, I want to change the navigation property:
var context1 = new MainContext();
var jakeStudent = context1.Set<Student>().First(s => s.Name == "Jake");
var mikeBackpackFromContext1 = context1.Set<Backpack>().First(s => s.Name == "Mike's Backpack");
mikeBackpackFromContext1.Student = jakeStudent;
var context2 = new MainContext();
var mikeBackpackFromContext2 = context2.Set<Backpack>().First(s => s.Name == "Mike's Backpack");
context1.SaveChanges();
context2.Entry(mikeBackpackFromContext2).Reload();
Console.WriteLine(mikeBackpackFromContext2.Student.Name); //Object reference exception because Student is null. I am expecting Jake
Scenario 3: when the item was added to the navigation collection property, I would like to see it in context2:
var context1 = new MainContext();
var jakeStudentFromContext1 = context1.Set<Student>().First(s => s.Name == "Jake");
var newBackpack = new Backpack() { Student = jakeStudentFromContext1, Guid = Guid.NewGuid(), Name = "New Jake backpack" };
context1.Add(newBackpack);
var context2 = new MainContext();
var jakeStudentFromContext2 = context2.Set<Student>().First(s => s.Name == "Jake");
var backpacks = jakeStudentFromContext2.Backpacks;
context1.SaveChanges();
context2.Entry(jakeStudentFromContext2).Reload();
Console.WriteLine(jakeStudentFromContext2.Backpacks.Any(d => d.Guid == newBackpack.Guid)); //It is false but I am expecting true
As you can see entry.Reload() working fine only with primitive type properties, but not working with navigation properties. I tried NavigationEntry.Load and CollectionEntry.Load but they are not working as well.
So, in these scenarios how can I re-load my navigation properties?

JSON for tree view of employees

I have list of students that needs to be loaded as tree structure.
The following is my Employee class
public class Employee
{
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public string Name { get; set; }
public bool IsDefault { get; set; }
public int Order { get; set; }
}
This is my tree view class which I want to display to the user
public class Tree
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool isSelected { get; set; }
public List<Tree> children { get; set; }
}
Based on the employee list the json should be displayed something like this
The below code is giving me duplicates for each parent id and not giving the expected result.
var groupedUsers = result.GroupBy(t=>t.ParentId).SelectMany(t=>t.ToList());
foreach (var item in groupedUsers)
{
if (item.ParentId == Guid.Empty)
{
treeData.Add(new Tree()
{
Id = item.Id,
children = new List<Tree>(),
isSelected = item.IsDefault,
Name = item.Name
});
}
else
{
var parent = treeData.FirstOrDefault(t => t.Id == item.ParentId);
if (parent != null)
{
parent.children.Add(new Tree()
{
Id = item.Id,
children = new List<Tree>(),
isSelected = item.IsDefault,
Name = item.Name
});
treeData.Add(parent);
}
}
}
Update: With Robert's answer I was able to get going but it is still duplicates
the end json could be found here Tree JSON So I updated the code to something like this to eliminate duplicates. But it is not working I guess I am missing some thing in recursion?
static void Main(string[] args)
{
var text = File.ReadAllText(#"C:\Users\Dinesh\source\repos\Tests\Tests\Employee.json");
var result = JsonConvert.DeserializeObject<List<Employee>>(text);
var groupedUsers = result.GroupBy(t => t.ParentId).SelectMany(t => t.ToList()).OrderBy(t=>t.Order).ToList();
foreach (var item in groupedUsers)
{
AddEmployee(item, null, groupedUsers, treeData);
}
}
static Tree AddEmployee(Employee parentEmployee, Employee childEmployee, List<Employee> groupedUsers, List<Tree> newTreeData)
{
if (parentEmployee.ParentId == Guid.Empty)
{
var root = new Tree()
{
Id = parentEmployee.Id,
children = new List<Tree>(),
isSelected = parentEmployee.IsDefault,
Name = parentEmployee.Name
};
treeData.Add(root);
return root;
}
else
{
var parent = treeData.FirstOrDefault(t => t.Id == parentEmployee.ParentId);
if (parent == null)
{
var parentItem = groupedUsers.Single(x => x.Id == parentEmployee.ParentId);
var currentTree = newTreeData.SelectMany(t => t.children).Where(y => y.Id == parentEmployee.ParentId).ToList();
parent = AddEmployee(parentItem, parentEmployee, groupedUsers, currentTree);
}
if (childEmployee != null)
{
var childsParent = newTreeData.Where(y=>y.Id == childEmployee.ParentId).First();
return childsParent;
}
var child = newTreeData.FirstOrDefault(t => t.Id == parentEmployee.Id);
if (child != null)
return child;
child = new Tree()
{
Id = parentEmployee.Id,
children = new List<Tree>(),
isSelected = parentEmployee.IsDefault,
Name = parentEmployee.Name
};
parent.children.Add(child);
return child;
}
}
UPDATED: I think you should use recurrency. You may loose children if parent is not found.
Some draft may look like this:
var treeData = new List<Tree>();
var groupedUsers = result.GroupBy(t => t.ParentId).SelectMany(t => t.ToList());
foreach (var item in groupedUsers)
{
AddEmployee(item);
}
Tree AddEmployee(dynamic item)
{
if (item.ParentId == Guid.Empty)
{
var root = new Tree()
{
Id = item.Id,
children = new List<Tree>(),
isSelected = item.IsDefault,
Name = item.Name
};
treeData.Add(root);
return root;
}
else
{
var parent = Find(item.ParentId, treeData);
if (parent == null)
{
var parentItem = groupedUsers.Single(x => x.Id == item.ParentId);
parent = AddEmployee(parentItem );
}
var child = Find(item.Id, treeData);
if (child != null)
return child;
child = new Tree()
{
Id = item.Id,
children = new List<Tree>(),
isSelected = item.IsDefault,
Name = item.Name
};
parent.children.Add(child );
return child;
}
}
Tree Find(Guid item, List<Tree> tree)
{
var parent = tree?.FirstOrDefault(t => t.Id == item);
if (parent != null)
{
return parent;
}
if (tree != null)
{
foreach(var t in tree)
{
parent = Find(item, t?.children);
if (parent != null)
return parent;
}
}
return null;
}
UPDATE: Added Find method. You need to search through whole tree
Because it gets better formated as in the comments.
Your line treeData.Add(parent); always adds the item -
regardless of whether it already was added or not.
var parent = treeData.FirstOrDefault(t => t.Id == item.ParentId);
if (parent != null)
{
parent.children.Add(new Tree()
{
Id = item.Id,
children = new List<Tree>(),
isSelected = item.IsDefault,
Name = item.Name
});
// do not add the parent again here
}
else
{
// add the parent only if was not found above
// TODO parent = new Tree(...);
treeData.Add(parent);
}

Set Object null if all its properties are null in C#

I want to write a function which it turns every properies and child properties of an object. And if all properties of one property are null, then I will set that property as null. I will explain with one example.
For example, if both TeacherName and TeacherSurname are null then I want to set Teacher to null. Then, if ExamMark and ExamName and Teacher are null, then Exam will be null.
You can see json version of like my question from this link Json Version
public class Student
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public Exam Exam { get; set; }
}
public class Exam
{
public string ExamMark { get; set; }
public string ExamName { get; set; }
public Teacher Teacher { get; set; }
}
public class Teacher
{
public string TeacherName { get; set; }
public string TeacherSurname { get; set; }
}
public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
I wrote this method. But this doing only first step. But I need recursive for child class. How can I convert this method to recursive?
public static object ConvertToNull(object obj)
{
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
var mainClassProperties = properties.Where(p => p.PropertyType.Assembly == objType.Assembly);
foreach (var mainClassProperty in mainClassProperties)
{
object propValue = mainClassProperty.GetValue(obj, null);
var classAllProperties = propValue.GetType().GetProperties();
if (propValue.GetType().GetProperties().All(propertyInfo => propertyInfo.GetValue(propValue) == null))
{
mainClassProperty.SetValue(obj, null);
}
}
return obj;
}
What you're asking for is actually an anti-pattern. What this does is it propagates the requirement for tons of null checks in your code. Not only do you have to check if all properties are null, you also have to check if your object is null when you want to use it. Also reflection is very slow and if you're doing this often, you'll bog down your application.
You should look into the Null Object Pattern. It basically does what you want it to, and you remove all those pesky null checks:
public class Student
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public Exam Exam { get; set; }
public static Student NullStudent { get; } = new Student
{
Name = null,
Surname = null,
Address = Address.NullAddress,
Exam = Exam.NullExam,
}
}
You can do this for each object in your code that acts this way (you can see that I did it on your nested Address and Exam types as well) and then instead of doing this:
if (Student.EverythingIsNull) { Student = null }
if (Student is null) { //do null stuff }
You can do this:
if (item == Student.NullStudent) { //do null stuff }
This leads to clearer code, your intent stands out more, and you can specifically define in each object what constitutes as null.
using Generics and Reflection.
public T ConvertToNull<T>(T model) where T : class
{
if (model == null) return null;
Type type = model.GetType();
PropertyInfo[] properties = type.GetProperties();
var valueTypes = properties.Where(p => p.PropertyType.Assembly != type.Assembly);
var nonValueTypes = properties.Where(p => p.PropertyType.Assembly == type.Assembly);
foreach (var nonValueType in nonValueTypes)
nonValueType.SetValue(model, ConvertToNull(nonValueType.GetValue(model)));
if (valueTypes.All(z => z.GetValue(model) == null) && nonValueTypes.All(z => z.GetValue(model) == null))
return null;
else
return model;
}
Here you can call it
List<Student> students = new List<Student>();
Student student = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address() { City = "City", Country = "Country" }, Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() { TeacherName = "TeacherName", TeacherSurname = "TeacherSurname" } } };
Student student2 = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address(), Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() } };
students.Add(student);
students.Add(student2);
List<Student> results = new List<Student>();
foreach (var item in students)
{
var result = ConvertToNull(item);
results.Add(result);
}
Have a look at this The GetValueOrNull should work and do what you need, not tested with all possible use cases but it oculd be tweaked a little if doesn't work in all cases
public static bool IsSimpleType(Type type)
{
return
type.IsPrimitive ||
new Type[] {
typeof(Enum),
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
;
}
public object GetValueOrNull(object obj)
{
if (obj == null) return null;
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
var simpleTypes = properties.Where(t => IsSimpleType(t.PropertyType));
var nonValueTypes = properties.Where(p => !simpleTypes.Contains(p));
foreach (var child in nonValueTypes)
{
child.SetValue(obj, GetValueOrNull(child.GetValue(obj)));
}
return simpleTypes.All(z => z.GetValue(obj) == null) && nonValueTypes.All(z => z.GetValue(obj) == null) ? null : obj;
}
call it as follows
var student = new Student { Address = new Address { }, Exam = new Exam { Teacher = new Teacher() } };
var test = GetValueOrNull(student);
hope it helps :)

asp.net mvc c# listing categories recursively

I am trying to fill a list with my categories data.
My categories have cascade format. They are formatted recursively in my database.
Here is my model object.
public class CategoriesDTO
{
public int Id { get; set; }
public int Level { get; set; }
public string Name { get; set; }
public List<CategoriesDTO> Subs { get; set; }
}
So, in my business layer, i am trying to call this method like this:
DAO.Categories(0);
it will start with "Level==0" condition and then it will go on..
but i couldn't manage the Data Access Layer. I tried this:
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
//im stucked
}
return DTO;
}
}
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
item.Subs = ((PrmLevel + 1) <= MaxLevel) ? Categories(PrmLevel + 1) : null;
}
return DTO;
}
}
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
int CountSub = 0;
CountSub = DB.Categories.Where(x => x.Level == item.Id).ToList().Count();
if (CountSub!=0)
{
item.Subs = Categories(item.Id).ToList();
}
}
return DTO;
}
}

How to add a C# class object to an Entity table using LINQ

How to add a class object to an Enitity table using LINQ???The prescriber object is built from FullMasters Entity but I need to take that object and save it to EPCS_Prescriber table. I'm using linq.
public class UserRepository : IUserRepository
{
SourceofTruthEntities context = null;
ePrescribeEntities epcs = null;
public UserRepository()
{
context = new SourceofTruthEntities();
}
public List<FullMaster> SelectByNPI(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).ToList();
return data;
}
public List<FullMaster> SelectByName(string first, string last)
{
var data = context.FullMasters.Where(x => x.FirstName == first && x.LastName == last).ToList();
return data;
}
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
Prescriber p = new Prescriber
{
First = data.FirstName,
Last = data.LastName,
DEA = data.DEA,
License = data.LIC,
Life = data.Life_Hosp,
NPI = data.NPI
};
epcs.EPCS_Prescriber.Add(p);
epcs.SaveChanges();
}
}
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
EPCS_Prescriber e = new EPCS_Prescriber();
e.FirstName = data.FirstName;
e.LastName = data.LastName;
e.DeaNo = data.DEA;
e.License = data.LIC;
e.LifeNo = data.Life_Hosp;
e.NpiNo = data.NPI;
epcs.EPCS_Prescriber.AddObject(e);
epcs.SaveChanges();
}
Both your contexts need initialization:
public UserRepository()
{
context = new SourceofTruthEntities();
ePrescribeEntities epcs = new ePrescribeEntities();
}
Assuming epcs is the db context, then you should use epcs.EPCS_Prescriber.Insert(p) rather than AddObject(p)
Unless this isn't one database, surely you just need one context here? This should work for you:
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
Prescriber p = new Prescriber
{
First = data.FirstName,
Last = data.LastName,
DEA = data.DEA,
License = data.LIC,
Life = data.Life_Hosp,
NPI = data.NPI
};
context.EPCS_Prescribers.Add(p);
context.SaveChanges();
}
Make sure your ...DbContext class contains the following:
...
public System.Data.Entity.DbSet<YourNamespace.Models.Prescriber> EPCS_Prescribers { get; set; }
...
Check the names of the classes/entities if necessary.

Categories

Resources