I am using TPT strategy in my model.
Here's follow my Context:
public class MyTestContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Seller> Sellers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Person>().ToTable("Persons");
modelBuilder.Entity<Seller>().ToTable("Sellers");
base.OnModelCreating(modelBuilder);
}
}
And here my entities:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Seller : Person
{
public decimal Comissao { get; set; }
}
I am trying to reuse an already existent database register.
I have 2 CRUD's, one for Person, and another to Seller.
Scenario:
I have 10 persons in my database and 0 sellers, like this seed:
using (var ctx = new MyTestContext())
{
Func<int, Person> selector = i => new Person
{
Name = string.Format("Person {0}", i)
};
var persons = Enumerable.Range(1, 10).Select(selector);
ctx.Persons.AddRange(persons);
ctx.SaveChanges();
}
I want to add new Seller, but I want to reuse alredy existent Person in database:
using (var ctx = new MyTestContext())
{
var seller = new Seller { Id = 1, Comissao = 10 };
ctx.Sellers.Add(seller);
ctx.SaveChanges();
}
When Entity Framework save, he create a new Person, and a new Seller.
How can I use inheritance strategy and be able to use the Id that I assigned in Seller instead to create a new Person and use this new Id?
You can't use TPT for one to one-or-zero relationship.
Because to be able to have one to one-or-zero relationship. You need to have Seller property on Person and Person property on Seller.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Seller Seller { get; set; }
}
public class Seller : Person
{
public decimal Comissao { get; set; }
public Person Person { get; set; }
}
But above code doesn't work, because Seller property on Person will also be inherited to Seller and it will be considered as another relationship.
Solution
Have another abstract class called BasePerson
Both Person and Seller are derived from BasePerson
Use Table Per Concrete Class mapping
Here are the classes
public abstract class BasePerson
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Person : BasePerson
{
public Seller Seller { get; set; }
}
public class Seller : BasePerson
{
public decimal Comissao { get; set; }
public Person Person { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Seller> Sellers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures one to one-or-zero relationship.
modelBuilder.Entity<Seller>().HasRequired(x => x.Person).WithRequiredDependent(x => x.Seller);
}
}
Sample Usage
using (var context = new AppContext())
{
context.People.Add(new Person { Id = 1 });
context.SaveChanges();
}
using (var context = new AppContext())
{
context.Sellers.Add(new Seller { Id = 1 });
context.SaveChanges();
}
using (var context = new AppContext())
{
// Throws DBUpdateException, Person Id = 2 doesn't exist.
context.Sellers.Add(new Seller { Id = 2 });
context.SaveChanges();
}
Related
I have a parent object which has the collection of the child objects.
I want to save this data into the DB using EF Code First.
For some strange reason the parent object gets saved but the child objects not.
Here's my code:
class Person
{
public Person()
{
Operations = new ICollection<Operation> ();
}
public int Id { get; set; }
public virtual ICollection<Operation> Operations { get; set; }
}
public class Operation
{
public int Id { get; set; }
public string Data { get; set; }
public int PersonId { get; set; }
public virtual Person Person{ get; set; }
}
Here's thee DAL UpdateMethod:
public bool UpdatePerson entity)
{
//_context.Set<Person>().AddOrUpdate(entity);
var entityInDb = _context.Persons.Find(entity.Id);
_context.Entry(entityInDb).CurrentValues.SetValues(entity);
_context.Entry(entityInDb).State = EntityState.Modified;
return _context.SaveChanges() > 0;
}
And here's the BLL:
//Create a new person if it does not exist. If exists, add only the Operations:
//check if such the person exists in the DB
var person = Repository.Filter(p=>p.Id == Id)
.AsNoTracking().FirstOrDefault();
//if not, create a new record
if(person == null)
{
personNew = new Person{
...
};
bool res = Repository.Insert(personNew );
if (res)
{
//find newly created person
person = Repository.Filter(p => p.Id == ...)
.AsNoTracking().FirstOrDefault();
}
}
//Add Operations to the Person entity and Save them:
var op = obj.ToOperationModel();
op.PersonId = person.Id;
person.Operations.Add(op);
var res2 = Repository.Update(person);
There is no error, the parent object (Person) gets created, res2 returns true, but no Operation is added into the DB
Modify the update method like this;
public bool UpdatePerson entity)
{
//_context.Set<Person>().AddOrUpdate(entity);
var entityInDb = _context.Persons.Include(x => x.Operations).FirstOrDefault(x => x.Id == entity.Id);//Add Include for navigation property
_context.Entry(entityInDb).CurrentValues.SetValues(entity);
_context.Entry(entityInDb).State = EntityState.Modified;
_context.Entry(entityInDb).Property(x => x.Operations).IsModified = true; // Add this line
return _context.SaveChanges() > 0;
}
To establish relationship between the entities, you can use DataAnnotations attributes or Fluent Api. To associate relationships by using DataAnnotations, modify the Operation and Person entities like this;
public class Operation
{
public int Id { get; set; }
public string Data { get; set; }
[ForeignKey("Person")] // Add ForeignKey attribute
public int PersonId { get; set; }
public virtual Person Person{ get; set; }
}
public class Person
{
public Person()
{
Operations = new ICollection<Operation> ();
}
[Key] // Add key attribute
public int Id { get; set; }
public virtual ICollection<Operation> Operations { get; set; }
}
I'm new to automapper and I'm trying very simple map. I have the following class and I', trying to map personmodel to person.
It keeps failing at orders. Sorry if this silly but I could not figure out. I removed all properties on orders to see what is wrong
public class Person
{
public Person()
{
Orders = new List<Orders>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<Orders> Orders { get; set; }
}
public class Orders
{
public string OrderName { get; set; }
}
public class PersonModel
{
public PersonModel()
{
Orders = new List<OrderModel>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public string OrderName { get; set; }
}
I have the mapper defined as Mapper.Initialize(x => x.CreateMap<PersonModel, Person>());
The error that I get is:
Error mapping types.
Mapping types:
PersonModel -> Person
PersonModel -> Person
Type Map configuration:
PersonModel -> Person
PersonModel -> Person
Property:
Orders
Create Map between Orders and OrderModel.
Mapper.CreateMap<Orders, OrderModel>().ReverseMap();
I use this code for map a list<>
var result = NewMapperHelper.MapList<OrderModel, Orders>(YourSource);
and:
public static class NewMapperHelper
{
public static List<TDestination> MapList<TDestination, TSource>
(List<TSource> entity)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TSource, TDestination>().ReverseMap();
});
var _mapper = config.CreateMapper();
return _mapper.Map<List<TDestination>>(entity);
}
}
Hi I have a list of people that also have a List of contacts.
I'm trying to link my Contact Methods so that it'll be populated with the correct Person_Id
Example
Id Name Method Value Person_Id
1 John Phone 7777777777 2
2 Joan Phone 8888888888 8
3 Jack Phone 9999999999 9
Currently it displays Person_Id as all nulls, I believe I didn't create my ContactMethod Class correctly. If I can get help establishing a proper foreign key. I think that's my issue.
// Primary Keys -------------------------------------------------------
public int Id { get; set; }
// Associations -------------------------------------------------------
public virtual Person Person { get; set; }
// Fields -------------------------------------------------------------
public string Name {get; set;
public string Value { get; set; }
I populate the information through a migration script, here's a snippet
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person = person }));
Working Method not utilizing foreign keys.
ContactMethod Class
// Associations -------------------------------------------------------
public int? Person_Id { get; set; }
MigrationScript
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person = person.Id }));
I'm going to suppose that you have a model like this:
public class Contact
{
public int Id { get; set; }
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
Now,to achieve the escenario that you want, you need to configure a one-to many relationship between Contact and Person. There are two ways to do that, using Data Annotations or using Fluent Api. I'm going to use Fluent Api in this case. The easy way is override the OnModelCreating method of your Context to configure the relationship, for example, at this way:
public class YourContext : DbContext
{
//...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(p => p.Contacts)
.WithRequired(c => c.Person)
.Map(f => f.MapKey("Person_Id"));
}
}
As you can see, I'm specifying a PK that is not declared in your Contact class, that is the escenario that you want. Now with this configuration, you could do this, for example:
Person john=new Person(){Name = "John"};
var contactList = new List<Contact>() {new Contact(){Name = "Phone",Value = "555-444-333",Person = john},
new Contact() { Name = "email", Value = "john#gmail.com",Person = john}};
using (YourContext db = new YourContext())
{
db.Contacts.AddRange(contactList);
db.SaveChanges();
}
Update
If you want to do the same configuration using Data Annotations, your model would be like this:
public class Contact
{
[Key]
public int Id { get; set; }
[InverseProperty("Contacts")]
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class Person
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you want to use a FK related to Person in your Contact class, you could do this:
public class Contact
{
[Key]
public int Id { get; set; }
public int? Person_Id { get; set; }
[ForeignKey("Person_Id"), InverseProperty("Contacts")]
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
This way you can use directly the Person_Id FK in your second migration script:
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person_Id = person.Id }));
I am currently in the process of learning ASP.NET MVC 5 with EF 6. Right now I am stuck with declaring a foreign key with Fluent API and then seeding data to the declared tables.
Here's the code I have:
Models:
public class Person
{
public int Id { get; set; }
public ICollection<Anime> DirectedAnimes { get; set; }
}
public class Anime
{
public int Id { get; set; }
public int DirectorId { get; set; }
public Person Director { get; set; }
}
public class AnimeDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Anime> Animes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Anime>()
.HasRequired(a => a.Director)
.WithMany(p => p.DirectedAnimes)
.HasForeignKey(p => p.DirectorId);
base.OnModelCreating(modelBuilder);
}
}
Seeding data:
public class AnimeInitializer : DropCreateDatabaseAlways<AnimeDbContext>
{
protected override void Seed(AnimeDbContext context)
{
var persons = new List<Person>
{
new Person { Id = 1 },
new Person { Id = 2 }
};
var animes = new List<Anime>
{
new Anime { DirectorId = 1 },
new Anime { DirectorId = 2 }
};
persons.ForEach(p => context.Persons.Add(p));
context.SaveChanges();
animes.ForEach(a => context.Animes.Add(a));
context.SaveChanges();
}
}
But when I fetch Anime objects they have expected DirectorId values, but their Director properties arenull:
var director = (new AnimeDbContext()).Animes.First().Director; //null
var directorId = (new AnimeDbContext()).Animes.First().DirectorId; //1
Entity framework knows about the foreign key though, because adding new Anime {DirectorId = 3} in the Seed method results in a runtime error.
I am pretty sure that my mistake is very dumb and is caused by me not following examples precisely, but I've been struggling with this problem for a while already and still can't figure it out. I would very much appreciate some help.
Your navigation-property is not virtual and thus cannot be overridden by the DynamicProxy.
Change it like this:
public class Person
{
public int Id { get; set; }
public virtual ICollection<Anime> DirectedAnimes { get; set; }
}
public class Anime
{
public int Id { get; set; }
public int DirectorId { get; set; }
public virtual Person Director { get; set; }
}
This excerpt code successfully creates a many-many relationship with an explicit Junction table that has additional data within it.
PROBLEM: I want to be able to access Courses from Student and vice versa,
(therefore the commented virtual property. But if I uncomment it, it causes an error (see below))
If I don't explicitly create a junction table (no additional data), the virtual keyword works though as EF creates a junction table by convention.
QUESTION:How can I make Student access Courses without going through Enrollments? Or is that not possible? If it's not possible, then what's the best way to go about this?
(a beginner in EF and C#)
public class Student
{
[Key]
public int StudentId { get; set; }
public string StudentName { get; set; }
//public virtual Course Courses { get; set; }
}
public class Course
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
//public virtual Student Students { get; set; }
}
public class Enrollment
{
[Key]
public int EnrollmentId { get; set; }
public Student Student { get; set; }
public Course Course { get; set; }
public string Grade { get; set; }
}
public class ManyMany : DbContext, IContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public void Run()
{
Database.SetInitializer(new DropCreateDatabaseAlways<ManyMany1>());
this.Courses.Add(new Course() {CourseName = "English"});
this.SaveChanges();
}
}
WHEN I UNCOMMENT public virtual...
ERROR: "Unable to determine the principal end of an association between the types 'EF.Course' and 'EF.Student'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations."
public class Student
{
public virtual int StudentId { get; set; }
public virtual string StudentName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Course
{
public virtual int CourseId { get; set; }
public virtual string CourseName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public virtual int StudentId { get; set; }
public virtual int CourseId { get; set; }
public virtual string Grade { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
}
public class ManyMany : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasKey(student => student.StudentId);
modelBuilder.Entity<Course>()
.HasKey(course => course.CourseId);
modelBuilder.Entity<Enrollment>()
.HasKey(enrollment => new { enrollment.StudentId, enrollment.CourseId } );
modelBuilder.Entity<Student>()
.HasMany(student => student.Enrollments)
.WithRequired(enrollment => enrollment.Student)
.HasForeignKey(enrollment => enrollment.StudentId);
modelBuilder.Entity<Course>()
.HasMany(course => course.Enrollments)
.WithRequired(enrollment => enrollment.Course)
.HasForeignKey(enrollment => enrollment.CourseId);
}
}
Older question concerning this, answer and more info here: Entity Framework CodeFirst many to many relationship with additional information.
EDIT:
Usage example:
var context = new ManyMany();
var physicsCourse = new Course() { CourseName = "Physics" };
var mathCourse = new Course() { CourseName = "Math" };
var studentJohn = new Student() { StudentName = "John Doe" };
var studentJane = new Student() { StudentName = "Jane Doe" };
var physicsCourseEnrollmentJohn = new Enrollment() { Student = studentJohn, Course = physicsCourse };
var mathCourseEnrollmentJohn = new Enrollment() { Student = studentJohn, Course = mathCourse };
var physicsCourseEnrollmentJane = new Enrollment() { Student = studentJane, Course = physicsCourse };
context.Courses.Add(physicsCourse);
context.Courses.Add(mathCourse);
context.Students.Add(studentJohn);
context.Students.Add(studentJane);
studentJohn.Enrollments.Add(physicsCourseEnrollmentJohn);
studentJohn.Enrollments.Add(mathCourseEnrollmentJohn);
studentJane.Enrollments.Add(physicsCourseEnrollmentJane);
physicsCourse.Enrollments.Add(physicsCourseEnrollmentJohn);
mathCourse.Enrollments.Add(mathCourseEnrollmentJohn);
physicsCourse.Enrollments.Add(physicsCourseEnrollmentJane);
context.Enrollments.Add(physicsCourseEnrollmentJohn);
context.Enrollments.Add(mathCourseEnrollmentJohn);
context.Enrollments.Add(physicsCourseEnrollmentJane);
context.SaveChanges();
var johnsEnrollments = context.Students.Where(student => student.StudentId == studentJohn.StudentId).Single().Enrollments;
MessageBox.Show(string.Format("Student John has enrolled in {0} courses.", johnsEnrollments.Count));
var janesEnrollments = context.Students.Where(student => student.StudentId == studentJane.StudentId).Single().Enrollments;
MessageBox.Show(string.Format("Student Jane has enrolled in {0} courses.", janesEnrollments.Count));
Entity Framework can't automatically determine 'many-to-many' relations because they are expressed with the help of additional tables in SQL (in your case it is Enrollment table). You can specify mappings directly in OnModelCreating method:
public class YourDbContext : DbContext
{
....
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasMany(x => x.Courses).WithMany(x => x.Students)
.Map(m =>
{
m.ToTable("Enrollment"); // Relationship table name
m.MapLeftKey("StudentID"); // Name of column for student IDs
m.MapRightKey("CourseID"); // Name of column for course IDs
});
}
}
Also, take a note that if an entity have many other entities, use collection for relationship:
public class Student
{
....
public virtual ICollection<Course> Courses { get; set; } // Many courses
}
public class Course
{
....
public virtual ICollection<Student> Students { get; set; } // Many students
}