I have tried looking for answers and though I am sure my question has been answered somewhere, I failed to get the answer.
I have two tables independent but related. I am dealing with my classes in EF.
I want to create a model in c# with none or one to many relationship.
How do I do that in code?
Below are my two models:
public class adult
{
[Key]
public int ID { get; set; }
[StringLength(50, ErrorMessage = "{0} cannot exceed {1} characters")]
public string LastName { get; set; }
public string Initials { get; set; }
public string Title { get; set;}
public string Address1 {get; set;}
public string Address2 {get; set;}
}
Below is the second model with many 'children'.
public class children
{
[Key]
public int childNo { get; set; }
public string favourite { get; set; }
**public int childID { get; set; }**
[Required]
[StringLength(50, ErrorMessage = "{0} cannot exceed {1} characters")]
public string colour { get; set; }
public DateTime DateReceived { get; set; }
public DateTime DatePaid { get; set; }
}
I am interested in a none or one to many relationship i.e I can have an adult without a child.
The childID in the children class corresponds to the ID in adult class. We have none or one ID in class matching with many childID in the children class. How do I model this in my class? I am also interested in displaying those mapped values in one view and edit, create and save the changes.
The answer is that Entity Framework Code first creates relationships such as one-to-one, one-to-many, and many-to-many itself only by some coding.
And by using Virtual Properties you can navigate between two models,so add code bellow to your first model :
public virtual ICollection<children> children_{ get; set;}
Then into the second model add this :
[Required]
public virtual adult adult_{ get; set;}
This represents one-to-many relationship between children and adult. [Required] Ensures that any child surely has a parent(adult) so it cannot be null.
Notice that there's no need to adding relational ID of the other models in current model. So using this part of code is not generally used:
public int childID {get; set;}
If you want to use the above code in 2nd model,No Navigation will appear in DB. must mount it manually in controller and navigate tables in your codes like controller.But, by using virtual properties EF generates relation key like this to navigate between models.After using this technique, see your tables, you will find a column named childID in adult table created by EF automatically.
As simple as calling a member. For example when you want to know which children are related to specific adult you may use a code like this:
var childno = db.adult.child.ChildNo;
or for searching :
string favor = db.adult.child.Find(input.id).Select(s => s.favourite).ToString();
child model now is a member of adult, and adults may have some children in their ICollection of child.
Related
I wonder if I could get some help with the following. I'm retrieving set of data as follows using EF.
var booking = this.GetDbSet<Booking>().Include(c => c.BookingProducts.Select(d => d.Product.PrinterProducts.Select(e => e.ProductPrices))).Single(c => c.BookingId == bookingId)
Within a PrinterProduct there is a foreign key PrinterId for an additional entity Printer. With the Booking Entity I also have PrinterId also linked by foreign key to the additonal entity Printer.
What I'm hoping to do is retrieve only the PrinterProduct relating to the PrinterId held in the booking entity rather that all the PrinterProducts as in my code. I've tried to use Join but have tied myself in knots!
Grateful for any help!
Edit:
Object structure:
public class Booking
{
public Guid BookingId { get; set; }
public string BookingName { get; set; }
public Printer Printer { get; set; }
public IEnumerable<BookingProduct> BookingProducts { get; set; }
}
public class BookingProduct
{
public int BookingProductId { get; set; }
public Booking Booking { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public IEnumerable<PrinterProduct> PrinterProducts { get; set; }
}
public class PrinterProduct
{
public int PrinterProductId { get; set; }
public Product Product { get; set; }
public Printer Printer { get; set; }
public IEnumerable<ProductPrice> ProductPrices { get; set; }
}
public class ProductPrice
{
public int ProductPriceId { get; set; }
public PrinterProduct PrinterProduct { get; set; }
public decimal Price { get; set; }
}
public class Printer
{
public int PrinterId { get; set; }
public string PrinterName { get; set; }
public IEnumerable<Booking> Bookings { get; set; }
public IEnumerable<PrinterProduct> PrinterProducts { get; set; }
}
Given the newly added class structures in your question, I hope I can clear it up now.
From what I see, Bookings and Products have a many-to-many relation (where BookingProduct is used as the connection). The same is true for Product and Printer (where PrinterProduct is used as the connection).
From what I understand, you are trying to get from a singular Booking item to a singular PrinterProduct. I don't see any efficient way to do this without introducing the possibility of inconsistency with your data. You're expecting some Lists to return you one result. If it's only one result, why is it a List in the first place?
You have a single Booking. You take its BookingProducts. Now you have many items.
You take the Product from each individual BookingProduct. If all BookingProducts have the same product, you're in luck and will only have a List<Product> with a single Product in it. However, there is nothing stopping the system from return many different products, so we are to assume that you now hold a List of several Products
From each Product in the list, you now take all of its PrinterProducts. You now hold many PrinterProducts of many Products.
As you see, you end up with a whole list of items, not just the singular entity you're expecting.
Bookings, Products and Printers are all connected to eachother individually, like a triangle. I have seen scenarios where that is correct, but nine times out of ten, this is not what you want; and only leads to possible data inconsistency.
Look at it this way: Is it ever possible for the Product to have a Printer other than the Printer that is already related to the Booking? If not, then why would you have two relations? This only introduces the possibility that Booking.Printer is not the same as PrinterProduct.Printer.
Your relational model is set up to yield many results, but I think you expect a single result in some places. I would suggest taking another look at your data model because it does not reflect the types of operation you wish to perform on it. Change the many-to-many relations to one-to-many where applicable, and you should be able to traverse your data model in a more logical fashion, akin to the answer I provided in my previous answer.
If you've set up navigational properties, you can just browse to it:
var myBooking = ... //A single Booking, don't know how you retrieve it in your case.
var myPrinter = myBooking.Printer; //the Printer that is related to the Booking.
var myPrintproducts = myPrinter.PrintProducts; //The products that are related to the printer.
You don't need to keep nesting select statements, that only creates unnecessary confusion and overhead cost.
Keep in mind that you need to do this while in scope of the db context. Every time you try to access a property, EF will fill in the needed variables from the database. As long as there is an open db connection, it works.
Edit
If you really need to optimize this, you can use a Select statement. But you only need a single one. For example:
var myPrintproducts = db.Bookings.Single( x => x.ID == some_id_variable ).Select( x => x.Printer.PrintProducts);
But unless you have a very strict performance requirement, it seems better for code readability to just browse to it.
Given the following:
public class Department
{
public int DepartmentID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
If I turn lazy loading off and issue the following:
var departments = _DepartmentRepository.GetAll()
.Include(c => c.courses);
Then I get the answers with a Department object inside of them.
Is there a way I can just include the courses and not get back the Department object. For example can I just include one level (courses).
You are just including one level. The department object inside the course is there because EF has done some relationship fixup so that you can navigate to the department from the course.
If you don't want departments then just get the courses directly. That is context.Courses.ToList(); or via a courses repo if you have one.
When fetching entities EF will automatically populate navigation properties where it is already tracking the target object. This means if you do say:
// Load the department with a PK of 1
_DepartmentRepository.Find(1);
and then, using the same context, for example:
// Load a course with PK of 17
_CourseRepository.Find(17);
If this courses department id is 1, then EF will have automatically populated it's Department navigation property even though you didn't specify the include. You could stop this behavior by not making the Department navigation property virtual.
Not sure how to explain this, but here goes...
I've built a code first data model using EF 4.3. One of classes, "Address" contains typical address data, street, city, state, etc. Other classes in the model contain instances of the "Address" class.
The problem. The data will be gathered/presented using different views, some of which will require the address fields, others that will not.
I can build different view models, each having the necessary validation attributes, and copy the data back and forth between data model and view model but that seems wrong.
What am I missing? There has to be a smarter way to do this.
Thanks for your help,
Jimmy
First read these questions and their answers:
MVC: Data Models and View Models
Why Two Classes, View Model and Domain Model?
also this article could help:
ASP.NET MVC View Model Patterns
In conclusion, I think in most scenarios it's helpful to have a chubby domain model (DM) but light weight presentation models (PM) related to it. So when we want to edit only a small chunk of that fat DM, one of our PMs will raise its hand.
Imagine this class in DM:
namespace DomainModels
{
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime? DoB { get; set; }
public MyAddressDM Address { get; set; }
public string Phone { get; set; }
public IEnumerable<MyCarModel> Cars { get; set; }
//etc.
}
}
Now imagine that in one view we need to edit only Address and Phone. A light weight PM could be like:
namesapce PresentationModels
{
public PersonAddressPhone
{
public int ID { get; set;}
public string FullName { get; set;}
public string AddressSteet { get; set; }
public string AddressCity { get; set; }
public string AddressState { get; set; }
public string AddressZipCode { get; set; }
public string Phone { get; set; }
}
}
and in another view we need to add/remove cars for a person:
namesapce PresentationModels
{
public PersonCars
{
public int ID { get; set;}
public string FullName { get; set;}
public IEnumerable<PMCar> Cars { get; set;}
}
}
Mapping between DO and PM is the golden piece of this puzzle. Be sure to take a look at AutoMapper.
Sorry about the title; couldn't think of a better one.
Any way, I'm accessing an associated property in my view like so:
#Model.Company.CompanyName // No problems here...
The model is a viewmodel mapped to an EF POCO. The Model has several properties associated to the Company table. Only one of the properties in the model share the same name as the PK in the Company table. All the other properties reference the same table:
public class MyModelClass
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AnotherCompanyId { get; set; } // References CompanyId
public int AndAnotherCompanyId { get; set; } // References CompanyId
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public string Address { get; set; }
}
I'm obviously missing something here.
How can I get the names of the other companies in my Model?
Any help is greatly appreciated.
The model is a viewmodel mapped to an EF POCO
I think you are confusing the notion of a view model. A view model is a class that is specifically designed to meet the requirements of your view. So if in your view you need to display the company name and not the company id then your view model should directly contain a CompanyName property. Or a reference to another view model (CompanyViewModel) which contains the name directly. It is then the responsibility of your controller action to query your domain models (EF entities) and aggregate them into a single view model tat will contain all the necessary information that the view requires.
Here's how a typical view model might look like:
public class MyViewModel
{
public CompanyViewModel Company { get; set; }
public CompanyViewModel AnotherCompany { get; set; }
public CompanyViewModel AndAnotherCompany { get; set; }
}
public class CompanyViewModel
{
public string Name { get; set; }
}
Where the data comes from in this view model is not important. You could have the Company property populated from your EF stuff, the AnotherCompany property populated from a XML file and AndAnotherCompany from WCF.
Setup
Using MVC 3 + Code First
Here are my classes
public class Member
{
[Key]
public Guid ID { get; set; }
[Required]
public String Email { get; set; }
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
public String Sex { get; set; }
public String Password { get; set; }
public String PasswordSalt { get; set; }
public DateTime RegisterDate { get; set; }
public DateTime LastOnline { get; set; }
public String SecurityQuestion { get; set; }
public String SecurityAnswer { get; set; }
public virtual ICollection<FamilyMember> Families { get; set; }
public virtual ICollection<Relationship> Relationships { get; set; }
}
public class Relationship
{
[Key]
public Guid ID { get; set; }
[ForeignKey("Member1")]
public Guid Member1ID { get; set; }
[ForeignKey("Member2")]
public Guid Member2ID { get; set; }
public Guid RelationshipTypeID { get; set; }
public virtual RelationshipType RelationshipType { get; set; }
public virtual Member Member1 { get; set; }
public virtual Member Member2 { get; set; }
}
Here is the problem
The database table "Relationship" is being created with the following columns:
ID, Member1ID, Member2ID, RelationshipTypeID, Member_ID
Why is it creating the Member_ID column?
I've seen this post in which the user has the same type of setup, but I am unsure of how to define the InverseProperty correctly. I tried using fluent API calls but from what I can tell they will not work here since I have two foreign keys referring to the same table.
Any help would be appreciated!
Member_ID is the foreign key column which EF created for the navigation property Member.Relationships. It belongs to a third association from Member.Relationships refering to an end endpoint which is not exposed in your Relationship entity. This relationship has nothing to do with the other two relationships from Relationship.Member1 and Relationship.Member2 which also both have an endpoint not exposed in Member.
I guess, this is not what you want. You need always pairs of endpoints in two entities to create an association. One endpoint is always a navigation property. The second endpoint can also be a navigation property but it is not required, you can omit the second navigation property.
Now, what is not possible, is to associate two navigation properties (Member1 and Member2) in one entity with one navigation property (Relationships) in the other entity. That is what you are trying to do apparently.
I assume that your Member.Relationships property is supposed to express that the member is either Member1 or Member2 in the relationship, or that it participates in the relationship, no matter if as Member1 or Member2.
Unfortunately you cannot express this in the model appropriately. You have to introduce something like RelationsshipsAsMember1 and RelationsshipsAsMember2 and for these two collection you can use the InverseProperty attribute as shown in the other question. In addition you can add a helper property which concats the two collections. But this is not a mapped property but readonly:
public class Member
{
// ...
[InverseProperty("Member1")]
public virtual ICollection<Relationship> RelationshipsAsMember1 { get; set; }
[InverseProperty("Member2")]
public virtual ICollection<Relationship> RelationshipsAsMember2 { get; set; }
public IEnumerable<Relationship> AllRelationships
{
get { return RelationshipsAsMember1.Concat(RelationshipsAsMember2); }
}
}
Accessing AllRelationships will cause two queries and roundtrips to the database (with lazy loading) to load both collections first before they get concatenated in memory.
With this mapping the Member_ID column will disappear and you will only get the two expected foreign key columns Member1ID, Member2ID because now you have only two associations and not three anymore.
You could also think about if you need the Relationships collection in the Member entity at all. As said, navigation properties on both sides are not required. If you rarely need to navigate from a member to its relationships you could fetch the relationships also with queries on the Relationship set, like so:
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID || r.Member2ID == givenMemberID)
.ToList();
...or...
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID)
.Concat(context.Relationships
.Where(r => r.Member2ID == givenMemberID)
.ToList();
This would give you all relationships the member with ID = givenMemberID participates in without the need of a navigation collection on the Member entity.