Save record to multiple tables using UoW pattern - c#

I am using MVC 5 with Entity Framework 6 and Unit of Work pattern.
Tables: and fields
Customer - Id, Name
ContactType - Id, Name (Home contact, work contact etc)
ContactDetails - Id, CustomerId, ContactTypeId, ContactValue
One Customer can have multiple Contact Details (so Customer1 has a home contact, work contact etc).
The ContactType table is a look-up table so it just displays the types of contacts available (home, work, emergency, mobile etc)
I have created the Interfaces and Classes as required to carry out the basic Add, Edit functionality, then created an Unit Of Work class to hold all these Repositories.
Tested it out and everything works as expected when i hard code values in.
When i created my MVC application, i added the below lines to add this entry into a database using the Unit of Work class
public ActionResult SaveContactDetails(CustomerContactType viewModel)
{
_unitOfWork.Customers.Add(viewModel.Customer);
_unitOfWork.ContactDetails.Add(viewModel.ContactDetail);
//_unitOfWork.SaveAllChanges();
return View();
}
I created a new ViewModel called CustomerContactType which is a class containing the tables i require in order to save the data successfully
public class CustomerContactType
{
public ContactType ContactType { get; set; }
public IEnumerable<ContactType> ContactTypes { get; set; }
public Customer Customer { get; set; }
public ContactDetail ContactDetail { get; set; }
}
I realised how to assign a dropdown value to the model within the .cshtml, so the ContactDetails table knows which ContactType is associated with that contact number (Home, emergency etc).
The problem i have is the ContactDetails requires a customer ID. This customer ID doesnt generate until the customer is saved so im not sure how i should be doing this?
These two lines carry out the task but i can see the customerID is null in the second line where i would have preferred it to contain the ID
_unitOfWork.Customers.Add(viewModel.Customer);
_unitOfWork.ContactDetails.Add(viewModel.ContactDetail);
I can provide additional code if required but wasnt sure if theres an easy fix or not.

Instead of using CustomerId in ContactDetails, why not change CustomerId to be of a complex type Customer and decorate it with a foreign key attribute? That way you can add a ContactDetails object along with a Customer object assigned to its property and a single call to SaveChanges should persist both entities to the DB.

Related

Update/Edit entity with relations

I am working in MVC and creating som CRUD operations. I am not a pro, som im having trouble doing the Update/edit of an entity, when the entity have a relation, that i also want to update. Here is a short description of the tables:
I have a model called "User" with follwing properties (userId and so on....)
I have a model called "Address" with following properties (addressId, street, zipcode, city)
I have a model called "Home" with following properties (StreetNumber, floor, side, and two navigation properties"userid" and "addressId").
Now.. i have a viewModel with the properties from Address and Home.. And i want to update Both using this viewModel. Im not having probilem showing all the data in my view. But i cannot update both entities at the same time?
Can anyone give an example on how to do this?
Can i update the address model going through the home model?
It would've been easier if you had posted some of your code, but I'll assume what you have.
First, I'd update your viewmodel and include id of the user:
public class UserViewModel
{
public Guid Id {get;set;}
public string Name {get;set;}
public string Surname {get;set;}
public string City {get;set;}
public string Address {get;set;}
}
Then you just have to map the properties from the ViewModel to the properties of the model. Since you mentioned that you have some navigation properties it shouldn't be hard to find the address of the user you want to update.
public void Update(UserViewModel userVM)
{
var user = db.Users.Find(userVM.Id);
if(user != null)
{
user.Name = userVM.Name;
user.Surname = userVM.Surname;
var address = user.Address;
address.City = userVM.City;
address.Address = userVM.Address;
db.SaveChanges();
}
}
I think you should get the idea now.
In case you don't have navigation properties just include the id of the address to your viewmodel so you can find it the same way as I did for the user.

Inheritance with EF Code First - Table per Concrete Type (TPC)

