Entity Framework map attribute to a new table - entity-framework

I wanted to know if there is any way to map an attribute such as the phone number from a User table and create a new table PhoneNumber where the only columns will be the UserID which will be the FK and PK for this new table and Phone attribute, which is the one I want to get from the User table.
The reason I want to do this is to reflect in the database all phone numbers from each users, which can be many per user.
This is how my class looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
namespace ProvEvents.Models
{
[Table("User")]
public class User
{
[Required]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity), Key()]
public int UserID{ get; set; }
[Required]
[StringLength(50)]
public string UserName{ get; set; }
[Required]
[DataType(DataType.Password)]
[MinLength(8), MaxLength(12)]
public string Password { get; set; }
[Required]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime RegisterDate { get; set; }
[Required]
[Phone]
[RegularExpression(#"^([0-9]{10})$"]
public string PhoneNumber{ get; set; }
}
}

Related

How to insert data into a table with composite key from Multiple Tables in ASP.NET MVC

Answer to this question is found at here
Having three tables:
Database diagram is here
Book class:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class Books
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Books()
{
UserBookComments = new HashSet<UserBookComments>();
}
[Key]
public int BookID { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
[Required]
[StringLength(255)]
public string Category { get; set; }
[Column(TypeName = "date")]
public DateTime PublishDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserBookComments> UserBookComments { get; set; }
}
User class:
public partial class Users
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Users()
{
UserBookComments = new HashSet<UserBookComments>();
}
[Key]
public int UserID { get; set; }
[Required]
[StringLength(255)]
public string UserName { get; set; }
[Required]
[StringLength(255)]
public string Password { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserBookComments> UserBookComments { get; set; }
}
And the UserBookComments class:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class UserBookComments
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserID { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BookID { get; set; }
public int? Comments { get; set; }
public virtual Bookss Bookss { get; set; }
public virtual Users Users { get; set; }
}
The table "Books" is an already saved database. Each user can comment for each book and I want a view model that holds all the data from books with their comments.
The primary key on UserBookComment would be composite, on UserID and BookID.
I used EF Code First and my DBModel context class looks so:
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class DbModel : DbContext
{
public DbModel()
: base("name=DbModel")
{
}
public virtual DbSet<Books> Books { get; set; }
public virtual DbSet<UserBookComments> UserBookComments { get; set; }
public virtual DbSet<Users> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Books>()
.Property(e => e.Category)
.IsUnicode(false);
modelBuilder.Entity<Books>()
.HasMany(e => e.UserBookComments)
.WithRequired(e => e.Books)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Users>()
.HasMany(e => e.UserBookComments)
.WithRequired(e => e.Users)
.WillCascadeOnDelete(false);
}
}
I wonder how to save comments and display whole list of [title, category, publish date and comments] using a ViewModel class?
As you asked in the comments, I have provided the way to insert a record into the UserBookComment table by adding a method into the BookEntities class.
public partial class BooksEntities : DbContext
{
public virtual DbSet<Books> Books { get; set; }
public virtual DbSet<UserBookComment> UserBookComments { get; set; }
public virtual DbSet<Users> Users { get; set; }
public void AddComment(int userId, int bookId, string comment)
{
var userBookComment = new UserBookComment()
{
UserId = userId,
BookId = bookId,
Comment = comment
};
this.AddComment(userBookComment);
}
public void AddComment(UserBookComment userBookComment)
{
this.UserBookComment.Add(userBookComment);
this.UserBookComment.SaveChanges();
}
}
I assumed based on your provided information that your UserBookComment class looked like this
public class UserBookComment
{
public int UserId { get; set; }
public int BookId { get; set; }
public string Comment { get; set; }
}
Looks like you are using entity framework. To create a composite key, just at the Key attribute on both properties. To control the key order use the column atribute. Referencing should be fixed automaticly using the names in the model, or try googling on entity framework foreign key. I'm sorry I don't have the link right now.
For the viewmodel, just don't foget to use include in the statement.

Error : NBTSol.Models.ContactDbContext.Persons is inaccessible due to its protection level -C#

