How to define default values with Entity Framework C#? - c#

Models in question:
public class EmployeeType
{
public int employeeTypeId { get; set; }
public string typeName { get; set; }
public virtual List<Employee> Employees { get; set; }
}
public class Employee
{
public int employeeId { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string password { get; set; }
public int employeeTypeId { get; set; }
public virtual EmployeeType EmployeeTypes { get; set; }
public virtual List<PhoneNumber> PhoneNumbers { get; set; }
}
At the moment i am adding different values through:
db.EmployeeType.Add(new EmployeeType
{
typeName = "Administrator"
});
db.EmployeeType.Add(new EmployeeType
{
typeName = "Seller"
});
db.EmployeeType.Add(new EmployeeType
{
typeName = "Accountant"
});
But in a case when i have to check if the user is an administrator or etc. i have to check through linq query and find out if the id is equal to the id in the Employee table.
How could i define default records in the EmployeeType model and not add the values through multiple .Add lines, so that i could use something like this:
if (db.Employee.FirstOrDefault(o => ...).servictypeId
== EmployeeType.Administrator)
{
}

The best way to handle this would be to convert employeetypeId into an enum in EF. You can easily achieve this by "converting" the field into an enum within the EDMX. Just right click on the property in the edmx model design screen and click "convert to enum".
Firstly though, you need to create an Enum, Called UserRole :-
enum UserRole : int
{
Administrator = 1,
Manager = 2,
Client = 3
}
Now, when you want to make new UserRole's you add them in your Enum.
When it comes to adding a new user, you simply do the following :-
new User object1 { Name = "Fred", UserRole = UserRole.Client};
dbContext.Save(object1);
EF will know to save the employeeTypeId as 3 in the database.
The advantage gets better, because now you can say :-
if(User.UserRole == UserRole.Adminstartor)
{
//Do Stuff
}

Related

Automapper one-to-many relationship

I have a method that creates a Course. The Course retains the Teacher and their id. But after adding the Course, TeacherID has the value but Teacher has null. I think the problem is in the mapping. CourseAddRequest only has a teacherID, how can I add a Teacher?
AddCourse:
public CourseResponse AddCourse(CourseAddRequest courseAddRequest, Guid teacherId)
{
var teacher = _uniDbContext.Teachers
.Include(t => t.Courses)
.SingleOrDefault(t => t.Id == teacherId);
if (teacher == null)
throw new Exception("User doesn't exist");
var course = _mapper.Map<Course>(courseAddRequest);
teacher.Courses.Add(course);
_uniDbContext.Teachers.Update(teacher);
_uniDbContext.Courses.Update(course);
_uniDbContext.Courses.Add(course);
_uniDbContext.SaveChanges();
return _mapper.Map<CourseResponse>(course);
}
Course:
public class Course : BaseEntity
{
public string Header { get; set; }
public string Description { get; set; }
public Guid TeacherId { get; set; }
public Teacher Teacher { get; set; }
public List<Student> StudentsOnCourse { get; set; } = new List<Student>();
}
CourseResponse:
public class CourseResponse
{
public Guid Id { get; set; }
public string Header { get; set; }
public string Description { get; set; }
public TeacherResponse Teacher { get; set; }
public Guid TeacherId { get; set; }
public IEnumerable<StudentResponse> Students { get; set; }
}
CourseAddRequest:
public class CourseAddRequest
{
public string Header { get; set; }
public string Description { get; set; }
public Guid TeacherId { get; set; }
}
CourseProfile:
public class CourseProfile : Profile
{
public CourseProfile()
{
CreateMap<CourseAddRequest, Course>();
CreateMap<Course, CourseResponse>();
}
}
TeacherProfile:
public class TeacherProfile : Profile
{
public TeacherProfile()
{
CreateMap<TeacherAddRequest, Teacher>();
CreateMap<Teacher, TeacherResponse>();
}
}
You guessed right. When you execute the mapping from CourseAddRequest to Course it has no Teacher Therefore the Course teach will be null.
var course = _mapper.Map<Course>(courseAddRequest);
Assuming you're using EntityFramework or another ORM it'll be able to do the insertion correctly due to the existence of the Teacher that you've referenced in the Course via TeacherId property.
And while you add the Course to the teacher's in line 11 of your method, This still leaves the Teacher property null in course. As a result, when you map it to CourseResponse you get null.
There's two way to fix this, First, you can add the teach to your course object So the mapper finds the teacher before mapping to CourseResponse in the return statement.
course.Teacher = teacher;
Or map the teacher object directly to the response.
var courseResponse = _mapper.Map<CourseResponse>(course);
courseResponse.Teacher = _mapper.Map<TeacherResponse>(teacher);
return courseResponse;

Checking conditions using Automapper

I am using Automapper. In that, I mapped the DTO with the database table. In that one, I need to check one condition and then take the value.
CreatedBy = mapper.Map<UserProperties>((from createdByUser in context.persons.Where(x => x.IsActive && x.Id == notes.CreatedBy) select createdByUser).FirstOrDefault())
This is my code.
User Properties Class:
public string DisplayName { get; set; }
public int Id { get; set; }
public bool IsUser { get; set; }
public int NotesCount {get;set;}
Persons
public string DisplayName { get; set; }
public int Id { get; set; }
public int RoleId{ get; set; }
public int NotesCount {get;set;}
public string Notes{get;set;}
public string Comments {get;set;}
The below code is the automapper configuration in the start up file.
Mapping Profile Class
In persons, have the field roleId. I need to assign the values for the IsUser field in the User properties class by checking the condition like RoleId field in Persons is equal to 2.
How to Check the condition using the automapper?
Automapper Version: 9.0.0
You need to add a ForMember clause to your mapping to add the condition - here's a working example (which took longer than it should have, because you posted an image of your code instead of the actual code. This is why on SO you should always post code, not images.)
void Main()
{
var mapperConfig =
new MapperConfiguration(mc => mc.AddProfile<MappingProfile>());
var mapper = mapperConfig.CreateMapper();
var notAUser = new Persons { RoleId = 1};
var isAUser = new Persons { RoleId = 2};
var shouldBeNotAUser = mapper.Map<UserProperties>(notAUser);
var shouldBeAUser = mapper.Map<UserProperties>(isAUser);
Console.WriteLine(shouldBeNotAUser.IsUser);
Console.WriteLine(shouldBeAUser.IsUser);
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Persons, UserProperties>()
.ForMember(destination => destination.IsUser,
options => options.MapFrom(src => src.RoleId == 2));
}
}
class UserProperties
{
public string DisplayName { get; set; }
public int Id { get; set; }
public bool IsUser { get; set; }
public int NotesCount { get; set; }
}
class Persons
{
public string DisplayName { get; set; }
public int Id { get; set; }
public int RoleId { get; set; }
public int NotesCount { get; set; }
public string Notes { get; set; }
public string Comments { get; set; }
}
Output:
False
True
However
Your mapping configuration code should not have to 'know' about what RoleID indicates a user. Your Person class should be where that knowledge is held, so that should have either an IsUser() method or a get-only IsUser property (with the NotMapped attribute) which returns RoleId == 2: in the former case you would still need ForMember but in the latter case you wouldn't, though if you do map back from UserProperties to Persons you would need something there to handle it - again, this should be in the Persons class and not in the mapper config. Maybe SetAsUser() that sets the RoleId.