I created such a kind of Table per Concrete Type (TPC) structure as shown below:
Here are the entities used here:
public abstract class BaseModel : MyOtherBaseClass
{
[Key]
public int Id { get; set; }
//Foreign key for Project
public int ProjectId { get; set; }
public int Sequence { get; set; }
public string Name { get; set; }
public string IconUrl { get; set; }
//Navigation Properties ####################
public virtual Project Project { get; set; }
}
[Table("Tool")]
public class Tool : BaseModel
{
[Key]
public int Id { get; set; }
public string ToolBrand { get; set; }
//Navigation Properties ####################
//public virtual Project Project { get; set; }
}
[Table("Priority")]
public class Priority : BaseModel
{
[Key]
public int Id { get; set; }
public string PriorityCode { get; set; }
//Navigation Properties ####################
//public virtual Project Project { get; set; }
}
At this point I am confused about the issues below:
1) Can I use Id for Tool and Priority entities? And will it be absolutely unnecessary?
2) I use FK (ProjectId) and related table Project in the BaseModel class. But, as the ProjectId columns will be created in the Tool and Priority tables, how can the relation be created? May there be any problem?
3) If I do not prefer Fluent API, should I add Tool and Priority entities besides BaseModel entity in the context? Because in some resources the child classes are added while in some others not. Which one is true?
public class EntityContext : DbContext
{
public DbSet<BaseModel> BaseModel { get; set; }
// ? public DbSet<Tool> Tool { get; set; }
// ? public DbSet<Priority> Priority { get; set; }
}
If there is any problem regarding to this usage could you also please let me know? Thanks...
Whenever you design classes you should be aware of what the classes really represent.
Your DbSet<...> represents one table your database. The class inside the DbSet represents the columns of this table as non-virtual properties, and the relations between the tables as virtual properties.
It seems to me that you want two tables in your database: a table with Tools and a table with Priorities. You currently think that some columns of Tools are also in Priorities. Therefore you intend to make a common base class of which you have no better name than BaseModel.
The fact that you can't invent a proper name for the common base class should warn you that maybe there is nothing really common between those two. Are you sure, that if you describe a Tool, that is is not an incident that it has some properties with the same name and type as a Priority
For example. If you would define rows of Tools and rows Priorities, you would say that every Tool is used in a Project, and every Priority is the priority of a Project. This is part of the design of your system. According your definition it is meaningless to have Tools without its Project
Even so, according to your definition, every tool should have a "method of unique Identification", you decided to use an integer Id for this. Similarly you decided to have an integer Id for priorities. However, is it intrinsic to a row in your Tool table that the type of the identification equals the type of identification of a Priority? Would you want your design be worthless if someone told you that a Tool has a Guid Id and an Priority has an integer Id?
No of course not: your design should be so robust that small changes in your database tables should lead to small changes in your design!
Which properties should be in the base class
1) Can I use Id for Tool and Priority entities?
Answer: Yes you can put the Id in the base class and omit it from the derived classes. However, this would add a constraint that every derived type should have the same name and type for the column in the table that represents the Id.
Hence: if you don't want to limit yourself to this: don't do this, remove the Id from the base class and put it in the derived class. If later someone decides a small change to rename the column, or give it a different type your changes will be similar small.
Of course this is also for all other properties: if they are identical "by coincidence", put the values in the derived class. If it is typical something that both Tools and Priorities have in common, put it in the base class.
It is not easy for me to detect similarities between Tools of a Project and Priorities of a Project, so I'll switch to a different example.
Suppose you have a database with Teachers and Students. Some properties are unique for the Teachers, and some are unique for the Students. But they also have something in common that is not by coincidence: Teachers and Students are both Persons, with a Name, a Birthday, possibly an Address etc. If later someone decides that an Address has an extra field indicating the GPS coordinates of the address, then you'll only have to change one class.
Conclusion: only put properties in the base class that are intrinsic common to all derived classes, not by coincidence
Where to put the foreign key?
According to your design, both Tools and Priorities are things belonging to a Project. If after the previous step you decided that this is the only thing they have in common, then you'll seldom take a bunch of objects that contain both Tools and Priorities.
In the school database it would be quite normal to put Students and Teachers into a bunch of Persons, where every Person would have an Address, and in every Address would be living zero or more Persons (one-to-many)
// this will be a row in a table, hence it has an Id
class Address
{
public int Id {get; set;}
public string ZipCode {get; set;}
public string Street {get; set;}
...
// on every address live zero or more Persons (one-to-many)
public virtual ICollection <Person> Persons {get; set;}
}
// this will not be a row in a separate table, hence it has no ID
class Person
{
public string Name {get; set;}
public DateTime Birthday {get; set;}
...
// every Person lives at an Address, using foreign key
public int AddressId {get; set;}
public virtual Address Address {get; set;}
}
class Teacher : Person
{
public int Id {get; set;}
...
}
class Student: Person
{
public int Id {get; set;}
...
}
So you will have three tables: Addresses, Teachers, Students. Both Teachers and Students will have the properties of a Person. They both live at exactly one Address.
See how little changes need to be made if we decide to add a column to a Teacher, or a Person? How little needs to be changes if you want a Teacher Id to be a GUID, or the Address.Id be a String (expect changes in primary Key of Address and foreign key inside Person). See how little needs to be changed if you want to add a new type of Person: a Parent?
Conclusion: if you have a base class where every derived class should have a reference to an item in a different table: put the foreign key in the base class. However, if this relation is not intrinsic to all derived items, put the foreign key in the derived classes.
3 should I add entities besides BaseModel entity in the context?
Remember: every DbSet in your DbContext will become a table. If you don't specify Tools and Priorities as separate tables, you won't be using Table per concreate class (TPC) but table per hierarchy (TPH): both Tools and Priorities will be in one table
I seldom use TPH because of all unused null values I get in my tables.
If you most often will ask for "Teachers that ..." and "Students which ...", you should keep them in separate tables. The columns of the base class are also in these separate tables.
If you most often will ask for "Persons who ...", where the Persons might be Students or Teachers, consider using table per type (TPT): a Persons table, and a Teachers table with a foreign key to the Persons tables, as well as a Students table with a foreign key. All base class properties are in the Persons table.
It is easy to see that if you ask for "Persons that..." TPT will only query one table, whild for TPC you'll need to query the Teachers table as well as the Students table and concatenate the result.
However if you ask for "Students that ..." TPT will need to join the Persons table and the Students table. TPC is here faster: only one table is accessed.

