I'm looking to add records to my database but not sure of the best approach.
Here are my models:
public class fList
{
public int fListID { get; set; }
public string Title { get; set; }
public int UserID { get; set; }
public ICollection<Item> Items { get; set; }
}
public class Item
{
public int ItemID { get; set; }
public string Name { get; set; }
public ICollection<fList> fLists { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public ICollection<fList> fLists { get; set; }
}
Here is the database before performing the operation:
Here is how the database should look after performing the operation:
What I want to do is add a new fList and new User. I also want to add new Items but only where they don't already exist. And I want to join Items to the fList accordingly.
I'm hoping to achieve the above as efficiently as possible with minimal db calls.
Here's the controller so far:
var flist = new fList
{
Title = "Colors",
UserName = "Chris"
Items = new List<Item>()
};
flist.Items.Add(new Item { Name = "White" });
flist.Items.Add(new Item { Name = "Orange" });
flist.Items.Add(new Item { Name = "Purple" });
flist.Items.Add(new Item { Name = "Blue" });
flist.Items.Add(new Item { Name = "Green" });
foreach (var item in flist.Items)
{
// Check item exists in database
var existingitem = db.Items.SingleOrDefault(n => n.Name == item.Name);
// If item doesn't exist, add it to database
if (existingitem == null) {
db.Items.Add(item);
}
// ...What next?
}
db.fLists.Add(flist);
db.SaveChanges();
If you have time, you can download the Prodinner sample from Microsoft, it is a perfect solution include OOP, DI, IOC,etc. It also show you the generic functions for saving data.
If you need control the state during save records, you need implement state control your own. Create Interface, Enum and Implement class as show below.
public interface IDataObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
public abstract class DataObjectWithState : IDataObjectWithState
{
[NotMapped]
public State State { get; set; }
}
After that any model class you want control the state you need inherit this Class, so your models have extra UnMap State columns.
In your DB Context file, make a contractor as sample below.
public Db()
: base("name=TP_HOST")
{
try
{
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IDataObjectWithState;
if (entity != null)
{
entity.State = YourEnumNamespace.State.Unchanged;
}
};
}
catch (Exception ex)
{
}
finally { }
}
// this class need include using System.Data.Entity.Infrastructure;
public static EntityState ConvertState(YourEnumNamespace.State state)
{
switch (state)
{
case YourEnumNamespace.State.Added:
return EntityState.Added;
case YourEnumNamespace.State.Deleted:
return EntityState.Deleted;
case YourEnumNamespace.State.Modified:
return EntityState.Modified;
default:
return EntityState.Unchanged;
}
}
Then before you make the call of db.SaveChanges(), you have to set the state like sample below
foreach (var entry in dbContext.ChangeTracker
.Entries<IDataObjectWithState>())
{
IDataObjectWithState stateInfo = entry.Entity;
entry.State =Functions.ConvertState(stateInfo.State);
}
I know difficult to understand mine code, what I want to mention is we define our own enum state, we tell the models what we want (Add, Unchanged, Update, Delete), using for loop change the state become knowing by System.Data.Entity.Infrastructure.
Related
I have a mongodb structured in this way :
public class CarModel
{
[BsonElement("Carname")]
public string Carname { get; set; }
[BsonElement("Color")]
public string Color { get; set; }
[BsonElement("Price")]
public string Price { get; set; }
}
public class Producer
{
[BsonId]
public ObjectId ID { get; set; }
[BsonElement("Producer")]
public string Producer { get; set; }
[BsonElement("CarModel")]
public List<CarModel> CarModel { get; set; }
}
I've made an asp.net core MVC application for make CRUD operation on this database.
now i want to insert a new list of CarModel inside a Producer, but before i want to check if one of the Carname present on this list is already present inside the DB :
[HttpPost]
public ActionResult CreateListino(Producer listino)
{
try
{
var document = _dbContext._database.GetCollection<Producer>("ListinoAuto");
var builder = Builders<Producer>.Filter;
long count = 0;
foreach (var item in listino.CarModel)
{
var filter = builder.Eq(x => x.Producer, listino.Producer) & builder.Where(b => b.CarModel.Any(a => a.Carname == item.Carname));
count += document.Find(filter).CountDocuments();
}
if (count == 0)
{
//Do Something
}
else
{
TempData["Message"] = "Car model Already exist";
return View("CreateListino", listino);
}
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
i really don't like to use the foreach statement for loop trough the CarModel received by the view, so I'm asking if there is a metod to compare the List and the database in one single query.
i want to look at only carname, not the other properies.
thank you
I am trying to build a three tier architecture with BLL, DAL, and UI. I am working on a small Apartment Management System project (which includes the information about resident students, their rooms and the apartment of the rooms they stay). Since I am a beginner I have some technical problems I need to solve. I am using Entity Framework with repository pattern. My project is based on MVVM approach and I am using Automapper.
Codes for Automapper
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Room, RoomViewModel>().ReverseMap();
CreateMap<Apartment, ApartmentViewModel>().ReverseMap();
}
}
public class AutoMapperConfig
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<AutoMapperProfile>();
});
}
}
I am trying to add a new room to my database. There is a many-to-many relationship between my Room and Apartment tables and my database looks like this:
My Database's screen shot
My IRepository interface looks like:
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
T Get(Expression<Func<T, bool>> expression);
IQueryable<T> GetMany(Expression<Func<T, bool>> expression);
bool Insert(T obj);
bool Update(T obj);
bool Delete(int id);
int Count();
void Save();
}
I have RoomRepository and ApartmentRepository, both implement IRepository interface. I have created my repositories and infrastructures:
Infrastructure (folder)
IApartmentRepository.cs
IRepository.cs
IRoomRepository.cs
IStudentRepository.cs
IUserRepository.cs
Repository (folder)
ApartmentRepository.cs
RoomRepository.cs
StudentRepository.cs
UserRepository.cs
While adding a new Room, admin has to state to which apartment that room belongs. The Add method inside the RoomController is below:
[HttpPost]
public JsonResult Add(RoomViewModel roomModel)
{
try
{
//var apartViewModel = new ApartmentRoomViewModel();
//apartViewModel.ApartmentID = roomModel.ApartmentNameId;
var room = AutoMapper.Mapper.Map<Room>(roomModel);
var status = _roomRepository.Insert(room);
_roomRepository.Save();
//apartViewModel.RoomID = room.Id;
return Json(new { status, message = "Ekleme işlemi başarılı." }, JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
return Json(new { status = false, message = "Hata! Ekleme işlemi gerçekleştirilemedi." }, JsonRequestBehavior.AllowGet);
}
}
RoomViewModel
public class RoomViewModel
{
public int Id { get; set; }
public int DoorNumber { get; set; }
public int FloorNumber { get; set; }
public int Capacity { get; set; }
public int Fullness { get; set; }
public string ApartmentName { get; set; }
public int ApartmentNameId { get; set; }
}
UI
Room Add UI
My question is, when I want to insert the data I take from the user in the Room Add page, I can add the information gathered for the room to the Room table but I also want to add the apartment information (which is the ApartmentID of that apartment) of that room and the same room's RoomID to the ApartmentRoom table in the database and I couldn’t do that so far. If I’m not wrong so far, in that case what should I do?
Should I create another repository like ApartmentRoomRepository implementing IRepository interface and call the insert method of ApartmentRoomRepository in the Add method inside the RoomController? (This approach doesn’t seem like a correct one, as far as I understand but I’m not sure.)
Instead of the first option, should I create ApartmentRoomViewModel? In this case, how can I insert ApartmentID of the room with the room's RoomID to the ApartmentRoom table?
EDIT 1: I am using Database First approach.
EDIT 2: I have found the solution and shared as an answer below.
I've never used the automapper feature, and have separate model and viewmodel objects, but in this scenario I would create a new ApartmentRoom object as well as the new Room object, save them both to the database. The ApartmentRoom model that was created is where the linking information is stored. EF is not very obvious for many-to-many relationships.
If you sure you handle the domain classes and fluent API also I presume it is not EF core you playing with. You can get Apartment from ApartmentRepository then add it to the room apartments and add it to database.
[HttpPost]
public JsonResult Add(RoomViewModel roomModel)
{
try
{
//var apartViewModel = new ApartmentRoomViewModel();
//apartViewModel.ApartmentID = roomModel.ApartmentNameId;
var room = AutoMapper.Mapper.Map<Room>(roomModel);
var apart= apartmentRepository.GetById(roomModel.ApartmentNameId);
room.Apartments.Add(apart);
var status = _roomRepository.Insert(room);
_roomRepository.Save();
//apartViewModel.RoomID = room.Id;
return Json(new { status, message = "Ekleme işlemi başarılı." }, JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
return Json(new { status = false, message = "Hata! Ekleme işlemi gerçekleştirilemedi." }, JsonRequestBehavior.AllowGet);
}
}
I have managed to find the solution. Firstly, I have added a single line to my AutoMapperProfile.cs file.
AutoMapperProfile.cs
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Room, RoomViewModel>().ReverseMap();
CreateMap<Apartment, ApartmentViewModel>().ReverseMap();
// Added this new line
CreateMap<ApartmentRoom, ApartmentRoomViewModel>().ReverseMap();
}
}
public class AutoMapperConfig
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<AutoMapperProfile>();
});
}
}
ApartmentRoom.cs
This class is already generated from my database.
public partial class ApartmentRoom
{
public int Id { get; set; }
public int ApartmentID { get; set; }
public int RoomID { get; set; }
public virtual Apartment Apartment { get; set; }
public virtual Room Room { get; set; }
}
ApartmentRoomViewModel.cs
I have created this class.
public class ApartmentRoomViewModel
{
public int ApartmentID { get; set; }
public int RoomID { get; set; }
}
RoomController
I have changed the inside of RoomController a little. I have created an _entities object to be able to insert data into ApartmentRoom table.
public class RoomController : Controller
{
ApartmentManSysEntities _entities = new ApartmentManSysEntities();
private readonly IRoomRepository _roomRepository;
private readonly IApartmentRepository _apartmentRepository;
public RoomController(IRoomRepository roomRepository, IApartmentRepository apartmentRepository)
{
_roomRepository = roomRepository;
_apartmentRepository = apartmentRepository;
}
[HttpPost]
public JsonResult Add(RoomViewModel roomViewModel)
{
try
{
var room = AutoMapper.Mapper.Map<Room>(roomViewModel);
if (_roomRepository.Insert(room))
{
_roomRepository.Save();
var apartRoomViewModel = new ApartmentRoomViewModel
{
ApartmentID = roomViewModel.ApartmentNameID,
RoomID = room.Id
};
var apartRoom = AutoMapper.Mapper.Map<ApartmentRoom>(apartRoomViewModel);
_entities.ApartmentRoom.Add(apartRoom);
_entities.SaveChanges();
}
else
{
return Json(new { status = false, message = "Hata! Ekleme işlemi gerçekleştirilemedi." }, JsonRequestBehavior.AllowGet);
}
return Json(new { status = true, message = "Ekleme işlemi başarılı." }, JsonRequestBehavior.AllowGet);
}
catch
{
return Json(new { status = false, message = "Hata! Ekleme işlemi gerçekleştirilemedi." }, JsonRequestBehavior.AllowGet);
}
}
}
After I added a new room into Room table, I called the last inserted room's Id value to make a new insert to ApartmentRoom table.
Result
ApartmentRoom table
Room table
I need help with Entity Framework.
Controller:
public ActionResult Create([Bind(Prefix = "visit")] visit visit, [Bind(Prefix = "drugsEdition")] IEnumerable<drugsEdition> drugsEdition, [Bind(Prefix = "accessoryEdition")] IEnumerable<accessoryEdition> accessoryEdition, [Bind(Prefix = "servicesEdition")] IEnumerable<servicesEdition> servicesEdition)
{
Models.VisitDetails visitDetails = new Models.VisitDetails();
visitDetails.visit = visit;
if (ModelState.IsValid)
{
db.visit.Add(visit);
if (drugsEdition != null)
{
foreach (var item in drugsEdition)
{
item.idVisit = visit.id;
db.drugsEdition.Add(item);
}
}
if (accessoryEdition != null)
{
foreach (var item in accessoryEdition)
{
item.idVisit = visit.id;
db.accessoryEdition.Add(item);
}
}
if (servicesEdition != null)
{
foreach (var item in servicesEdition)
{
item.idVisit = visit.id;
db.servicesEdition.Add(item);
}
}
db.SaveChanges();
return RedirectToAction("Details", new { id = visit.id });
}
return View(visitDetails);
}
Model:
[Table("servicesEdition")]
public partial class servicesEdition
{
public int id { get; set; }
public int idService { get; set; }
public int idVisit { get; set; }
public double priceSell { get; set; }
[ForeignKey("idService")]
public virtual services services { get; set; }
}
In this code, I added a new visit to the database, and I want get the visit's id after the code line db.visit.Add (visit). When I add new drugsEdition and new accessoryEdition, this code is correct and added good idVisit, but when I addedservicesEdition idVisit = 0. Why 'servicesEdition' doesn't get good idVisit ?
If you add a virtual navigational property of type Visit, EF will get the corresponding visit id and fill that in when it saves a servicesEdition object on your SaveChanges method call.
I would also renaming the foreign key property to VisitId so that the referential integrity works by convention (naming convention) and you do not need to explicitly use the ForeignKey attribute. I would also recommend you to use PascalCasing when writing C# classes( not necessarily needed for your code to work)
public partial class ServicesEdition
{
public int id { get; set; }
public int idService { get; set; }
public int VisitId { get; set; }
public virtual Visit Visit { get; set; }
public double priceSell { get; set; }
[ForeignKey("idService")]
public virtual services services { get; set; }
}
If you do not want to alter the ServicesEdition entity definition like i described above, another option is to call the SaveChanges() method after you add the Visit entity and then you can access the Id property of that.
db.visit.Add(visit);
db.SaveChanges();
var visitId = db.Id;
//Now you can use visitId to save other entities
The issue is db.visit.Add(visit); does not generate id until hit db.SaveChanges();
You could add db.SaveChanges(); after db.visit.Add(visit);.
Or you will save as a graph like Shyju explained.
There is an article about how to update related data with the Entity Framework in an ASP.NET MVC application. It implements a simple University in which you can choose your courses as an instructor.
Here is simplified version for its courses controllers:
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
var selectedCoursesHS = new HashSet<string>(selectedCourses);
var instructorCourses = new HashSet<int>
(instructorToUpdate.Courses.Select(c => c.CourseID));
foreach (var course in db.Courses)
{
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedCourses)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.Courses)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
//Log the error
}
}
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
As you can see UpdateInstructorCourses fills instructorToUpdate.Courses with objects retrieved from db.Courses based on selectedCourses which is a string array.
So, is it the only way to create many-to-many relationships? Do we need to get objects from db and then Add them to our List member? Isn't it better to only pass related object's Id and update related data?
So, for many-to-many mappings, there are two ways to do it in EF:
1) Two ICollection properties (which you are already using):
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<Course> Courses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<Instructor> Instructors { get; set; }
}
In this case, EF will generate/use a mapping table, such as:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[Instructor_Courses_Mapping]([Id],[InstructorId],[CoursesId])
Now, as you pointed out, there's no way to get a list from that mapping table, making you load the collection into memory using a navigation property. So:
2) However, you can override EF's mapping-table generation with your own custom mapping entity:
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class InstructorCourse
{
public Int32 Id { get; set; }
public Int32 InstructorId { get; set; }
public Instructor Instructor { get; set; }
public Int32 CourseId { get; set; }
public Course Course { get; set; }
}
Now, EF will generate these tables:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[InstructorCourses]([Id],[InstructorId],[CourseId])
This will allow you to then query for InstructorCourses using a dbContext:
var instructorCourses = dbContext.Set<InstructorCourse>().Where( c => c.InstructorId == instructorId ).ToList()
That will return you a list of InstructorCourse objects, with all InstructorId values matching the one your looking for. Then, if you wanted to add/remove mappings:
//add item
dbContext.Set<InstructorCourse>().Add(new InstructorCourse()
{
InstructorId = instructorId,
CourseId = courseId
});
dbContext.SaveChanges();
//remove item
var itemToRemove = dbContext.Set<InstructorCourse>().FirstOrDefault( c => c.InstructorId == instructorId && c.CourseId == courseId);
dbContext.Set<InstructorCourse>().Remove(itemToRemove);
dbContext.SaveChanges();
I found this method to be much more cleaner, represents your database structure more clearer, but it does make nested Linq statements more complicated, and nulls (without proper foreign key restrictions) potentially more common.
I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.