So, I have this User Entity
using System;
using System.Collections.Generic;
using System.Text;
using Transport.Data.Entities;
namespace Transport.Data.Entities
{
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public DateTime BirthDay { get; set; }
public String Email { get; set; }
public String UserName { get; set; }
public bool IsActive { get; set; }
public List<Viaje> Viaje { get; set; }
}
}
and here is the ViewModel for the Entity
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using Transport.Data.Entities;
using Transport.Model.Infraestructure;
namespace Transport.Model.ViewModel
{
public class ViajeViewModel : BaseViewModel
{
public string Route { get; set; }
public string Destination { get; set; }
public string Origin { get; set; }
public int Price { get; set; }
public DateTime DepartureTime { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public Viaje Viaje { get; set; }
public List<User> User { get; set; }
}
}
This is my Update Repository
DataResult IRepository<T>.Update(T entity)
{
DataResult result = new DataResult();
try
{
result.Data = entity;
context.SaveChanges();
result.Successfull = true;
}
catch (Exception ex)
{
result.LogError(ex);
result.Successfull = false;
}
return result;
}
And my update service
public ServiceResult Update(Vm viewModel)
{
ServiceResult serviceResult = new ServiceResult();
var ToUpdate = this.Repository.GetById((int)viewModel.Id).Data;
if (ToUpdate == null)
{
serviceResult.Success = false;
serviceResult.ResultTitle = "ERROR: Record No Found";
//serviceResult.Messages.Add(Error.GetErrorMessage(Error.RecordNotFound));
return serviceResult;
}
var Entity = MapperHelper.Instance.Map<Vm, Ent>(viewModel);
var result = this.Repository.Update(Entity);
serviceResult.Success = result.Successfull;
serviceResult.ResultTitle = (result.Successfull ? Error.GetErrorMessage(Error.CorrectTransaction) : Error.GetErrorMessage(Error.InternalServerError));
//serviceResult.Messages.Add(result.Successfull ? "Updated" : "Failed");
serviceResult.ResultObject = MapperHelper.
Instance.Map<Ent, Vm>(result.Data);
this.Repository.SaveChanges();
return serviceResult;
}
This is my update user controller
[HttpPost("users/edit/{id}")]
public ActionResult UserEdit(UserViewModel userViewModel)
{
var users = userService.Update(userViewModel).ResultObject;
return RedirectToAction("Index", "Users");
}
The repository and service are doing their jobs looking for an user by an id and updating their values but when the UserEdit Controller it's done my changes are not being saved in the database.
Can anyone give me some advice to resolve this problem ?
EF relies on internal entity change tracking to determine what operations it needs to perform in the database. All your Update method does is call SaveChanges, so simply changes being made to the entity are not being tracked for some reason, and when you call SaveChanges, EF sees no work it needs to do, and just returns. As to why your entity changes aren't being tracked, there's not enough of your repository here to tell.
However, I will say that this is one of the paramount reasons to not use the repository pattern with EF. It's far too easy to do stuff that borks EF's change tracking, and 99 times out of 100, that's exactly what developers do. When you use an ORM like EF, that is your data layer. It implements the repository and unit of work patterns already. Not every "layer" in your architecture has to actually be owned by you, and this is a critical mistake far too many developers make. Just use your context directly. That's what it's for.
Now, purists might argue that you'll have a hard dependency on EF. Well, guess what? You do regardless. You've chosen it as an ORM, and that decision doesn't and shouldn't come lightly. What if you want to switch it out with something else down the line? That question always gets raised as well. Simply, you won't. The friction involved in switching out something like an ORM is such that it will never be a business priority.
Nevertheless, if you want to truly abstract the dependency, you should be looking at patterns like CQRS or microservices, which unlike a redundant and useless repository layer, actually do add value to your application. However, those patterns are complex to implement and overkill for most applications.
Related
The api response returns as if the function ran without problems but when i look the database the row simple is not there.
I don't get any error messages.
Even if I try to add one exam directly through _context.Exames it won't add.
I'm getting really frustrated because I don't even know where or what I should look for as the api returns that the method run successfully
I'm using ASP.NET Core 5 and EF Core with MySQL.
Here is the code:
public class Account
{
public int Id { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public List<Exame> Exames { get; set; }
public List<Consulta> Consultas { get; set; }
}
public class Exame
{
[Key]
public int ExameID { get; set; }
public long Image { get; set; }
}
public class DataContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<Exame> Exames { get; set; }
}
[HttpPost("exame")]
public IActionResult CreateExame(ExameRequest exame)
{
_accountService.CreateExame(exame);
return Ok(new { message = "Exame added successfully" });
}
public void CreateExame(ExameRequest model)
{
var account = _context.Accounts.SingleOrDefault(x => x.Id == model.AccountId);
var exame = new Exame();
exame.ExameID = model.Id;
exame.Image = model.Image;
if (account.Exames == null)
{
account.Exames = new List<Exame>();
}
account.Exames.Add(exame);
_context.Accounts.Update(account);
_context.SaveChangesAsync();
}
It looks like it's returning before the Save has finished, try altering it to something like:
[HttpPost("exame")]
public Task<IActionResult> CreateExame(ExameRequest exame)
{
var result = await _accountService.CreateExame(exame);
// check the value and return the appropriate message.
return Ok(new { message = "Exame added successfully" });
}
public async Task<int> CreateExame(ExameRequest model)
{
// removed other code
return await _context.SaveChangesAsync();
}
Since the call to SaveChangesAsync() is async, the code is making the call and moving straight back to the api call and returning OK. The result of the SaveChangesAsync is as follows.
A task that represents the asynchronous save operation. The task
result contains the number of state entries written to the underlying
database. This can include state entries for entities and/or
relationships. Relationship state entries are created for many-to-many
relationships and relationships where there is no foreign key property
included in the entity class (often referred to as independent
associations).
I have the same problem as I am following a course.
it is probably a versioning problem, cause my code is exactly the same as the instructor but I can't get the result I want.
I think it is because of Microsoft.EntityFrameworkCore.InMemory version.
the 5th version apparently don't have this problem but 7 does.
I have two entity classes that have a one-to-many relationship.
public class Call : IEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string Username { get; set; }
public virtual ICollection<Call> Calls { get; set; }
}
And I have a view model for 'Call' operations on the web layer.
public class CallVm : IViewModel
{
public string Id { get; set; }
public string UserFullname { get; set; }
}
And I use a method to convert my 'Call' object to 'CallVm' object.
This method is briefly as follows.
public CallVm MapCallVm(Call call)
{
return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Fullname };
}
When I read the 'Call' entity from the database, I sometimes include 'User' and sometimes I don't. When I do not include it, there is no User property definition in the Call object because it is lazy loading. Therefore, I get the following error in MapCallVm method.
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Is there a way to check this? I just want to assign UserFullname = call.User?.Fullname when there is a eager load.
The only solution I can think of is controlling with try-catch. Is there a different solution?
You can use DbReferenceEntry.IsLoaded Property.
Gets or sets a value indicating whether the entity has been loaded
from the database.
if (_dbContext.Entry(Call).Reference(e => e.User).IsLoaded)
Updated
If you are getting value without dbContext, you should force the query to Eager loading instead.
Read the following post to have a better understanding.
Should we disable lazy loading of Entity Framework in web apps?
As #Phong's answer - avoid passing DbContext. Normally, your repository class should map DB entities to simple POCO/DTO objects.
I suggest to introduce mapper class. This will help you to unit test your logic
// Interface to inject to repository
public interface ICallMapper
{
CallVm Map(Call call);
}
public class CallMapper : ICallMapper
{
public CallVm Map(Call call)
{
return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Username };
}
}
Pass mapper to repository and ensure that your objects are not connected with DB anymore
public class CallRepository : ICallRepository
{
private readonly ICallMapper _callMapper;
public CallRepository(ICallMapper callMapper)
{
_callMapper = callMapper;
}
public IList<CallVm> GetList()
{
// Call DB and get entities
var calls = GetCalls();
// Map DB entities to plain model
return calls.Select(_callMapper.Map).ToList();
}
}
This lets you to get rid of your error. And makes your program more structable and testable.
I'm currently building an ASP.NET MVC 5 application with EF 6.1.3 linked to an SQL Server database.
My issue: I receive the following error when I attempt to save 2 or more instantiations of "PurchaseInvoiceSplitsViewModel", no issue when only 1 saved.
Unable to determine the principal end of the 'WebAS.Models.PurchaseInvoiceSplits_VehicleStock' relationship.
Multiple added entities may have the same primary key.
The issue arises at line _context.SaveChanges();
My ViewModel:
public class PurchaseInvoiceHeaderFormViewModel
{
public PurchaseInvoiceHeaderFormViewModel()
{
Splits = new List<PurchaseInvoiceSplitsViewModel>();
}
public List<PurchaseInvoiceSplitsViewModel> Splits { get; set; }
}
public class PurchaseInvoiceSplitsViewModel
{
public PurchaseInvoiceSplits PurchaseInvoiceSplits { get; set; }
public VehicleInformation VehicleInformation { get; set; }
public VehicleStock VehicleStock { get; set; }
}
My Models:
public class VehicleStock
{
[Key]
public int VehicleStockId { get; set; }
public int VehicleInformationId { get; set; }
public VehicleInformation VehicleInformation { get; set; }
}
public class PurchaseInvoiceSplits
{
[Key]
public int PurchaseInvoiceSplitsId { get; set; }
public int? VehicleStockId { get; set; }
public VehicleStock VehicleStock { get; set; }
}
public class VehicleInformation
{
[Key]
public int VehicleInformationId { get; set; }
}
Controller:
[HttpPost]
public ActionResult Save(PurchaseInvoiceHeaderFormViewModel viewModel)
{
_context.PurchaseInvoiceHeader.Add(viewModel.PurchaseInvoiceHeader);
foreach (var Split in viewModel.Splits)
{
Split.PurchaseInvoiceSplits.VehicleStockId =
Split.VehicleStock.Id;
Split.VehicleStock.VehicleInformationId =
Split.VehicleInformation.Id;
_context.VehicleInformation.Add(Split.VehicleInformation);
_context.VehicleStock.Add(Split.VehicleStock);
_context.PurchaseInvoiceSplits.Add(Split.PurchaseInvoiceSplits);
}
_context.SaveChanges();
return RedirectToAction("xxx", "xxx");
}
I have spent ages looking into this and I believe it is related to EF assigning temporary Id's of 0 to the models. This is fine when there is one model but appears to cause navigation/reference issues with multiple model instantiations. Answers to other forum posts suggest using temporary Id's which I have attempted but have not managed to get to work. Any help will be greatly appreciated. Please feel free to ask for further clarification/code snippets.
Issue resolved using Entity Framework Transactions (note, only appear to be available for version 6+). Very useful information found here:
Entity Framework Tutorials
MSDN turotials
Modifications only required within Controller:
[HttpPost]
public ActionResult Save(PurchaseInvoiceHeaderFormViewModel viewModel)
{
using (CustData Tcontext = new CustData())
{
using (DbContextTransaction transaction =
Tcontext.Database.BeginTransaction())
{
try
{
Tcontext.PurchaseInvoiceHeader.Add(viewModel.PurchaseInvoiceHeader);
Tcontext.SaveChanges();
foreach (var Split in viewModel.Splits)
{
Tcontext.VehicleInformation.Add(Split.VehicleInformation);
Split.VehicleStock.VehicleInformationId = Split.VehicleInformation.Id;
Tcontext.VehicleStock.Add(Split.VehicleStock);
Split.PurchaseInvoiceSplits.VehicleStockId = Split.VehicleStock.Id;
Split.PurchaseInvoiceSplits.PurchaseInvoiceNumberId = viewModel.PurchaseInvoiceHeader.Id;
Tcontext.PurchaseInvoiceSplits.Add(Split.PurchaseInvoiceSplits);
Tcontext.SaveChanges();
}
transaction.Commit();
}
catch (Exception ex)
{
Console.WriteLine("Error occurred." + ex);
}
}
}
return RedirectToAction("xxx", "xxx");
}
I believe that the order in which the .Add occurs is also important, hence the rearrange.
I had the same error and an unsimilar code setup, but...
I took a look at the above answer and noticed the comment at the bottom: I believe that the order in which the .Add occurs is also important, hence the rearrange.
What I did was arrange the saves in my code and was able to eliminate the error. I just thought I would post this for future error searches.
My application has a business logic layer, and a data access layer. I want to give only the data access layer access to the database model. Now, I can easily do this, but then my UI classes cannot access the database classes like Reminder:
namespace Database
{
using System;
using System.Collections.Generic;
public partial class Reminder
{
public long Id { get; set; }
public string Name { get; set; }
public string Date { get; set; }
public string RepeatType { get; set; }
public string Note { get; set; }
public long Enabled { get; set; }
public string SoundFilePath { get; set; }
public string PostponeDate { get; set; }
public Nullable<long> EveryXCustom { get; set; }
public string RepeatDays { get; set; }
public Nullable<long> DayOfMonth { get; set; }
}
}
which is inside the database class library
I use this reminder class to store reminders. In my UI classes I use this class for various reasons.
To make use of this Reminder class, I simply add a reference to the class library that needs to use it. This works fine, but the problem is that every class library that references this, can alter the database like this.
If I'm not using Entity Framework, I could simply have a Reminder class outside the model (because there is no model) and load reminders from the database into that and extract them without using Entity Framework.
Here's an example of why I need to use the Reminder class in my UI classes (this is just a small code sample of one UI class)
This code is inside a timer that ticks every 30 seconds
// We will check for reminders here every 30 seconds.
foreach (Reminder rem in BLReminder.GetReminders())
{
// Create the popup. Do the other stuff afterwards.
if(rem.PostponeDate != null && Convert.ToDateTime(rem.PostponeDate) <= DateTime.Now && rem.Enabled == 1)
{
allowRefreshListview = true;
// temporarily disable it. When the user postpones the reminder, it will be re-enabled.
rem.Enabled = 0;
BLReminder.EditReminder(rem);
MakePopup(rem);
}
else if(Convert.ToDateTime(rem.Date.Split(',')[0]) <= DateTime.Now && rem.PostponeDate == null && rem.Enabled == 1)
{
allowRefreshListview = true;
// temporarily disable it. When the user postpones the reminder, it will be re-enabled.
rem.Enabled = 0;
BLReminder.EditReminder(rem);
MakePopup(rem);
}
}
GetReminders will do get the reminders from the database and put them in reminder objects
using (RemindMeDbEntities db = new RemindMeDbEntities())
{
localReminders = (from g in db.Reminder select g).ToList();
db.Dispose();
}
You can create separate project called i.e. Shared and put there all classes which are used in many projects. Then you need to reference this project by UI project and data access project (and by others which use these classes).
Both will have access to shared classes and UI won't be able to call data access layer directly.
You can also create interface outside of data access layer but if your classes are DTOs (Data Transfer Object) first option will be better.
If im not using the entity framework, i could simply have a reminder
class outside the model
You could create an interface instead of a class outside of the model in a shared assembly:
public interface IReminder
{
public long Id { get; }
public string Name { get; }
public string Date { get; }
public string RepeatType { get; }
public string Note { get; }
public long Enabled { get; }
public string SoundFilePath { get; }
public string PostponeDate { get; }
public Nullable<long> EveryXCustom { get; }
public string RepeatDays { get; }
public Nullable<long> DayOfMonth { get; }
}
Your Entity can than implement the interface:
public partial class Reminder : IReminder
{
//...
}
Maybe you want to make your Entities only internal visible and expose public service methods like IEnumerable<IReminder> GetReminders()
I just started using Entity Framework and it created a Context class which I can use to get all the data i need from it. But I am facing an issue on how I should organize my code, by watching the demos, the person just uses the framework and codes everything on a console application. What is the best way to use Entity Framework and that it looks clean?, what I mean by this is...right now using aspx pages, I could just use the aspx.cs to get the data or save the data. But I do not want this, I would like it to be more organized although the Entity Framework did almost everything by creating the objects etc.. but still, I need to use things like
using(var myobject = new MyContextData())
{
blah blah..
}
would you say that it would be nicer to write classes that would wrap these calls?. I would really appreciate any inputs as it would really make me a better programmer using the entity framework.
Regards
This question should everyone, who provides some tutorial about EF, ask. It is hard to say what is the best way, but put all code in the codebehind classes (aspx.cs) does not help extensibility and testability. Please, try to read this article:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Not only it is official tutorial on asp.net, but it mostly shows, that Framework EF could be used correctly in currently fancy Repository pattern
Edit:
I think that Generic Repository is Anti Pattern. But I do not understand #TomTom comment.
Original Answer:
As Radim Köhler mentioned you need to implement Repository and Unit of Work patterns
But the article he provided in my opinion is not fully correct.
At my current job I use following implementation of these patterns.
For example, we have three types of entities: Person, Good and Order. I created repository for Persons. In common case Repository must not be generic. It must contain methods which represent specific queries for this entity. So by looking at the interface of repository you can tell what kinds of queries executed for entity (Person, e.g.). As you will see I created DTO for Person called PersonWrap. For creating PersonWrap from Person and updating Person from PersonWrap you can use AutoMapper instead of PersonWrap() constructor and Update() method. Because EntityFramework DbContext implements Unit of Work pattern, you just need to provide created DbContext to repository methods. If repository method is a separate action and you do not need DbContext outside of this method you can create and dispose it inside this method.
public class Person {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public DateTime RegistrationDate { get; set; }
public List<Order> Orders { get; set; }
}
public class Good {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Order {
public int Id { get; set; }
public Person Person { get; set; }
public Good Good { get; set; }
public int Count { get; set; }
}
public class MyDbContext: DbContext
{
public IDbSet<Person> Persons { get { return Set<Person>(); }}
public IDbSet<Good> Goods { get { return Set<Good>(); }}
public IDbSet<Order> Orders { get { return Set<Order>(); }}
}
public class PersonRepository {
public IEnumerable<Person> GetAll() {
using (var context = new MyDbContext()) {
return context.Persons.ToList();
}
}
public IEnumerable<Person> GetLastWeekPersons() {
using (var context = new MyDbContext()) {
return context.Persons.Where(p => p.RegistrationDate > new DateTime().AddDays(-7)).ToList();
}
}
public Person GetById(int id, MyDbContext context) {
return context.Persons.Include(p => p.Orders).FirstOrDefault(p => p.Id == id);
}
public Person GetById(int id) {
using (var context = new MyDbContext()) {
return GetById(id, context);
}
}
}
public class PersonWrap {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public int OrderCount { get; set; }
public PersonWrap(Person person) {
Id = person.Id;
FirstName = person.FirstName;
SecondName = person.SecondName;
OrderCount = person.Orders.Count;
}
public void Update(Person person) {
person.FirstName = FirstName;
person.SecondName = SecondName;
}
}
public class PersonDetailsViewController {
public PersonWrap Person { get; protected set; }
public PersonDetailsViewController(int personId) {
var person = new PersonRepository().GetById(personId);
if (person != null) {
Person = new PersonWrap(person);
}
}
public void Save() {
using (var context = new MyDbContext()) {
var person = new PersonRepository().GetById(Person.Id, context);
Person.Update(person);
context.SaveChanges();
}
}
}
You are on the right track for creating classes to handle your EF.
The biggest benefit for doing it this way is able to unit test easily.
Test early and test often is always a good idea.
I suggest putting your EF related classes in a separate project.