Cross context communication EF Core - c#

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?

Related

Iterate through nested collection and form org structure with Parent

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.

trying to add different types inside if statement

I have a below method where I am loop through the list of id's and getting the data from db based on id and then creating the material and then adding to material list
public Construction AddToOsm(Model model, APIDbContext dbContext)
{
var construction = new Construction(model);
var surfaceType = dbContext.IntendedSurfaceTypes.SingleOrDefault(s => s.Id == this.SurfaceTypeId);
construction.setName(surfaceType?.Name);
using var materials = new MaterialVector();
var fenestrationMaterialById = new Dictionary<Guid, FenestrationMaterial>();
var opaqueMaterialById = new Dictionary<Guid, StandardOpaqueMaterial>();
foreach (var materialId in this.LayerIds.Where(i => i != default))
{
var opaqueMaterial = dbContext.OpaqueMaterials.SingleOrDefault(o => o.Id == materialId);
if (opaqueMaterial != default)
{
materials.Add(opaqueMaterialById.GetOrCreate(opaqueMaterial.Id, () => opaqueMaterial.AddToOsm(model)));
}
else
{
var glazingMaterial = dbContext.GlazingMaterials.SingleOrDefault(o => o.Id == materialId);
if (glazingMaterial != default)
{
materials.Add(fenestrationMaterialById.GetOrCreate(glazingMaterial.Id, () => glazingMaterial.AddToOsm(model)));
}
else
{
var glazingSimpleMaterial = dbContext.SimpleGlazingMaterials.SingleOrDefault(s => s.Id == materialId);
if(glazingSimpleMaterial != default)
{
materials.Add(fenestrationMaterialById.GetOrCreate(glazingSimpleMaterial.Id, () => glazingSimpleMaterial.AddToOsm(model)));
}
else
{
var gasGlazingMaterials = dbContext.GasGlazingMaterials.SingleOrDefault(a => a.Id == materialId);
if(gasGlazingMaterials != default)
{
materials.Add(fenestrationMaterialById.GetOrCreate(gasGlazingMaterials.Id, () => gasGlazingMaterials.AddToOsm(model)));
}
}
}
}
}
construction.setLayers(materials);
return construction;
}
I am looking a way to avoid this much of if-else statements mainly refactoring this but could not find a way to do. Could any one please suggest any idea on how to achieve the same.
Thanks in advance.
update: sample entity structure
public class GasGlazingMaterial : ISourceOfData, IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
[ForeignKey("SourceOfData")]
public Guid? SourceOfDataId { get; set; }
public virtual CodeStandardGuideline SourceOfData { get; set; }
......
.....
}
A simple fix would be to "continue" after each materials.add. This would mean you dont need to embed the rest in an else

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