I have a very simple server model that includes a parent entity with a [Composition] list of child entities. In my client, I have 2 functions. One function removes all the child entities from the parent and the other removes all and also edits a property on the parent entity.
When I simply remove all child entities and SubmitChanges(), all is well.
When I remove all child entities and edit the parent and SubmitChanges(), there are still pending changes (HasChanges == true) when the SubmitChanges() callback is fired.
I am using Silveright 4 RTM and RIA Services 1.0 RTM.
Any ideas what is going on here?
Here are the server entities:
public class RegionDto
{
public RegionDto()
{
Cities = new List<CityDto>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
[Include]
[Composition]
[Association("RegionDto_CityDto", "Id", "RegionId")]
public List<CityDto> Cities { get; set; }
}
public class CityDto
{
[Key]
public int Id { get; set; }
public int RegionId { get; set; }
public string Name { get; set; }
}
And here is the client code:
public static class CState
{
private static RegionDomainContext _domainContext;
public static RegionDomainContext DomainContext
{
get
{
if (_domainContext == null)
{
_domainContext = new RegionDomainContext();
}
return _domainContext;
}
}
public static void SaveChanges()
{
DomainContext.SubmitChanges(op =>
{
if (DomainContext.HasChanges && !DomainContext.IsSubmitting)
{
var w = new ChildWindow();
w.Content = "The DomainContext still has unsaved changes.";
w.Show();
}
}, null);
}
}
public partial class MainPage : UserControl
{
private void ClearCitiesEditRegion(object sender, RoutedEventArgs e)
{
var region = (RegionDto)regionList.SelectedItem;
if (region != null)
{
region.Name += "*";
while (region.Cities.Count > 0)
{
region.Cities.Remove(region.Cities.First());
}
CState.SaveChanges();
}
}
private void ClearCities(object sender, RoutedEventArgs e)
{
var region = (RegionDto)regionList.SelectedItem;
if (region != null)
{
while (region.Cities.Count > 0)
{
region.Cities.Remove(region.Cities.First());
}
CState.SaveChanges();
}
}
}
When you run this code the ChildWindow is only shown when you the ClearCitiesEditRegion() method is called. The only difference between this and the ClearCities() method is the line where I edit the region.Name property.
You can also download a sample project that reproduces this here: http://dl.dropbox.com/u/2393192/RIA_Services_Problem.zip
I received an answer to this on the Silverlight forums. Apparently this is a bug in RIA Service 1.0. The following is Matthew's response on the Silverlight forums.
Yes, I've confirmed this is a bug.
Thanks for reporting it and providing
the repro. As you discovered, the bug
will only repro in composition
scenarios where the parent has been
modified in addition to one or more
children. The workaround is to do an
explicit AcceptChanges if the submit
was successful. For example, here is
the code you'd write in a submit
callback:
if (!submitOperation.HasError)
{
((IChangeTracking)ctxt.EntityContainer).AcceptChanges();
}
This will accept all changes and reset
change state correctly.
Related
I am trying to build a Patient's edit form that will get Patient data from a database using EF Core and the DbContext derived AppDbContext.
On the same form there will be a dropdown combobox that will be displaying all the available insurances (fetched from the database).
What I want to achieve is the ability, the user to be able to select an existing insurance (which is obvious and easily achieved) or to add a new one by typing it into the combobox and this new entry should be selected as the patient's insurance from now on until the SaveChanges takes place and when the same patient is reopened for editing.
I use two BindingSources one for the patient itself (bsPatient) and one for the insurances list (bsInsurances).
I have the following two models (1:many relationship)
public class Insurance
{
public int Id { get; set; }
public string Name { get; set; } = String.Empty;
public virtual ObservableCollectionListSource<Person> Persons { get; } = new();
}
public class Person
{
public int Id { get; set; }
public string LastName { get; set; } = String.Empty;
public string FirstName { get; set; } = String.Empty;
public DateTime DOB { get; set; }
public int InsuranceId { get; set; }
public Insurance Insurance { get; set; } = null;
}
And this is the DbContext:
public class AppDbContext : DbContext
{
private const string DatabaseFileName = "MyPatientsDB.sqlite3";
public DbSet<Person> Persons { get; set; }
public DbSet<Insurance> Insurances { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder
.UseSqlite(#$"Data Source={DatabaseFileName}");
On the form there will be
public partial class PatientForm : Form
{
private AppDbContext _db = new();
private int _patientId = 0;
ObservableCollection<Insurance> _insurancesList = new();
public PatientForm(int patientId)
{
InitializeComponent();
_patientId = patientId;
}
protected async override void OnLoad(EventArgs e)
{
base.OnLoad(e);
await _db.Insurances.LoadAsync();
_insurancesList = new ObservableCollection<Insurance>(_db.Insurances.Local);
bsInsurances.DataSource = _insurancesList;
bsPatient.DataSource = await _db.Persons.FirstOrDefaultAsync(p => p.Id == _patientId);
}
protected async override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
bsInsurances.EndEdit();
if (cbInsurances.FindStringExact(cbInsurances.Text) == -1)
{
var newInsurance = new Insurance { Id = 0, Name = cbInsurances.Text };
_db.Insurances.Local.Add(newInsurance);
}
bsPatient.EndEdit();
await _db.SaveChangesAsync();
_db?.Dispose();
}
}
So far, I am able to save correctly the Insurance selection of the combobox when an already existing item is selected. The problem arises when the user inserts a new insurance entry into the combo textbox. This new entry can not be saved to the db and be displayed the next time the same patient is opened for editing.
I would be grateful if someone could point me towards which direction to follow to achieve this. I mean, while editing a patient's data how to be able to insert a new entry into the insurances combo and this new entry to be persisted into the db and be displayed and selected the next time the patient is opened for editing.
I think I've found a solution. I don't know if it is the best one but it seems to be working at least into my project. I am just referring it in case someone else has the same query.
Please if anyone has a better solution I would be grateful for his/her help.
protected async override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
bsInsurances.EndEdit();
if (cbInsurances.FindStringExact(cbInsurances.Text) == -1)
{
if (bsPatient != null && bsPatient.DataSource != null)
{
(bsPatient.DataSource as Person).Insurance = new Insurance() { Name = cbInsurances.Text as string };
}
}
bsPatient.EndEdit();
await _db.SaveChangesAsync();
_db?.Dispose();
}
Hello I have a strange issue here. I have a Project model that has a foreign key to the Company model. The thing is that when I attach my Project property in order to update it, then it updates all other primitive fields, except for the Company.
The Project model looks like this:
public class Project
{
[Key]
public int ID { get; set; }
[Index(IsUnique = true)]
public string Name { get; set; }
public virtual Company Company { get; set; }
public bool ExportProjectName { get; set; }
public DateTime CreatedAt { get; set; }
}
Then what I do, is I have a ProjectsViewModel, that gets all Projects from the Database. I wrap each of them then in a ProjectViewModel which exposes some fields of the Project and also has a saving method. I think the ProjectsViewModel implementation may not be that important so I'll paste only the two lines that populate the ProjectViewModels:
var projects = dbcontext.Projects.Include(x => x.Company).ToList().Select(x => new ProjectViewModel(x));
this.ListOfProjects = new ObservableCollection<ProjectViewModel>(projects);
Then I have the ProjectViewModel. Take a look at the SaveProject method:
public class ProjectViewModel : INotifyPropertyChanged
{
private Project _project;
public Project Project
{
get { return _project; }
set
{
_project = value;
NotifyPropertyChanged("Project");
}
}
public int ID
{
get { return Project.ID; }
set
{
Project.ID = value;
NotifyPropertyChanged("ID");
}
}
public string Name
{
get { return Project.Name; }
set
{
Project.Name = value;
NotifyPropertyChanged("Name");
}
}
public Company Company
{
get { return Project.Company; }
set
{
Project.Company = value;
NotifyPropertyChanged("Company");
}
}
public ProjectViewModel(Project project)
{
this.Project = project;
}
public void SaveProject()
{
using (DbContext dbcontext = new DbContext())
{
// At this state this.Project and this.Company exist in the database
dbcontext.Companies.Attach(this.Company);
dbcontext.Projects.Attach(this.Project);
dbcontext.Entry(this.Project).State = EntityState.Modified;
dbcontext.SaveChanges();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
And that's it. I have the SaveProject bound to a command, which I just trigger.
And then in the logs I can only see such a query:
Opened connection at 08/12/2018 17:47:21 +01:00
Started transaction at 08/12/2018 17:47:21 +01:00
UPDATE "public"."Projects" SET "Name"=#p_0,"ExportProjectName"=#p_1,"CreatedAt"=#p_2 WHERE "ID" = #p_3
So all properties are there, except for the Company. If that's important - I'm using PostgreSQL with NpgSql. I saw many question on SO in regards to the related object not being updated itself, but I couldn't find any question where the relation would be only broken. Hope somebody can help!
EDIT:
BTW the code below would work, but I do not want to assign all properties by myself and would want to avoid getting the object from the DB one more time. And I want to know, why the relation is not updated in the first case, as it doesn't make sense for me.
dbcontext.Companies.Attach(this.Company);
var p = dbcontext.Projects.Single(x => x.ID == this.ID);
p.Name = this.Name;
p.Company = this.Company;
dbcontext.SaveChanges();
i am Trying to fit in breezeJS with my existing architecture. I have a structure like
html/JS/Angular :: based view using hot-towel angular.
web api controllers :: whom the view calls.
Services layer :: that is being called from Web api. Any business logic goes here.
Unit of Work :: And (if) business logic requires to talk to data base for CRUDs it calls UOW.
Repository Pattern :: UOW is actually wrapping repositories. and repositores in turn talking to DbContexts.
Uptill now i was able to conver normal repositories implementation into the one using
public EFContextProvider<MyContext> DbContext { get; set; }
instead of just DbContext and i am also exposing MetaData using a string property with in UOW and IQueryables are returned using DbContext.Context.SomeEntity
Question 1 : Am i on right track ??
Question 2 : Most of the breeze examples are suggesting one SaveChanges method that give you all the entities that were changed and it will persist it at once. What if i want to trigger some business logic before Add,Update and Delete. i want to call me AddSomething service method and want to have a particular type of entity being sent to AddSomething and run some business logic before persistence. How can i put it together.
my code looksl ike
[BreezeController]//This is the controller
public class BreezeController : ApiController
{
private readonly ISomeService someService;
public BreezeController(ISomeService someService)
{
this.someService = someService;
}
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata()
{
return someService.MetaData();
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<Node> Nodes()
{
return nodesService.GetAllNodes().AsQueryable();
}
// ~/breeze/todos/SaveChanges
//[HttpPost]
//public SaveResult SaveChanges(JObject saveBundle)
//{
// return _contextProvider.SaveChanges(saveBundle);
//}
Below is the service
public class SomeService : BaseService, ISomeService
{
private readonly IUow Uow;
public SomeService(IUow Uow)
: base(Uow)
{
this.Uow = Uow;
}
public IEnumerable<Something> GetAllNodes()
{
return Uow.Somethings.GetAll();
}
}
every service can expose one property through base. that is actually the meta data
public class BaseService : IBaseService
{
private readonly IUow Uow;
public BaseService(IUow Uow)
{
this.Uow = Uow;
}
public string MetaData()
{
return Uow.MetaData;
}
}
and the my UOW looks like
public class VNUow : IUow, IDisposable
{
public VNUow(IRepositoryProvider repositoryProvider)
{
CreateDbContext();
repositoryProvider.DbContext = DbContext;
RepositoryProvider = repositoryProvider;
}
// Code Camper repositories
public IRepository<Something> NodeGroup { get { return GetStandardRepo<Something>(); } }
} }
public IRepository<Node> Nodes { get { return GetStandardRepo<Node>(); } }
/// <summary>
/// Save pending changes to the database
/// </summary>
public void Commit()
{
//System.Diagnostics.Debug.WriteLine("Committed");
DbContext.Context.SaveChanges();
}
public string MetaData // the Name property
{
get
{
return DbContext.Metadata();
}
}
protected void CreateDbContext()
{
// DbContext = new VNContext();
DbContext = new EFContextProvider<VNContext>();
// Load navigation properties always if it is true
DbContext.Context.Configuration.LazyLoadingEnabled = false;
// Do NOT enable proxied entities, else serialization fails
DbContext.Context.Configuration.ProxyCreationEnabled = true;
// Because Web API will perform validation, we don't need/want EF to do so
DbContext.Context.Configuration.ValidateOnSaveEnabled = false;
//DbContext.Configuration.AutoDetectChangesEnabled = false;
// We won't use this performance tweak because we don't need
// the extra performance and, when autodetect is false,
// we'd have to be careful. We're not being that careful.
}
protected IRepositoryProvider RepositoryProvider { get; set; }
private IRepository<T> GetStandardRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
private T GetRepo<T>() where T : class
{
return RepositoryProvider.GetRepository<T>();
}
private EFContextProvider<VNContext> DbContext { get; set; }
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (DbContext != null)
{
DbContext.Context.Dispose();
}
}
}
#endregion
}
in the end Repository Implementaion looks like
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(EFContextProvider<VNContext> dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Context.Set<T>();
}
protected EFContextProvider<VNContext> DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual IQueryable<T> GetAllEagerLoad(params Expression<Func<T, object>>[] children)
{
children.ToList().ForEach(x => DbSet.Include(x).Load());
return DbSet;
}
public virtual IQueryable<T> GetAllEagerLoadSelective(string[] children)
{
foreach (var item in children)
{
DbSet.Include(item);
}
return DbSet;
}
public virtual IQueryable<T> GetAllLazyLoad()
{
return DbSet;
}
public virtual T GetById(int id)
{
//return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
return DbSet.Find(id);
}
public virtual T GetByIdLazyLoad(int id, params Expression<Func<T, object>>[] children)
{
children.ToList().ForEach(x => DbSet.Include(x).Load());
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return; // not found; assume already deleted.
Delete(entity);
}
}
Much of this question is broad question and answers will be primarily opinion based... that said, here's my two cents: keep it simple. Carefully consider whether you truly need 3, 4 and 5, especially whether you need to implement UoW or the Repository Pattern yourself. The EF DbContext implements both, you could use it in your controllers directly if you wanted.
If you have custom logic that needs to execute prior to savechanges utilize one of the interceptor methods: BeforeSaveEntity or BeforeSaveEntites. Here's the documentation for those methods:
http://www.getbreezenow.com/documentation/contextprovider#BeforeSaveEntity
Breeze supports "Named saves" where you specify the name of the specific server endpoint ( i.e. your service method) on a per save basis. See:
http://www.getbreezenow.com/documentation/saving-changes
This would look something like this on your client.
var saveOptions = new SaveOptions({ resourceName: "CustomSave1" });
em.saveChanges(entitiesToSave, saveOptions).then(function (saveResult) {
// .. do something interesting.
}
and on your server
[HttpPost]
public SaveResult CustomSave1(JObject saveBundle) {
ContextProvider.BeforeSaveEntityDelegate = CustomSave1Interceptor;
return ContextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> CustomSave1Interceptor(Dictionary<Type, List<EntityInfo>> saveMap) {
// In this method you can
// 1) validate entities in the saveMap and optionally throw an exception
// 2) update any of the entities in the saveMap
// 3) add new entities to the saveMap
// 4) delete entities from the save map.
// For example
List<EntityInfo> fooInfos;
if (!saveMap.TryGetValue(typeof(Foo), out fooEntities)) {
// modify or delete any of the fooEntites
// or add new entityInfo instances to the fooEntities list.
}
}
So that I can store the user's screen preferences, I have ScreenSettings entity that I want to retrieve when the program starts and save when the program ends.
For this reason I don't want to keep the context open.
I am wondering about the best way to do this.
I have tried the following
however I am not comfortable with the SaveSettings function because it deletes and re-adds the object.
How do I save changes to the object without actually replacing it?
namespace ClassLibrary1
{
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
//Domain Class
public class ScreenSetting
{
#region Properties
public int Id { get; set; }
[Required]
public int WindowLeft { get; set; }
[Required]
public int WindowTop { get; set; }
#endregion
}
// Context
public class Context : DbContext
{
#region Properties
public DbSet<ScreenSetting> ScreenSettings { get; set; }
#endregion
}
// UI
public class UI
{
#region Public Methods
// Get the settings object
public ScreenSetting GetSettings(int SettingsId)
{
var Db = new Context();
ScreenSetting settings = Db.ScreenSettings.Find(SettingsId);
if (settings == null)
{
settings = new ScreenSetting { Id = SettingsId, WindowTop = 100, WindowLeft = 100 };
Db.ScreenSettings.Add(settings);
}
Db.Dispose();
return settings;
}
// Save the settings object
public void SaveSettings(ScreenSetting settings)
{
var Db = new Context();
ScreenSetting oldSettings = Db.ScreenSettings.Find(settings.Id);
if (oldSettings == null)
{
Db.ScreenSettings.Add(settings);
}
else
{
Db.ScreenSettings.Remove(oldSettings);
Db.ScreenSettings.Add(settings);
}
Db.Dispose();
}
public void test()
{
ScreenSetting setting = this.GetSettings(1);
setting.WindowLeft = 500;
setting.WindowTop = 500;
this.SaveSettings(setting);
}
#endregion
#region Methods
private static void Main()
{
var o = new UI();
o.test();
}
#endregion
}
}
You ran into a common pattern, update or insert, which is so common that it's got a name: upsert. When a pattern is common, usually there also is a common solution.
In System.Data.Entity.Migrations there is an extension method AddOrUpdate that does exactly what you want:
public void SaveSettings(ScreenSetting settings)
{
using (var db = new Context())
{
db.ScreenSettings.AddOrUpdate(settings);
db.SaveChanges();
}
}
I'm working on adding a Windsor IoC container to an existing WinForms application that uses an MVP UI design pattern. I'm trying to determine a good approach to resgistering a datacontext that depends on a connection string supplied at runtime. The problem is that I cannot create a datacontext until the user selects a database, i.e. a 'connection string' after the application has loaded. Granted only one datacontext is generally used, but sometimes a user need to switch to a different database, i.e. creating a differnet datacontext. This leads to additional runtime dependencies as well.
public interface IProductsView
{
event EventHandler<ProductSelectedEventArgs> ProductSelectedEvent;
event EventHandler<StringEventArgs> ProductStatusEvent;
void ClearProductList();
void DisplayProductList(IList<Product> products);
Control Control { get; }
IProductsPresenter Presenter { get; set; }
}
public class ProductsPresenter : IProductsPresenter
{
public IProductsView View { get; set; }
private IProductRepository Repository { get; set; }
public ProductsPresenter(IProductsView view, IProductRepository repository)
{
View = view;
View.Presenter = this;
Repository = repository;
}
public void ProductSelected(IList<Product> products)
{
throw new NotImplementedException();
}
public void ShowProductList(string name)
{
IList<Product> productList;
if (string.IsNullOrEmpty(name))
productList = Repository.GetProducts();
else
productList = Repository.GetProductsByName(name);
View.DisplayProductList(productList);
}
}
public class ProductDao : IDisposable, IProductRepository
{
private MeasurementDataContext dataContext;
public ProductDao(MeasurementDataContext context)
{
dataContext = context;
}
public List<Product> GetProducts()
{
return dataContext.Products.Select(p => Mapper.Map(p)).ToList().OrderBy(x => x.Name).ToList();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
if (dataContext != null)
{
dataContext.Dispose();
dataContext = null;
}
}
~ProductDao()
{
this.Dispose(false);
}
}
So this means that the Presenter in my View is null until the IProductRepository is created, which in turn depends on creating a MeasurementDataContext. I have these component regisitered in a IWindsorInstaller like so:
container.Register(Component.For<IProductsView>()
.ImplementedBy<ViewProductsControl>());
container.Register(Component.For<IProductsPresenter>()
.ImplementedBy<ProductsPresenter>());
Do I need to use Named and DependsOn which supply a unique name and connectionString argument for each datacontext?
What I currently do to register the data context at runtime after the user has selected a database
kernel.Register(Component.For<MeasurementDataContext>()
.UsingFactoryMethod(() => new MeasurementDataContext(conn)));
and then `Resolve' my Views and set their Presenters. I know this is not good design, but it's a brute force way of resolving my dependcies.
Thanks
UPDATE:
I changed the way I registered my datacontext's in the installer to the following:
container.Register(Component.For<DataContext>().ImplementedBy<MeasurementDataContext>().Named("Localhost").DependsOn(new { connectionString = conn }));
and then modified my model's constructor to:
public ProductDao(DataContext context)
{
dataContext = context as MeasurementDataContext;
}
All components will resolve with the right key:
kernel.Resolve<DataContext>(cbo.SelectedItem.ToString());
What about injecting a wrapper class to hold the connection string and have the datacontext objects use that? Something along these lines:
public class ConnectionStringProvider : IConnectionStringProvider
{
private string _value;
public event EventHandler ConnectionStringChanged;
public string ConnectionString
{
get { return _value; }
set
{
_value = value;
var del = ValueChanged;
if (del != null)
del(this, EventArgs.Empty);
}
}
}
Register this with and singleton lifestyle. This way your application can set or update the connection string on a single object and everyone who depends on it will be notified of the change.