I am trying to create a quick demo shop, and I am failing with an optional many to one or zero relationship.
The relevant classes are:
Item
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public int SubCategoryID { get; set; }
public virtual SubCategory Category { get; set; }
public double Price { get; set; }
}
Order
public class Order
{
public int ID { get; set; }
public DateTime DateOfOrder { get; set; }
public virtual ICollection<Item> Items { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
However, I am getting confused because viewing the model shows:
but, the database itself shows (for items):
Which indicates to me that each item can only belong to a single order.
Do I have to create a separate class that is many to many orders/items?
I seem to remember EF doing this automatically, but, I haven't touched it for a few years and I just can't remember what I used to do.
I had to add:
public virtual ICollection<order> Orders { get; set; }
to the Item... I'm never going to call it this way, but it looks like that is required for EF to build this relationship.
I am sure it used to be easier, so, leaving this question open so someone can give a better answer!
If you add a collection of Orders to the Item entity, EF will create for you implicitly the junction table on your DB. In this page you can find more info about how to configure a many to many relationship.
The junction table generally is mapped when you need to add an additional column (that excludes both keys of the tables you are joining). In that case you need to create two one-to-many relationships between the entity that represent the junction table and Order and Item respectively. Some people recommend always map the junction table because that way you have all the tables represented as entities and you can write queries starting by the junction table. You can find interesting info about this subject in this link. But, in my experience, in most cases you can work perfectly without map explicitly the junction table.
Related
I want to store multiple values from a dropdown using .NET Core MVC and Entity Framework. I have no idea how to do that. This is my model code.
public class Project
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string? Description { get; set; }
public List<int> SkillsID { get; set; }
[ValidateNever]
public List<Skill> skills { get; set; }
}
Start here (https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key ) to understand relationships in EF. In your case you will likely want to set up a Many-to-Many relationship between Projects and Skills. This assumes you will have a list of Skills where by each Project associates itself with 0, 1, or many of those skills. In this case you would have classes like:
public class Project
{
// ... project properties.
public virtual ICollection<Skill> Skills { get; protected set; } = new List<Skill>();
}
public class Skill
{
// ... skill properties.
}
When mapped, you tell EF that Project .HasMany(x => x.Skills).WithMany() as we likely don't need a Projects collection on each individual Skill entity. The last step is telling EF how to associate these two entities. In Many-to-Many relationships this involves a joining table such as a ProjectSkills table:
[tbl:ProjectSkills]
- PK,FK - ProjectId
- PK,FK - SkillId
You don't track lists of FKs in the entity. Think of it from a database perspective. When relating tables together with FKs, you don't have an array or such of FKs within a single row. You use a joining table.
The PK for the table is a composite between the Project ID and Skill ID, where each of those is a FK back to the corresponding table. EF can create this table by convention or you can configure it manually if you want to fine-tune the naming.
If you want to track more detail about the relationship such as tracking a CreatedDate etc. then you will need to map the relationship as an entity which would look like:
public class Project
{
// ... project properties.
public virtual ICollection<ProjectSkill> ProjectSkills { get; protected set; } = new List<ProjectSkill>();
}
public class Skill
{
// ... skill properties.
}
public class ProjectSkill
{
[Key, Column(Order = 0)]
public int ProjectId { get; set; }
[Key, Column(Order = 1)]
public int SkillId { get; set; }
public DateTime CreatedDateTime { get; set; }
// .. Other details about the relationship.
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }
[ForeignKey("SkillId")]
public virtual Skill Skill { get; set; }
}
You may come across examples for EF core using these joining entities as this was required in earlier versions of EF Core (2, 3.1) for all Many-to-Many relationships.
For a One-to-Many where skills specifically belong to a Project, then a ProjectID would be put into the Skill table. The Project entity would have the same collection of Skills, but the mapping would be a: .HasMany(x => x.Skills).WithOne() Where the Skill table would contain a ProjectID to associate itself to a given project. EF can represent this relationship as a one-way where Project has the collection and Skill doesn't expose a reference to Project, or bi-directional where you can add a Project reference into Skill. (.HasMany(x => x.Skills).WithOne(x => x.Project))
I am using EF Code First.
I need two tables, LedgerCategories and LedgerSubCategories with a one-to-many relationship (Categories -> SubCategories), with the keys in each being codes (strings) - i.e. LedgerCategoryCode and LedgerSubCategoryCode respectively. However, I need to allow the SubCategoryCode values to be the same for different Categories.
E.g. CategoryCode = REHAB, SubCategoryCodes = MATL, CONTR, and FEES; and CategoryCode = MAINT, SubCategoryCodes = MATL, CONTR, and FEES.
I'm thinking I need to use a composite key and include both the CategoryCode and SubCategoryCode fields in the LedgerSubCategories table. Currently I have:
public class LedgerCategory
{
[Key]
public string LedgerCategoryCode { get; set; }
public string Description { get; set; }
public List<LedgerSubCategory> LedgerSubCategories { get; set; }
}
public class LedgerSubCategory
{
[Key, Column(Order = 0)]
public string LedgerCategoryCode { get; set; }
[Key, Column(Order = 1)]
public string LedgerSubCategoryCode { get; set; }
public string Description { get; set; }
}
I am seeding these tables using only instances of the LedgerCategory class, having each contain a List of appropriately instantiated LedgerSubCategory classes. This appears to both set up the DB schema correctly (in my perception), and populate both tables appropriately.
But, when I reinstantiate a simple List of LedgerCategory, i.e.
using (var db = new BusinessLedgerDBContext())
{
var LedgerCategories = db.LedgerCategories.ToList();
}
The LedgerCategory instances don't contain their respective List of associated LedgerSubCategory instances.
I am trying to avoid, what seems like a kludge, to introduce a unique number or Guid ID field in LedgerSubCategories as a PK and just index off the other Code fields. I haven't tried this, but I'm not sure it would cause any different results for reinstantiating the LedgerCategories and getting associated LedgerSubCategories.
Any advice on how to do this appropriately and get proper results is appreciated.
To, I suppose, answer my own question, I have found that overriding OnModelCreating() in the respective DbContext with Fluent API to establish the one to many relationship and foreign key when the Code First framework establishes the desired DB Schema. There appears no other way to do this, such as with Attributes. By many accounts of others, including MSDN, Fluent API appears to be what is needed. However, that has led me to a new issue, or set of issues, which I've posed as a question here.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures the one-many relationship between Categories and
// SubCategories, and established the Foreign Key in SubCategories
modelBuilder.Entity<Category>()
.HasMany<SubCategory>(c => c.SubCategories)
.WithRequired(s => s.Category)
.HasForeignKey<string>(s => s.CategoryCode);
}
I want to update a mediate table's primary keys. This post suggests to take another primary key and don't change it, but we're using our tables in other non-EF projects, I didn't designed them and I'm not able to change them. Do I have any other option? Anything? Even deleting the old record and inserting a new one. I just don't know how to retrieve the old values.
This is my class:
public class ZChangeUnits : User
{
[Key]
[Column(TypeName = "VARCHAR", Order = 0), StringLength(4)]
public string CCode1 { get; set; }
[ForeignKey("CCode1")]
public virtual ZUnits ZUnits1 { get; set; }
[Key]
[Column(TypeName = "VARCHAR", Order = 1), StringLength(4)]
public string CCode2 { get; set; }
[ForeignKey("CCode2")]
public virtual ZUnits ZUnits2 { get; set; }
[Column(TypeName = "NUMERIC")]
[DecimalPrecision(18, 5)]
public decimal NZarib { get; set; }
}
UPDATE
I've posted the schema below, the left table is used for unit conversion. This is actually the suggested method in this answer.
I can't think of any other way to implement that. Whether I should update my table's design or some EF code will do the job. Any help and solution is welcome. :)
Document oriented databases (particularly RavenDB) are really intriguing me, and I'm wanting to play around with them a bit. However as someone who is very used to relational mapping, I was trying to think of how to model data correctly in a document database.
Say I have a CRM with the following entities in my C# application (leaving out unneeded properties):
public class Company
{
public int Id { get; set; }
public IList<Contact> Contacts { get; set; }
public IList<Task> Tasks { get; set; }
}
public class Contact
{
public int Id { get; set; }
public Company Company { get; set; }
public IList<Task> Tasks { get; set; }
}
public class Task
{
public int Id { get; set; }
public Company Company { get; set; }
public Contact Contact { get; set; }
}
I was thinking of putting this all in a Company document, as contacts and tasks do not have a purpose out side of companies, and most of the time query for a task or contacts will also show information about the associated company.
The issue comes with Task entities. Say the business requires that a task is ALWAYS associated with a company but optionally also associated with a task.
In a relational model this is easy, as you just have a Tasks table and have the Company.Tasks relate to all tasks for the company, while Contact.Tasks only show the tasks for the specific Task.
For modeling this in a document database, I thought of the following three ideas:
Model Tasks as a separate document. This seems kind of anti-document db as most of the time you look at a company or contact you will want to see the list of tasks, thus having to perform joins over documents a lot.
Keep tasks that are not associated with a contact in the Company.Tasks list and put tasks assocaited with a contact in the list for each individual contacts. This unfortunately means that if you want to see all tasks for a company (which will probably be a lot) you have to combine all tasks for the company with all tasks for each individual contact. I also see this being complicated when you want to disassociate a task from a contact, as you have to move it from the contact to the company
Keep all tasks in the Company.Tasks list, and each contact has a list of id values for tasks it is associated with. This seems like a good approach except for having to manually take id values and having to make a sub-list of Task entities for a contact.
What is the recommended way to model this data in a document oriented database?
Use denormalized references:
http://ravendb.net/faq/denormalized-references
in essence you have a DenormalizedReference class:
public class DenormalizedReference<T> where T : INamedDocument
{
public string Id { get; set; }
public string Name { get; set; }
public static implicit operator DenormalizedReference<T> (T doc)
{
return new DenormalizedReference<T>
{
Id = doc.Id,
Name = doc.Name
}
}
}
your documents look like - i've implemented the INamedDocument interface - this can be whatever you need it to be though:
public class Company : INamedDocument
{
public string Name{get;set;}
public int Id { get; set; }
public IList<DenormalizedReference<Contact>> Contacts { get; set; }
public IList<DenormalizedReference<Task>> Tasks { get; set; }
}
public class Contact : INamedDocument
{
public string Name{get;set;}
public int Id { get; set; }
public DenormalizedReference<Company> Company { get; set; }
public IList<DenormalizedReference<Task>> Tasks { get; set; }
}
public class Task : INamedDocument
{
public string Name{get;set;}
public int Id { get; set; }
public DenormalizedReference<Company> Company { get; set; }
public DenormalizedReference<Contact> Contact { get; set; }
}
Now saving a Task works exactly as it did before:
var task = new Task{
Company = myCompany,
Contact = myContact
};
However pulling all this back will mean you're only going to get the denormalized reference for the child objects. To hydrate these I use an index:
public class Tasks_Hydrated : AbstractIndexCreationTask<Task>
{
public Tasks_Hydrated()
{
Map = docs => from doc in docs
select new
{
doc.Name
};
TransformResults = (db, docs) => from doc in docs
let Company = db.Load<Company>(doc.Company.Id)
let Contact = db.Load<Contact>(doc.Contact.Id)
select new
{
Contact,
Company,
doc.Id,
doc.Name
};
}
}
And using your index to retrieve the hydrated tasks is:
var tasks = from c in _session.Query<Projections.Task, Tasks_Hydrated>()
where c.Name == "taskmaster"
select c;
Which i think is quite clean :)
As a design conversation - the general rule is that if you ever need to load the child documents alone as in - not part of the parent document. Whether that be for editing or viewing - you should model it with it's own Id as it's own document. Using the method above makes this quite simple.
I'm new to document dbs as well...so with a grain of salt...
As a contrasting example...if you are on Twitter and you have a list of the people you follow, which contains a list of their tweets...you would not move their tweets into your twitter account in order to read them, and if you re-tweet, you would only have a copy, not the original.
So, in the same way, my opinion is that if Tasks belong to a company, then they stay within the Company. The Company is the Aggregate Root for Tasks. The Contacts can only hold references (ids) or copies of the Tasks and cannot modify them directly. If you have your contact hold a "copy" of the task, that's fine, but in order to modify the task (e.g. mark it complete) you would modify the task through its Aggregate Root (Company). Since a copy could quickly become outdated, it seems like you would only want a copy to exist while in memory and when saving the Contact, you would only save references to the Tasks.
I'm experimenting with db4o as a data store, so to get to grips with it I thought I'd build myself a simple issue tracking web application (in ASP.NET MVC). I've found db4o to be excellent in terms of rapid development, especially for small apps like this, and it also negates the need for an ORM.
However, having come from a SQL Server/MySQL background I'm a little unsure of how I should be structuring my objects when it comes to relationships (or perhaps I just don't properly understand the way object databases work).
Here's my simple example: I have just two model classes, Issue and Person.
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public Person AssignedBy { get; set; }
public Person AssignedTo { get; set; }
}
public class Person
{
public string ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
The ID properties are just GUID strings generated by the .NET Guid.NewGuid() helper.
So here's how I initially thought the application would work; please ignore any security concerns etc and assume we already have a few Person objects stored in the database:
User logs in. Query the database for the Person which matches the username and password, and store his/her GUID id as a session variable. Redirect to app home screen.
Logged in user creates a new issue ticket, selecting the user to assign it to from a drop-down list. They fill in the other details (Title, Description etc), and then submit the form.
Query the Person objects in the database (by their GUID ID's) to get an object representing the logged in user and one representing the user the ticket has been assigned to. Create a new Person object (populated with the posted form data), assign the Person objects to the Issue object's AssignedBy and AssignedTo properties, and store it.
This would mean I have two Person objects stored against each Issue record. But what happens if I update the original Person—do all the stored references to that Person in the various issue objects update, or do I have to handle that manually? Are they references, or copies?
Would it be better/more efficient to just store a GUID string for the AssignedBy and AssignedTo fields (as below) and then look up the original person based on that each time?
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public string AssignedByID { get; set; }
public string AssignedToID { get; set; }
}
I think I'm just stuck in a certain way of thinking which is confusing me. If someone could explain it clearly that would be most helpful!
Object-Databases try to provide the same semantics as objects in memory. The rule of thumb is: It works like objects in memory. Object databases store references between the objects in the database. When you update the object, that object is updates. And if you have a reference to that objects, you see the changed version.
In your case, the Issue-objects refer to the person object. When you update that person, all Issues which refer to it 'see' that update.
Of course, primitive types like int, strings, longs etc are handled like value objects and not a reference objects. Also arrays are handled like value objects in db4o, this means a array is stored together with the object and not as a reference. Everything else is stored as a reference, even collections like List or Dictionaries.
Please take a look at:
http://developer.db4o.com/Documentation/Reference/db4o-7.4/java/reference/html/reference/basic_concepts/database_models/object-relational_how_to.html
Best!