i am using entity framework in windowformapplication , when i call "db.Persons.Add()" it doesn't show refernce for Persons and gives the error :
Error : NBTSol.Models.ContactDbContext.Persons is inaccessible due to its protection level
even everything is public,
then why?
Person Model :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NBTSol.Models
{
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long PersonId { get; set; }
[Required(ErrorMessage = "First Name is Required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email is Required")]
public string Email { get; set; }
[Required(ErrorMessage = "Gender is Required")]
public string Gender { get; set; }
[Required(ErrorMessage = "Address is Required")]
public string Address { get; set; }
[Required(ErrorMessage = "City is Required")]
public string City { get; set; }
[Required(ErrorMessage = "State is Required")]
public string State { get; set; }
[Required(ErrorMessage = "PostalCode is Required")]
public string PostalCode { get; set; }
[Required(ErrorMessage = "Country is Required")]
public string Country { get; set; }
[Required(ErrorMessage = "Contact Number is Required")]
public string ContactNumber { get; set; }
[Required(ErrorMessage = "CNIC is Required")]
public string CNIC { get; set; }
public string OrganizationName { get; set; }
public string Designation { get; set; }
public string Salary { get; set; }
public string Description { get; set; }
public Nullable<bool> IsClient { get; set; }
public DateTime EntryDateTime { get; set; }
public Nullable<bool> IsDeleted { get; set; }
}
}
My Context Class :
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NBTSol.Models
{
public class ContactDbContext :DbContext
{
public ContactDbContext() : base( "name=DefaultConnection") { }
DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure domain classes using Fluent API here
base.OnModelCreating(modelBuilder);
}
}
}
main form :[here is the error when i try to call "db.Persons.Add()"]
using NBTSol.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NBTSol
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private void Main_Load(object sender, EventArgs e)
{
ContactDbContext db = new ContactDbContext();
Person person = new Person();
db.Persons.Add(person);
}
}
}
Make Person property public in your db context as following and it will work.
public DbSet<Person> Persons { get; set; }
Replace your ContactDbContext class with following code:
public class ContactDbContext: DbContext {
public ContactDbContext(): base("name=DefaultConnection") {}
public DbSet < Person > Persons {
get;
set;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//Configure domain classes using Fluent API here
//Configure table name
modelBuilder.Entity<Persons>()
.ToTable("Persons ");
base.OnModelCreating(modelBuilder);
}
}

entity framework cannot get results of a many to many relationship

