Viewmodel not updating model after add or insert to list - mvvm

I'm fairly new to MVVM and Entity framework and I've hit a problem trying to add new records to my SQL database through MVVM. Below is the first and last part of my viewmodel which loads from my entity framework and this is working fine.
internal class MainWindowViewModel : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
private TEAMSEntities ctx = new TEAMSEntities();
public MainWindowViewModel()
{
FillSales();
}
public void AddASale()
{
Tbl_Sales newsale = new Tbl_Sales();
newsale.SALEID = "2018...";
newsale.SALE = "....";
newsale.SALEDESC = "....";
newsale.START = Convert.ToDateTime("12/04/2018");
newsale.SESSIONS = 2;
newsale.DAYS = 2;
newsale.LOTS = 100;
newsale.FIRSTLOT = 1;
newsale.LASTLOT = 100;
Sales.Add(newsale);
SaveChanges();
}
#region Sale
private void FillSales()
{
var q = (from a in ctx.Tbl_Sales
select a).ToList();
this.Sales = q;
}
private List<Tbl_Sales> _sales;
public List<Tbl_Sales> Sales
{
get
{
return _sales;
}
set
{
_sales = value;
NotifyPropertyChanged();
}
}
private Tbl_Sales _selectedSale;
public Tbl_Sales SelectedSale
{
get
{
return _selectedSale;
}
set
{
_selectedSale = value;
NotifyPropertyChanged();
}
}
#endregion Sale
.
.
.
.
public void SaveChanges()
{
ctx.SaveChanges();
}
private void NotifyPropertyChanged([CallerMemberName] String
propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void Dispose()
{
((IDisposable)ctx).Dispose();
}
}
When I make changes to existing data in the view bound to the viewmodel and call the SaveChanges method in the viewmodel it saves the change back to the SQL database every time. If I call the AddASale method it adds that sale to the list but doesn't refresh the UI control bound to Sales and doesn't pass the newly created sale back to the SQL DB. Through debugging I can see the set being called in the Sales property when the LINQ code runs but it doesn't fire when I add a new sale through the AddASale code which is probably why the UI isn't updating...?
Can anyone offer any guidance as to what I'm doing wrong?
Thanks in advance.
Alex

First, add your new entity in your context and save it like this :
Tbl_Sales newsale = new Tbl_Sales
{
SALEID = "2018...",
SALE = "....",
SALEDESC = "....",
START = Convert.ToDateTime("12/04/2018"),
SESSIONS = 2,
DAYS = 2,
LOTS = 100,
FIRSTLOT = 1,
LASTLOT = 100
};
ctx.Tbl_Sales.Add(newsale);
SaveChanges();
after that, I think you have to refresh your list manually :
FillSales();
Here, a link to MSDN
Hope I helped you

Related

How to write a generic WebAPI Put method against Entity Framework that works with child lists?

I am tinkering with WebAPI to create a generic implementation for entity framework. I am able to implement most of the methods just fine, but am finding PUT to be tricky in non-trivial cases. The implementation most commonly found online works for simple entities:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var entry = db.Entry(entity);
entry.State = EntityState.Modified;
var set = db.Set<T>();
set.Attach(entity);
db.SaveChanges();
return entity;
}
...but does not delete or update child lists:
public class Invoice
{
...
public virtual InvoiceLineItem {get; set;} //Attach method doesn't address these
}
In an MVC Controller, you could simply use "UpdateModel" and it would add/update/delete children as needed, however that method is not available on ApiController. I understand that some code would be necessary to get the original item from the database, and that it would need to use Include to get the child lists, but can't quite figure out the best way to replicate UpdateModel's functionality:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var original = GetOriginalFor(entity);
//TODO: Something similar to UpdateModel(original), such as UpdateModel(original, entity);
db.SaveChanges();
return original;
}
How can I implement UpdateModel OR somehow implement Put in such a way that it will handle child lists?
The routine dont validate entity, but fill the pre-existent entity.
protected virtual void UpdateModel<T>(T original, bool overrideForEmptyList = true)
{
var json = ControllerContext.Request.Content.ReadAsStringAsync().Result;
UpdateModel<T>(json, original, overrideForEmptyList);
}
private void UpdateModel<T>(string json, T original, bool overrideForEmptyList = true)
{
var newValues = JsonConvert.DeserializeObject<Pessoa>(json);
foreach (var property in original.GetType().GetProperties())
{
var isEnumerable = property.PropertyType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (isEnumerable && property.PropertyType != typeof(string))
{
var propertyOriginalValue = property.GetValue(original, null);
if (propertyOriginalValue != null)
{
var propertyNewValue = property.GetValue(newValues, null);
if (propertyNewValue != null && (overrideForEmptyList || ((IEnumerable<object>)propertyNewValue).Any()))
{
property.SetValue(original, null);
}
}
}
}
JsonConvert.PopulateObject(json, original);
}
public void Post()
{
var sample = Pessoa.FindById(12);
UpdateModel(sample);
}