WPF Entity Framework Foreign key won't load it's value in a DataGrid

I am using Entity Framework code first with fluent API I have an items table with foreign keys from users and units tables
but when I load the table to ObservableCollection then bind it to a datagrid the table normal column load it's data normally into the datagrid excpet for the foreign keys which show nothing but when i insert a break point to see the data inside the ObservableCollection I can see that every thing from Users and Units table is there
private void MainContentsWindow_ContentRendered(object sender, EventArgs e)
{
using (var db2 = new DataContext())
{
var AllItems2 = new ObservableCollection<Model.Items.Item>(db2.Items);
ItemsDataGrid.ItemsSource = AllItems2;
}
}
Users
public class User
{
public User()
{
Id = Guid.NewGuid();
IsActive = false;
}
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public UserGroup Group { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Items.Item> Items { get; set; } = new List<Items.Item>();
}
public enum UserGroup
{
Administrator = 1,
User,
Unknown
}
base
public class NormalBaseModel : CommonBase
{
public NormalBaseModel()
{
Id = new Guid();
CreateDate = DateTime.Now;
EditDate = null;
}
public Guid Id { get; set; }
public string Notes { get; set; }
public virtual User CreateBy { get; set; }
public DateTimeOffset? CreateDate { get; set; }
public virtual User EditBy { get; set; }
public DateTimeOffset? EditDate { get; set; }
}
items
public class Item : NormalBaseModel
{
public string NameAr { get; set; }
public string NameEn { get; set; }
public int? ManualId { get; set; }
public string Barcode { get; set; }
public byte?[] Image { get; set; }
public virtual Unit Unit { get; set; }
public string MadeIn { get; set; }
public bool IsSerail { get; set; }
public bool IsExpire{ get; set; }
}
Here is a test project on Github
https://github.com/ahmedpiosol/psychic-parakeet.git
https://imgur.com/a/zimd4
When you load your items via EF it needs to create new instances of User and Item. Behind the scenes, EF will call the constructor for each new instance. Your problem is in your constructors:
public User()
{
Id = Guid.NewGuid(); // <- here
}
Your constructor reassigns a new ID each time an instance is created, this will break the referential integrity and cause all sorts of other problems.
Your code doesn't know the difference between creating a new User and recreating a User instance from the database.
I suggest removing the assignments from inside your constructor and placing this either in a static Create method or place wherever you are creating a new User or Item.
p.s. WPF is irrelevant to your problem here.
Fluent API needs to specify foreign key in code, something like
modelBuilder.Entity<Items>()
.HasRequired(o => o.User)
.WithMany(c => c.Items)
.HasForeignKey(o => o.UserId);

Getting data from multiple tables (C# Entity), linked by foreign keys, converted to anonymous object

I'm new to Entity/Linq/Lambda and I have the following problem:
I have a web application which provides a JSON Api through ASP.NET MVC. The database is MSSQL and I use the C# entity framework as data access layer.
When getting data from a single table, I need to convert this to an anonymous object, before I can convert it to JSON to avoid a circular reference error.
This is a simplified example, but take these tables for example:
If I simply want to return all the translators in JSON, this is how I go about it:
DBEntities db = new DBEntities();
var data = db.Translator.Select(x => new
{
TranslatorID = x.TranslatorID,
FirstName = x.FirstName,
LastName = x.LastName,
Email = x.Email,
Referenced = x.TranslatorLanguage.Count != 0
});
return Json(data, JsonRequestBehavior.AllowGet);
The generated Model classes by Entity would look something like this:
public partial class Translator
{
public Translator()
{
this.TranslatorLanguage = new HashSet<TranslatorLanguage>();
}
public int TranslatorID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage { get; set; }
}
public partial class TranslatorLanguage
{
public int TranslatorLanguageID { get; set; }
public int SourceLanguageID { get; set; }
public int TargetLanguageID { get; set; }
public virtual Translator Translator { get; set; }
public virtual Language Language1 { get; set; }
public virtual Language Language2 { get; set; }
}
public partial class Language
{
public Language()
{
this.TranslatorLanguage = new HashSet<TranslatorLanguage>();
this.TranslatorLanguage1 = new HashSet<TranslatorLanguage>();
}
public int TranslatorLanguageID { get; set; }
public int SourceLanguageID { get; set; }
public int TargetLanguageID { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage1 { get; set; }
}
But I would like to be able to return a JSON with all the translators where each Translator-object contains an array with the TranslatorLanguage entries, and for each source- and target language to have it's varchar code and description values.
I have no idea how to go about this,
Thanks in advance.
The same way you project (select) Translator to anonymous type, you can project TranslatorLanguage to a nested anonymous type list.
Since you have defined the necessary navigation properties, it's quite easy - all you need is to follow the navigation properties (i.e. navigate) inside the query like if they were objects:
var data = db.Translator.Select(t => new
{
TranslatorID = t.TranslatorID,
FirstName = t.FirstName,
LastName = t.LastName,
Email = t.Email,
Languages = t.TranslatorLanguage.Select(tl => new
{
SourceCode = tl.Language1.Code,
SourceDescription = tl.Language1.Description,
TargetCode = tl.Language2.Code,
TargetDescription = tl.Language2.Description,
}).ToList()
}).ToList();

How can I tell what subclass was saved?

I have a model with 2 subclasses:
public class User
{
public string eRaiderUsername { get; set; }
public int AllowedSpaces { get; set; }
public ContactInformation ContactInformation { get; set; }
public Ethnicity Ethnicity { get; set; }
public Classification Classification { get; set; }
public Living Living { get; set; }
}
public class Student : User
{
public Student()
{
AllowedSpaces = AppSettings.AllowedStudentSpaces;
}
}
public class OrganizationRepresentative : User
{
public Organization Organization { get; set; }
public OrganizationRepresentative()
{
AllowedSpaces = AppSettings.AllowedOrganizationSpaces;
}
}
public class ContactInformation
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string CellPhoneNumber { get; set; }
}
public enum Ethnicity
{
AfricanAmerican,
AmericanIndian,
Asian,
Black,
Hispanic,
NativeHawaiian,
NonResidentAlien,
White
}
public enum Classification
{
Freshman,
Sophomore,
Junior,
Senior,
GraduateStudent
}
public enum Living
{
OnCompus,
OffCampus
}
This is (mostly) saving fine using these initializers:
var students = new List<Student>
{
new Student{ eRaiderUsername="somestudent", ContactInformation=new ContactInformation{FirstName="Some", LastName="Student", EmailAddress="student#example.com", CellPhoneNumber="1234567890"}, Classification=Classification.Junior, Ethnicity=Ethnicity.Hispanic, Living=Living.OffCampus }
};
students.ForEach(s => context.Users.Add(s));
context.SaveChanges();
var orgReps = new List<OrganizationRepresentative>
{
new OrganizationRepresentative{ eRaiderUsername="somerep", ContactInformation=new ContactInformation{FirstName="Some", LastName="Representative", EmailAddress="orgrep#example.com", CellPhoneNumber="0987654321"}, Classification=Classification.Freshman, Ethnicity=Ethnicity.White, Living=Living.OnCompus, Organization=context.Organizations.Find(1) }
};
orgReps.ForEach(o => context.Users.Add(o));
context.SaveChanges();
None of the enums are saving (advice on this would be awesome too). But everything else is saving fine.
I have noticed Entity has added a Discriminator column with the subclass names. How do I use this to query only students, only organization reps, or just tell if the current object is a student or organization rep in a controller or view?
The discriminator column is used internally by EF to determine the type of object to instantiate.
For example you could query for a student directly. context.Set<Student>.Find(id). The same is true for an org rep. Or you could query for any user context.Set<User>.Find(id).
If you query for a student, but pass an org rep's ID, then EF will return null, because the ID doesn't belong to a student.

Categories