Add variable to Entity Framework created model class without editing the actual database columns?

I just recently started creating a .net web app in mvc5. I've tried to search for the answer as I usually do rather than asking a question but Im not sure how to word this but here it goes:
I have a class:
public partial class employee
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
In which the class was created by using the Database First wizard with Entity Framework. (reasoning for the tag)
What I did was get the FistName, MiddleName, and LastName from the database. In which I successfully did with this ActionResult from my controller.
[ChildActionOnly]
public ActionResult Nav()
{
employee user;
using (DatabaseEntities context = new DatabaseEntities())
{
user = context.employees.FirstOrDefault(e => e.FirstName ="Bob");
if (!System.IO.File.Exists(Server.MapPath(user.imagefile)))
{
user.imagefile = $"/Content/Images/user/{user.FirstName[0]}.png";
}
}
return PartialView("_Nav", user);
}
Now I'm wondering if its possible, how to create another item to the class without adding a column to the database. I want to add a FormattedName variable to the class that is got after the database call to get the model (user) to include the new variable (FormattedName) that gets its value from the existing variables in the class.
I understand the wording and terminology could be off but any help would be great and thx in advance.
Edit:
In the comment section, I was informed about attributes ex.[NotMapped] which was information I definitely needed to know. But for this particular question, it doesn't work. Everything was ok until I updated the model using entity framework, the attributes were gone and have to keep adding them every model update which is not ideal for me. I went ahead and edited my database to include a (FormattedName) column so the problem is fixed. But I still would love to know if this is doable.

Entity Framework 6.1 Code First MySql entity tracking persists across context

I'm having an odd problem with a DbContext saving duplicate objects. I have a graph of objects similar to the following:
public class Customer
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<Order> Orders {get;set;}
}
public class Order
{
public int Id {get;set;}
public DateTime OrderDate {get;set;}
public ICollection<Product> Products {get;set;}
}
public class Product
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<Orders> Orders {get;set;}
}
public class MyContext:DbContext
{
public DbSet<Customer> Customers {get;set;}
public DbSet<Order> Orders {get;set;}
public DbSet<Product> Products {get;set;}
}
This is a contrived example of course, but if I want to save two new Customers. I add them with DbContext.Customers.Add(). The customer is completely filled out with orders and products. It saves all that data just fine. But if a second customer is added, even with a totally new DbContext instance, it will add the original customer a second time. I still have a reference to the original customer, but I did not add it to the new context instance. Yet DbContext.SaveChanges will save two customers, the original one a second time, and the new one. This will exponentially increase the number of records I'm saving. And this applies to the entire graph. Orders and Products are also duplicated.
If I manually detach the object after saving, it won't save duplicates. Since I'm adding an entire Customer at once, I have to loop through all child entities and detach those as well. If I only detach Customer, it will still duplicate previous Orders and Products. This even seems to happen if I turn off Change Tracking!
Any ideas?
I tried, as suggested, to duplicate this in a small application to make it easier to share. But I couldn't get it to happen. I went over my code for the 20th time and finally noticed something.
Let's say I added a new class Company at the top of the hierarchy. So each Company had many Customers. If I add a company, save it, but keep the reference, and then use that reference to assign to the navigation property of each of the "Customers" before I add them, it will save duplicates. Even if every save (Company, Customer 1, Customer 2, etc.) has a completely new DbContext in a using statement.
I guess for now the moral of this story is don't assign navigation properties in this instance. Instead add Id columns and use Id values.
Here's my example that reproduces the issue I'm talking about. Maybe this is something you "just don't do" but i was not aware that it would result in duplicates.

One to Many mapping models inside DbContext

Currently I am new to MVC and practicing with some demo applications.
I have 2 models.
Employee - (with data members employeeId,name,gender,city,deptId)
Department - (with data members id,name,Collection employees )
I have put these two classes inside a context class inheriting from DbContext.
Problem is when I try to get employee data based on deptId , it throws error that department_Id column is not defined. If I remove the employees data member from Department then everything works fine. What exactly is happening here, why is it automatically adding a column, and how to tackle it?
Don't add deptId property to your Employee class. Just add a navigation property like this:
public virtual Department Department { get; set; }
And your Department class:
public virtual ICollection<Employee> Employees { get; set; }
Then Entity Framework will create all necessary relationships automatically for you.
For more informatin about Navigation Properties take a look at here: http://msdn.microsoft.com/en-us/data/jj713564.aspx

Categories

Resources