Nullpointer exception in CompositePlanningValueRangeDescriptor.extractValues

I'm facing a NPE when trying to solve my solution:
Exception in thread "main" java.lang.NullPointerException
at java.util.ArrayList.addAll(ArrayList.java:472)
at org.drools.planner.core.domain.variable.CompositePlanningValueRangeDescriptor.extractValues(CompositePlanningValueRangeDescriptor.java:46)
at org.drools.planner.core.domain.variable.PlanningVariableDescriptor.extractPlanningValues(PlanningVariableDescriptor.java:259)
at org.drools.planner.core.heuristic.selector.variable.PlanningValueSelector.initSelectedPlanningValueList(PlanningValueSelector.java:91)
at org.drools.planner.core.heuristic.selector.variable.PlanningValueSelector.phaseStarted(PlanningValueSelector.java:73)
at org.drools.planner.core.heuristic.selector.variable.PlanningValueWalker.phaseStarted(PlanningValueWalker.java:64)
at org.drools.planner.core.heuristic.selector.variable.PlanningVariableWalker.phaseStarted(PlanningVariableWalker.java:62)
at org.drools.planner.core.constructionheuristic.greedyFit.decider.DefaultGreedyDecider.phaseStarted(DefaultGreedyDecider.java:62)
at org.drools.planner.core.constructionheuristic.greedyFit.DefaultGreedyFitSolverPhase.phaseStarted(DefaultGreedyFitSolverPhase.java:112)
at org.drools.planner.core.constructionheuristic.greedyFit.DefaultGreedyFitSolverPhase.solve(DefaultGreedyFitSolverPhase.java:57)
at org.drools.planner.core.solver.DefaultSolver.runSolverPhases(DefaultSolver.java:190)
at org.drools.planner.core.solver.DefaultSolver.solve(DefaultSolver.java:155)
at de.haw.dsms.applicationcore.planning.BalancingApp.main(BalancingApp.java:47)
I have annotated my planning entity with the following annotations to collect the value range from two lists in the solution:
#PlanningEntity
public class ScheduleItem implements Cloneable{
private ChangeOfferEvent item;
#PlanningVariable()
#ValueRanges({
#ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "offers"),
#ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "dummies")
})
public ChangeOfferEvent getItem() {
return item;
}
public void setItem(ChangeOfferEvent item) {
this.item = item;
}
public ScheduleItem() {
this.item = null;
}
...
This is the solution:
public class ProductionConsumptionBalancing implements Solution<HardAndSoftLongScore> {
/*
* Problem facts
*/
// The grid entity offers
private List<ChangeOfferEvent> offers;
// Placeholder events to represent "not used schedule items"
private List<PlaceholderOfferEvent> dummies;
// The total energy consumption in the grid
// [Watt]
private TotalEnergyConsumption totalElectricityConsumption;
// The total energy production in the grid
// [Watt]
private TotalEnergyProduction totalElectricityProduction;
public List<ChangeOfferEvent> getOffers() {
return offers;
}
public void setOffers(List<ChangeOfferEvent> offers) {
this.offers = offers;
}
public List<PlaceholderOfferEvent> getDummies() {
return dummies;
}
public void setDummies(List<PlaceholderOfferEvent> dummies) {
this.dummies = dummies;
}
public TotalEnergyConsumption getTotalElectricityConsumption() {
return totalElectricityConsumption;
}
public void setTotalElectricityConsumption(
TotalEnergyConsumption totalElectricityConsumption) {
this.totalElectricityConsumption = totalElectricityConsumption;
}
public TotalEnergyProduction getTotalElectricityProduction() {
return totalElectricityProduction;
}
public void setTotalElectricityProduction(
TotalEnergyProduction totalElectricityProduction) {
this.totalElectricityProduction = totalElectricityProduction;
}
/*
* Problem entities
*/
private List<ScheduleItem> schedule;
#PlanningEntityCollectionProperty
public List<ScheduleItem> getSchedule() {
return schedule;
}
public void setSchedule(List<ScheduleItem> schedule) {
this.schedule = schedule;
}
...
The strange thing about this is, that during debugging I discoverd that it is the parameter "planningEntity" which is null and not the values in the solution.
Does anybody encounter the same issue or does know how to solve this?
Thanks and best regards!
PS:
It seems like this is coming from the method initSelectedPlanningValueList:
private void initSelectedPlanningValueList(AbstractSolverPhaseScope phaseScope) {
90 if (planningVariableDescriptor.isPlanningValuesCacheable()) {
91 Collection<?> planningValues = planningVariableDescriptor.extractPlanningValues(
92 phaseScope.getWorkingSolution(), null);
93 cachedPlanningValues = applySelectionOrder(planningValues);
94 } else {
95 cachedPlanningValues = null;
96 }
97 }
PSPS:
Problem solved.
The issue appeared because I forgot to link the clone's dummies-attribute to the original dummies list. So the dummies list in the cloned solution was null.
#Override
public Solution<HardAndSoftLongScore> cloneSolution() {
ProductionConsumptionBalancing clone = new ProductionConsumptionBalancing();
// Transfer consumption and production values
clone.totalElectricityConsumption = this.totalElectricityConsumption;
clone.totalElectricityProduction = this.totalElectricityProduction;
// Shallow copy offer lists (shouldn't change)
clone.offers = this.offers;
// Shallow copy of dummy list
clone.dummies = this.dummies;
// Deep copy schedule
...
Starting from 6.0.0.Beta1, OptaPlanner (= Drools Planner) supports automatic cloning out-of-the-box. So you don't need to implement the cloneSolution() method no more, because planner figures it out automatically. Because you don't need to implement the method no more, you can't implement it incorrectly.
Note that you can still implement a custom clone method if you really want too.

EF: How to enclose context object in a using statement?

Let's say I have the following classes Customer.cs, a context OfficeContext.cs, and a repository OfficeRepository.cs. Knowing that the context use a connection object, so it's advised to enclose it in a using statement:
public List<Customer> GetAllCustomersWithOrders()
{
using(var oContext = new OfficeContext())
{
//Code here....
}
}
My question is what if I want to re-use some of the code already in the repository? For instance, what if I want to display all the customers that ordered products but didn't receive them yet, do I need to duplicate the code?
public List<Customer> GetCustomersNotReceiveProducts()
{
using(var oContext = new OfficeContext())
{
//Re-use GetAllCustomersWithOrders() here???...
}
}
But as you can see, each time access a method, I also open instantiate a new context object. Is there any way to deal with that?
What I do is have my repositories implement IDisposable.
Then have two constructors (one default) that instaniates a new context that holds it as a class level variable. And another constructor that takes a context and uses that internally.
The on the dispose of the class the context is disposed (if the current repository instatiated it).
This removes the context out of the method level and moves it to the class level. My functions keep everything in IQueryable so one function can call another function and perform additional refinements before the database it hit.
Exmaple:
public class MemberRepository : IDisposable
{
OfficeContext db;
bool isExternalDb = false;
public MemberRepository()
{
db = new OfficeContext();
isExternalDb = false;
}
public MemberRepository(OfficeContext db)
{
this.db = db;
isExternalDb = true;
}
public IQueryable<Member> GetAllMembers()
{
var members= db.Members
return members;
}
public IQueryable<Member> GetActiveMembers()
{
var members = GetAllMembers();
var activeMembers = members.Where(m => m.isActive == true);
return activeMembers;
}
public void Dispose()
{
if (isExternalDb == false)
{
db.Dispose();
}
}
}
Then where I use the repository, I do a using at that level:
using(var memberRepository = new MemberRepository())
{
var members = memberRepository.GetActiveMembers();
}

How to implement something like a wizard screen?

I want to place a "Next" button which , when clicked , will display another group of components ; and I want also to place a "Previous" button which , when clicked , then display the previous group of components. How to achieve that ?
I recently implemented forms for data entry. Typically i have a wizard class that holds all the forms in the wizard, so i can easily navigate back and forth between them. And when i call a new form, i pass along the object of the wizard.
Below is my wizard, with implementation omitted.
public final class ReportWizard {
public static ReportWizard instance = null;
Form parent = null;
Form titleForm = null;
Form budgetForm = null;
Form iconForm = null;
final Report reports[] = new Report[20];
public ReportWizard(Form parent) {
this.parent = parent;
this.instance = this;
}
void getTitle() {
AddReportForm reportForm = new AddReportForm(parent, this);
reportForm.showReportForm();
titleForm = reportForm;
ImageListPicker getIcon = new ImageListPicker(titleForm, reports, this);
iconForm = getIcon.imageListForm;
}
void getIcon() {
iconForm.show();
}
public void cancelWizard() {
titleForm = null;
iconForm = null;
budgetForm = null;
instance = null;
parent.show();
parent = null;
System.gc();
}
}

Entity Framework + ODATA: side-stepping the pagination

The project I'm working on has the Entity Framework on top of an OData layer. The Odata layer has it's server side pagination turned to a value of 75. My reading on the subject leads me to believe that this pagination value is used across the board, rather than a per table basis. The table that I'm currently looking to extract all the data from is, of course, more than 75 rows. Using the entity framework, my code is simply thus:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors;
}
where db is the entity context. This is returning the first 75 records. I read something where I could append a parameter inlinecount set to allpages giving me the following code:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors.AddQueryOption("inlinecount","allpages");
}
However, this too returns 75 rows!
Can anyone shed light on how to truly get all the records regardless of the OData server-side pagination stuff?
important: I cannot remove the pagination or turn it off! It's extremely valuable in other scenarios where performance is a concern.
Update:
Through some more searching I've found an MSDN that describes how to do this task.
I'd love to be able to turn it into a full Generic method but, this was as close as I could get to a generic without using reflection:
public IQueryable<T> TakeAll<T>(QueryOperationResponse<T> qor)
{
var collection = new List<T>();
DataServiceQueryContinuation<T> next = null;
QueryOperationResponse<T> response = qor;
do
{
if (next != null)
{
response = db.Execute<T>(next) as QueryOperationResponse<T>;
}
foreach (var elem in response)
{
collection.Add(elem);
}
} while ((next = response.GetContinuation()) != null);
return collection.AsQueryable();
}
calling it like:
public IQueryable<ProductColor> GetProductColors()
{
QueryOperationResponse<ProductColor> response = db.ProductColors.Execute() as QueryOperationResponse<ProductColor>;
var productColors = this.TakeAll<ProductColor>(response);
return productColors.AsQueryable();
}
If unable turn off paging you'll receive 75 row by call, always. You can get all rows in following ways:
Add another IQueryable<ProductColor> AllProductColors and modify
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.SetEntitySetPageSize("ProductColors", 75); - Note only paged queries are present
config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
You should call ProductColors as many as needed, for example
var cat = new NetflixCatalog(new Uri("http://odata.netflix.com/v1/Catalog/"));
var x = from t in cat.Titles
where t.ReleaseYear == 2009
select t;
var response = (QueryOperationResponse<Title>)((DataServiceQuery<Title>)x).Execute();
while (true)
{
foreach (Title title in response)
{
Console.WriteLine(title.Name);
}
var continuation = response.GetContinuation();
if (continuation == null)
{
break;
}
response = cat.Execute(continuation);
}
I use Rx with following code
public sealed class DataSequence<TEntry> : IObservable<TEntry>
{
private readonly DataServiceContext context;
private readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly IQueryable<TEntry> query;
public DataSequence(IQueryable<TEntry> query, DataServiceContext context)
{
this.query = query;
this.context = context;
}
public IDisposable Subscribe(IObserver<TEntry> observer)
{
QueryOperationResponse<TEntry> response;
try
{
response = (QueryOperationResponse<TEntry>)((DataServiceQuery<TEntry>)query).Execute();
if (response == null)
{
return Disposable.Empty;
}
}
catch (Exception ex)
{
logger.Error(ex);
return Disposable.Empty;
}
var initialState = new State
{
CanContinue = true,
Response = response
};
IObservable<TEntry> sequence = Observable.Generate(
initialState,
state => state.CanContinue,
MoveToNextState,
GetCurrentValue,
Scheduler.ThreadPool).Merge();
return new CompositeDisposable(initialState, sequence.Subscribe(observer));
}
private static IObservable<TEntry> GetCurrentValue(State state)
{
if (state.Response == null)
{
return Observable.Empty<TEntry>();
}
return state.Response.ToObservable();
}
private State MoveToNextState(State state)
{
DataServiceQueryContinuation<TEntry> continuation = state.Response.GetContinuation();
if (continuation == null)
{
state.CanContinue = false;
return state;
}
QueryOperationResponse<TEntry> response;
try
{
response = context.Execute(continuation);
}
catch (Exception)
{
state.CanContinue = false;
return state;
}
state.Response = response;
return state;
}
private sealed class State : IDisposable
{
public bool CanContinue { get; set; }
public QueryOperationResponse<TEntry> Response { get; set; }
public void Dispose()
{
CanContinue = false;
}
}
}
so for get any data thru OData, create a sequence and Rx does the rest
var sequence = new DataSequence<Product>(context.Products, context);
sequence.OnErrorResumeNext(Observable.Empty<Product>())
.ObserveOnDispatcher().SubscribeOn(Scheduler.NewThread).Subscribe(AddProduct, logger.Error);
The page size is set by the service author and can be set per entity set (but a service may choose to apply the same page size to all entity sets). There's no way to avoid it from the client (which is by design since it's a security feature).
The inlinecount option asks the server to include the total count of the results (just the number), it doesn't disable the paging.
From the client the only way to read all the data is to issue the request which will return the first page and it may contain a next link which you request to read the next page and so on until the last response doesn't have the next link.
If you're using the WCF Data Services client library it has support for continuations (the next link) and a simple sample can be found in this blog post (for example): http://blogs.msdn.com/b/phaniraj/archive/2010/04/25/server-driven-paging-with-wcf-data-services.aspx