I am trying to modify data and save changes to my database using EFCore 3.1.
but my modifications are not being saved to the database, so after further investigation I found out that after I pull my entity from the context, its state is DETACHED instead of ATTACHED,
so that's why the changes aren't being saved, because they're not tracked in the first place.
I couldn't figure out why this is happening, I made sure that I did not add AsNoTracking() when getting the entity.
Here are my classes and methods:
public class UserSettingsDataAccess : IUserSettingsDataAccess
private readonly NotificationDBContext _context;
private readonly IReminderDatesDataAccess _reminderDatesDataAccess;
public UserSettingsDataAccess(NotificationDBContext context, IReminderDatesDataAccess reminderDatesDataAccess)
{
_context = context;
_reminderDatesDataAccess = reminderDatesDataAccess;
}
public bool ToggleRemindersForAppointmentAsync(int appointment_id)
{
Appointments appointment = _appointmentDataAccess.GetByIdWithReminders(appointment_id);
if (appointment == null || appointment.Reminders == null)
return bool.Parse(null);
var x = _context.Entry(appointment).State;
appointment.Reminders.IsActive = !appointment.Reminders.IsActive;
var y = _context.Entry(appointment).State;
_context.SaveChanges();
var z = _context.Entry(appointment).State;
return appointment.Reminders.IsActive.Value;
}
//rest of code is omitted for brevity
}
This one uses another method to get the appointment, toggle its reminder , save changes and return the new reminder state. all of x,y,z variables have DETACHED value . when debugging
Here's the second class that contains the method that brings the appointment :
public class AppointmentDataAccess: IAppointmentDataAccess
{
private readonly NotificationDBContext _context;
public ReminderDatesDataAccess(NotificationDBContext context)
{
_context = context;
}
public Appointments GetByIdWithReminders(int appointment_id)
{
return _context.Appointments.Where(a => a.Id == appointment_id && a.DeletedAt == null)
.Include(a=>a.Reminders).FirstOrDefault();
}
}
Startup.cs :
services.AddDbContext<NotificationDBContext>(options => options
.UseSqlServer(Configuration.GetConnectionString("Database"))
, ServiceLifetime.Transient,ServiceLifetime.Transient);
and IUserSettingsDataAccess, IAppointmentDataAccess are just interfaces.
Can anyone point out why this is happening? and how to fix it? it's been driving me crazy for a good couple of hours . TIA!
Related
I have a .NET6 API project that allows users to fetch resources from a database (SQL Server), and update them on a web client, and submit the updated resource back for saving to db. I need to notify users if another user has already updated the same resource during editing. I tried using EF IsRowVersion property for this concurrency check.
I noticed that "normal" update procedure (just getting the entity, changing properties and saving) does not respect the RowVersion expected behavior. But if I get the entity using AsNoTracking and use the db.Update method, the concurrency check works as expected. What could be the reason, and is the db.Update the only way to force the RowVersion check? That method has the downside that it tries to update every property, not just those that have changed. Simplified and runnable console app example below:
using Microsoft.EntityFrameworkCore;
Guid guid;
using (PeopleContext db = new())
{
Person p = new() { Name = "EF", Age = 30 };
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.People.Add(p);
await db.SaveChangesAsync();
guid = p.Id;
}
using (PeopleContext db = new())
{
Person p = await db.People.FirstAsync(x => x.Id == guid);
p.Name = "FE";
p.RowVersion = Convert.FromBase64String("AAAAAADDC9I=");
await db.SaveChangesAsync(); // Does not throw even though RowVersion is incorrect
}
using (PeopleContext db = new())
{
Person p = await db.People.AsNoTracking().FirstAsync(x => x.Id == guid);
p.Name = "EFFE";
p.RowVersion = Convert.FromBase64String("AAAAAAGGC9I=");
db.People.Update(p);
await db.SaveChangesAsync(); // Throws DbUpdateConcurrencyException as expected, but updates all properties
}
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
public byte[] RowVersion { get; set; } = Array.Empty<byte>();
}
public class PeopleContext : DbContext
{
public PeopleContext(){}
public DbSet<Person> People => Set<Person>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=EFRowVersionDb;Integrated Security=True;");
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
optionsBuilder.EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>(entity =>
{
entity.Property(e => e.RowVersion)
.IsRequired()
.IsRowVersion();
});
}
}
I solved the problem by overriding the SaveChangesAsync method like this:
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
foreach (var item in ChangeTracker.Entries().Where(x=>x.State == EntityState.Modified))
{
item.OriginalValues["RowVersion"] = item.CurrentValues["RowVersion"];
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
I override that signature method cause the one without boolean calls that method. Same thing on sync version.
We are creating a spring and hibernate application and using a legacy database.
Our requirement is to get values from few database tables on server startup.
We are planning to put these values in properties files.So that we don't need to fetch DB for these values again and again.
We have used ApplicationListener to get hook on startup using following stackoverflow question:-
Listener for server starup and all spring bean loaded completely
the code being used is as below
#Component
public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
private List<Yosemitecompany> companyList = new ArrayList<Yosemitecompany>();
private YosemitecompanyRI iYosemitecompanyBO;
public SpringContextListener(){
}
public SpringContextListener(YosemitecompanyRI iYosemitecompanyBO) {
this.iYosemitecompanyBO = iYosemitecompanyBO;
}
public void onApplicationEvent(final ContextRefreshedEvent event) {
System.out.println("ApplicationListener Started"+iYosemitecompanyBO);
if(companyList == null || (companyList != null && companyList.size() <= 0) && iYosemitecompanyBO != null)
{
companyList = iYosemitecompanyBO.getCompanyDetailsWithStatus();
}
}
public List<Yosemitecompany> getCompanyList()
{
return companyList;
}
}
and this is the repository class
#Repository
#Transactional
public class YosemitecompanyRI implements IYosemitecompanyR{
static final Logger log = Logger.getLogger("YosemitecompanyDAOI");
#Autowired
private SessionFactory sessionFactory;
protected Session getSession() {
log.info(sessionFactory);
if (sessionFactory != null)
return sessionFactory.getCurrentSession();
else
return null;
}
#Override
public List<Yosemitecompany> getCompanyDetailsWithStatus()
{
List<Yosemitecompany> results = new ArrayList<Yosemitecompany>();
log.info("reached "+getSession());
if(getSession() != null)
{
log.info("executing query");
Criteria cr = getSession().createCriteria(Yosemitecompany.class);
cr.add(Restrictions.eq("cmpstatus",new BigDecimal(1)));
results = (List<Yosemitecompany>)cr.list();
}
return results;
}
}
Now on server startup..i get sessionFactory always as null..so my code for getting the list never gets executed.
i am new to spring and Hibernate.If this approach is fine then please help me to know what i am doing wrong.if there is a better approach to achieve please suggest that too.
Thanks in advance.
I have a Caliburn Micro's bootstrapper and I use the MEF as IoC. One of interfaces implementors can throw exception from it's constructor. So, when I do the following:
CompositionBatch batch = new CompositionBatch();
batch.AddExportedValue<IFrProvider>(new ShtrihFr());
Then I get exception at application startup, but I want to get it at the time of resolvation.
How to accomplish that with MEF?
Update 1.
Here what I did:
[Export(typeof (LoadingViewModel))]
public class LoadingViewModel {
public LoadingViewModel() {}
[Import]
private readonly IFrProvider frProvider;
}
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider {
[ImportingConstructor]
public ShtrihFr(int password = 1) {
}
}
So when I do the following:
protected override object GetInstance(Type serviceType, string key) {
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
IEnumerable<object> exports = container.GetExportedValues<object>(contract);
var exportedList = exports as IList<object> ?? exports.ToList();
if (exportedList.Any())
return exportedList.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
exportedList count is 0. It can't find the implementation. I cheked that the container has amidts it's parts the ShtrihFr implementation. How to solve the problem?
Update 2.
For debugging purpose I did added the following to the beggining of GetInstance method:
if (serviceType.FullName == "Microtech.Hardware.IFrProvider") {
var export = container.GetExport<IFrProvider>();
var frProvider = export.Value;
}
At line container.GetExport I get ImportCardinalityMismatchException. No exports were found that match the constraint...
One way to achieve this is the following. On application startup create Catalog that contains all composable parts of your application and initialize the CompositionContainer. Here is the code.
var catalog = new DirectoryCatalog(path to the directory that contains your dlls, "*.dll");
var compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts();
Next mark your ShtrihFr class with Export attribute:
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider
{
public ShtrihFr()
{
throw new NotImplementedException("Not Implemented");
}
}
This way the CompositionContainer through the DirectoryCatalog will only get list of composable parts and their contracts. Actual instances will not be created, so you won't get exception on application startup.
When you need instance of the ShtrihFr class, you can use one of the following lines:
var part = compositionContainer.GetExportedValue<IFrProvider>();
The above line will throw exception if the constructor throws exception, which is the case now.
var part = compositionContainer.GetExport<IFrProvider>();
The above line will not throw exception when executed, but to get the actual instance you will need to access the Value property of the part variable, which will throw exception.
Update 1:
Add default constructor to the ShtrihFr class, so it look like this:
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider
{
public ShtrihFr(int password = 1)
{
}
[ImportingConstructor]
public ShtrihFr(int password = 1)
{
}
}
And instead of using the overridden GetInstance method, use something like this, if you need collection of exported parts:
public IEnumerable<T> GetInstances<T>(Type constraint = null)
{
if (constraint == null)
return compositionContainer.GetExportedValues<T>();
return compositionContainer.GetExportedValues<T>(AttributedModelServices.GetContractName(constraint));
}
or something like this, if you need single exported part:
public T GetInstance<T>(Type constraint = null)
{
if (constraint == null)
return compositionContainer.GetExportedValue<T>();
return compositionContainer.GetExportedValue<T>(AttributedModelServices.GetContractName(constraint));
}
In both methods, the type parametar T is the type of the exported part you want to instantiate. The constraint parameter passed to both methods is additional constraint that is optional and if needed is applied to the Export attribute of the classes.
I have the following class that I use in order to populate a combo box:
public class DamageTypeList
{
static Begbil2Entities _DB = new Begbil2Entities();
public static List<HUB_DamageTypes> _list = (from d in _DB.HUB_DamageTypes orderby d.DamageOrder select d).ToList();
public static List<HUB_DamageTypes> TList
{
get
{
return _list;
}
}
}
In the xaml file I add it like this:
<UserControl.Resources>
<me:DamageTypeList x:Key="DamageTypeList"/>
The xaml line creates an error (ONLY in design time, it runs pefectly at runtime):
Cannot create an instance of "DamageTypeList". C:\HUB\HUB\HubbCostOfferPage.xaml
I have found some suggestions to solve it by using:
if (!DesignerProperties.IsInDesignTool)
But how do I use it to solve my problem?
You can use the flag DesignerProperties.IsInDesignTool to prevent the DB creation and to use hardcoded entities in your list.
public class DamageTypeList
{
static Begbil2Entities _DB;
public static List<HUB_DamageTypes> _list;
public static Begbil2Entities DB
{
get
{
if(_DB == null && !DesignerProperties.IsInDesignTool)
_DB = new Begbil2Entities();
return _DB;
}
}
public static List<HUB_DamageTypes> TList
{
get
{
if(_list == null)
{
if(!DesignerProperties.IsInDesignTool)
_list = (from d in DB.HUB_DamageTypes orderby d.DamageOrder select d).ToList();
else
_list = new List<HUB_DamageTypes>(){
// Initialize it with hardcoded values
};
}
return _list;
}
}
}
Before doing that, tough, I would investigate a little further what is the cause of the design-time exception, as #fhlamarche suggested. You can try to debug the design time execution, is not that hard. See this link.
The designer attempts to call the default constructor but your class doesn't have one.
You just need to add a private or internal default constructor to your class.
How can I implement cancelation of editing an object using MVVM.
For example: I have a list of customers. I choose one customer an click the button "Edit", a dialog window(DataContext is binded to CustomerViewModel) opens and I start editing customer's fields. And then I decide to cancel editing, but the fields of the customer have been already changed, so how can I return a customer to its previous state in MVVM?
Check out the IEditableObject interface. Your Customer class should implement that, and your commands can execute BeginEdit / CancelEdit / EndEdit as appropriate.
You can use binding with UpdateSourceTrigger=Explicit. Here you can find more information how this can be implemented.
One super easy way, if your object is already serializable, such as if you are using WCF. You can serialize your original object into an internal field. If, your object isn't serializable, then just use AutoMapper to create a copy of your object with one line of code.
Order backup = Mapper.Map<Order, Order>(order);
When you handle your CancelCommand, just call AutoMapper in reverse. Since your properties already have a change notification everything just works. Its possible you could combine these techniques with IEditableObject, if you need and want to write the extra code.
In this article, Raul just reload the object from the DB. I guess it's less trouble than the solution Kent proposes.
internal void Cancel(CustomerWorkspaceViewModel cvm)
{
Mainardi.Model.ObjectMapping.Individual dc = cvm.DataContext
as Mainardi.Model.ObjectMapping.Individual;
int index = 0;
if (dc.ContactID > 0 && dc.CustomerID > 0)
{
index = _customerCollectionViewModel.List.IndexOf(dc);
_customerCollectionViewModel.List[index] =
_customerBAL.GetCustomerById(dc.CustomerID);
}
Collection.Remove(cvm);
}
Based on Камен Великов's answer:
You can mark your bindings as to be updated manually by defining
<TextBox Name="yourTextBox" Text="{BindingPath=YourBinding, UpdateSourceTrigger=Explicit}" />
in your view (XAML). Then, you have to write the changes from your UI in ViewModel by calling
yourTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
when Save is clicked.
Please note, if there are updated to the binding source triggered from anything else, they are still shown directly in the UI.
I had this problem too. I solved it using "The Memento Pattern Design". With this pattern you could easy save a copy of your original object and, in selectedIndexChange (of a control) or in the Cancel button, you could restore easy the prior version of your object.
An example of use of this pattern is available at How is the Memento Pattern implemented in C#4?
An example of code:
If we have a class User with properties UserName Password and NombrePersona we need to add methods CreateMemento and SetMemento:
public class Usuario : INotifyPropertyChanged
{
#region "Implementación InotifyPropertyChanged"
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private String _UserName = "Capture su UserName";
public String UserName
{
get { return _UserName; }
set { _UserName = value; RaisePropertyChanged("UserName"); }
}
private String _Password = "Capture su contraseña";
public String Password
{
get { return _Password; }
set { _Password = value; RaisePropertyChanged("Password"); }
}
private String _NombrePersona = "Capture su nombre";
public String NombrePersona
{
get { return _NombrePersona; }
set { _NombrePersona = value; RaisePropertyChanged("NombrePersona"); }
}
// Creates memento
public Memento CreateMemento()
{
return (new Memento(this));
}
// Restores original state
public void SetMemento(Memento memento)
{
this.UserName memento.State.UserName ;
this.Password = memento.State.Password ;
this.NombrePersona = memento.State.NombrePersona;
}
Then, we need a class Memento that will contain the "copy" of our object like this:
/// <summary>
/// The 'Memento' class
/// </summary>
public class Memento
{
//private Usuario _UsuarioMemento;
private Usuario UsuarioMemento { get; set; }
// Constructor
public Memento(Usuario state)
{
this.UsuarioMemento = new Usuario();
this.State.UserName = state.UserName ;
this.State.Password = state.Password ;
this.State.NombrePersona = state.NombrePersona ;
}
// Gets or sets state
public Usuario State
{
get { return UsuarioMemento; }
}
}
And we need a class that will generate and contains our memento object:
/// <summary>
/// The 'Caretaker' class
/// </summary>
class Caretaker
{
private Memento _memento;
// Gets or sets memento
public Memento Memento
{
set { _memento = value; }
get { return _memento; }
}
}
Then for implement this pattern we have to create an instance of Caretaker class
Caretaker creadorMemento = new Caretaker();
And create our memento object when a new user was selected for edit, for example in selectedIndexChange after the SelectedUser has been initializing, I use the method for event RaisPropertyChanged like this:
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
if (prop == "RowIndexSelected") // This is my property assigned to SelectedIndex property of my DataGrid
{
if ((this.UserSelected != null) && (creadorMemento .Memento != null))
{
this.UserSelected.SetMemento(creadorMemento .Memento);
}
}
if (prop == "UserSelected") // Property UserSelected changed and if not is null we create the Memento Object
{
if (this.UserSelected != null)
creadorMemento .Memento = new Memento(this.UserSelected);
}
}
An explication for this, when selectedIndexChanged change value we check if UserSelected and our memento object are not null means that our actual item in edit mode has changed then we have to Restore our object with the method SetMemento.
And if our UserSelected property change and is not null we "Create our Memento Object" that we will use when the edit was cancel.
For finish, we have use the SetMemento method in every method that we need to cancel the edition, and when the edit has commited like in the SaveCommand we can set null our memento object like this this.creadorMemento = null.
You could also, in your ViewModel copy the model's state to internal fields, and then expose these and then only set them on the model, when the user actually commits the change.
Problem could be, that on-the-fly validation will be more troublesome if validation relies on the entity being updated - if this is a requirement you could create a clone of the model to work on and then merging the clone with the actual entity when it is saved.