I was trying to explore Code First today when I ran into a weird situation. I created 3 tables and entity framework created a 4th table for my many-to-many relationship. the code is as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Pizza
{
public int Id { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public string Description { get; set; }
public int IngredientId { get; set; }
public virtual IList<Bottom> Bottoms { get; set; }
public virtual IList<Ingredient> Ingredients { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Bottom
{
public int Id { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public virtual Pizza Pizzas { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Ingredient
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual Pizza Pizzas { get; set; }
}
}
The code of my dbcontext is as follows:
namespace pizza_test
{
using models;
using System;
using System.Data.Entity;
using System.Linq;
public class PizzaContext : DbContext
{
public PizzaContext()
: base("name=PizzaContext")
{
}
public DbSet<Pizza> Pizza { get; set; }
public DbSet<Ingredient> Ingredient { get; set; }
public DbSet<Bottom> Bottom { get; set; }
}
}
However when I try to query the database in my program.cs I am not allowed to call item.Ingredients.Name .
class Program
{
static void Main(string[] args)
{
var _context = new PizzaContext();
var firstPizza = _context.Pizza.Include(p => p.Ingredients).ToList();
foreach (var item in firstPizza)
{
Console.WriteLine(item.Ingredients.Name);
}
}
}
Which makes me kind of lost since i thought this was the power of ORMs. Did i forget something here? Also when i debug it in Visual Studio I can find all the related data of Ingredients including name, description and id. Thanks for any help provided.

How should I model one-to-many relation with EntityFramework 4 to work with migration and ASP MVC

I'm trying to use ASP MVC 4 and Entity Framework 4 to create pretty simple web site.
I need to use the migration feature because I will deploy the application to shared hosting (GoDaddy) and I don't want to manually change tables on each change.
What is the correct way to model one-to-many relations? Using the other entity type or the other entity's primary key type?
When I use the other entity type, which is preferred because it keeps the model cleaner, the migration tools worked but the scaffolding of ASP MVC did not. Even when I've manually add drop down to select the other entity ASP MVC did not parse the request right and did not set the other entity property.
This is the two options:
Option1: Use other entity type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public Tenant Tenant { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
Option 2: use primary key type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public string TenantID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
I've create MVC controller with scaffolding for the Survey entity in my ASP MVC 4 project. It create the CRUD controller and views. In the view it did not put any field for the Tenant.
After I've add it myself the method Create(Tenant tenant) was called but the Tenant field that was sent by the HTML form did not get parsed by MVC and did not set the Tenant field of the Survey entity.
Ido
These look like you are mapping one-to-one relationships and not one-to-many. If one Survey can have multiple Tenants then:
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
public virtual Survey Survey {get; set;}
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
public virtual ICollection<Tenant> Tenant {get; set;}
}
I've found this series of posts which explain how to make EF models so that they will work with both EF and ASP MVC.
The idea is to have both "plain" reference type and strong reference type.
public class Team
{
public int TeamId { get; set; }
// ... other Team properties go here
// Each Team has an optional "next opponent" which is another Team
public int? NextOpponentId { get; set; }
[ForeignKey("NextOpponentId")] public virtual Team NextOpponent { get; set; }
// Each Team also has a required Manager and Administrator, both of which are people
public int ManagerId { get; set; }
public int AdministratorId { get; set; }
[ForeignKey("ManagerId")] public virtual Person Manager { get; set; }
[ForeignKey("AdministratorId")] public virtual Person Administrator { get; set; }
}

dbcontext loading related entities using select(), include() and where() not working

I have the following relationship between the entities.
Company 1 ---* Appointments *---1 Employee
I have the .net asp membership in a separate database. Whenever a user is created it can be assigned to companies, employees, or administrators roles.
in the Index action of my Company Controller, I check the logged in user's role. Based on the role, I make different linq query. For example, administrators can get list of all companies, companies can get list of company which has a username property (string) same as the User.Identity.Name. For both of administrators and companies role, it is working fine.
For the employees role, I want to load all the companies that are related to the current employee. I am having hard time to compose a linq query that does this job.
i tried
var companies = db.Companies.Include(c => c.Appointments.Select(a=>a.Employee).Where(e=>e.Username.ToLower() == this.User.Identity.Name.ToLower())).ToList();
to which i get this error
"The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path"
Here are the source code,
CompanyController
[Authorize]
public class CompanyController : Controller
{
private MyDBContext db = new MyDBContext();
//
// GET: /Company/
public ViewResult Index()
{
var viewModel = new CompanyIndexViewModel();
if (Roles.IsUserInRole("administrators")) {
viewModel = new CompanyIndexViewModel { Companies = db.Companies.ToList() };
}
else if (Roles.IsUserInRole("companies")) {
viewModel = new CompanyIndexViewModel { Companies = db.Companies.Where(c => c.Username.ToLower().Equals(this.User.Identity.Name.ToLower())).ToList() };
}
else if (Roles.IsUserInRole("employees")) {
var companies = db.Companies.Include(c => c.Appointments.Select(a=>a.Employee).Where(e=>e.Username.ToLower() == this.User.Identity.Name.ToLower())).ToList();
viewModel = new CompanyIndexViewModel { Companies = companies.ToList() };
}
return View(viewModel);
}
...
Models
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace TorontoWorkforce.Models
{
public class Company
{
public int CompanyId { get; set; }
[Required]
public string Username { get; set; }
[Display(Name="Company Name")]
[Required]
public string Name { get; set; }
[UIHint("PhoneNumber")]
public string Phone { get; set; }
[DataType(DataType.Url)]
public string Website { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public AddressInfo AddressInfo { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
public virtual ICollection<Appointment> Appointments { get; set; }
public Company(){
this.AddressInfo = new AddressInfo();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace TorontoWorkforce.Models
{
public class Appointment
{
public int AppointmentId { get; set; }
[Required]
[UIHint("DateTime")]
[Display(Name="Appointment Date")]
public DateTime? DateOfAppointment { get; set; }
[Required]
public int CompanyId { get; set; }
[Required]
public int EmployeeId { get; set; }
[Required]
[UIHint("MultilineText")]
[Display(Name = "Appointment Summary")]
public string Description { get; set; }
[Display(Name="Allocated No of Hours")]
public decimal NoOfHoursWorked { get; set; }
public virtual Company Company { get; set; }
public virtual Employee Employee { get; set; }
public virtual ICollection<AppointmentLine> AppointmentLines { get; set; }
public Appointment() {
//this.AppointmentLines = new List<AppointmentLine>();
this.DateOfAppointment = DateTime.Now;
}
[NotMapped]
[Display(Name="Actual No of Hours")]
public decimal ActualHoursWorked {
get
{
decimal total = 0;
foreach (var jobline in this.AppointmentLines)
{
total = total + jobline.TimeSpent;
}
return total;
}
}
}
public class AppointmentLine
{
public int AppointmentLineId { get; set; }
[UIHint("MultilineText")]
[Required]
public string Description { get; set; }
[Display(Name="Time Spent")]
[DataType(DataType.Duration)]
public decimal TimeSpent { get; set; }
public int AppointmentId { get; set; }
public virtual Appointment Appointment { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace TorontoWorkforce.Models
{
public class Employee: TorontoWorkforce.Models.Person
{
public int EmployeeId { get; set; }
[Required]
public string Username { get; set; }
[Display(Name="Date Hired")]
public DateTime? DateHired { get; set; }
[Required]
public string Position { get; set; }
public virtual ICollection<Appointment> Appointments { get; set; }
public Employee() {
this.DateHired = DateTime.Now;
}
}
}
If you want to get companies which have appointment with selected employee you don't need to use Include. Include is for instructing EF to load all appointments related to the company (and it doesn't support filtering). Try this:
string userName = this.User.Identity.Name.ToLower();
var companies = db.Companies.Where(c => c.Appointments.Any(a =>
a.Employee.Username.ToLower() == userName)).ToList();
I think you just have an end parentheses in the wrong place. You need one more after "a => a.Employee" and one less after "this.User.Identity.Name.ToLower()));"
Try this code:
var companies = db.Companies.Include(c => c.Appointments.Select(a=>a.Employee)).Where(e=>e.Username.ToLower() == this.User.Identity.Name.ToLower()).ToList();
Edit:
You should also be able to use the standard string include method:
var companies = db.Companies.Include("Appointments.Employee").Where(e=>e.Username.ToLower() == this.User.Identity.Name.ToLower()).ToList();