I created a WPF MVVM project in Devexpress with the Scaffolding wizard and everything created works fine, I even modified the grids so they call the SaveCommand on RowUpdated.
Now I'm trying to insert new registers programmatically and my strategy was to get and instance of the CollectionViewModel from my model and use its Save method sending an object from the same model as a parameter.
I was reading this guide but still couldn't find what i'm doing wrong.
This is the code
Transaction transaction = new Transaction();
transaction.IdClient = 1;
TransactionCollectionViewModel tcvm = TransactionCollectionViewModel.Create(UnitOfWorkSource.GetUnitOfWorkFactory());
tcvm.Save(transaction);
and it gives me this error on the variable tcvm when calling the Save function
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=DevExpress.Mvvm.v16.1.DataModel
StackTrace:
at DevExpress.Mvvm.DataModel.RepositoryExtensions.<>c__DisplayClass1_0`3.<GetProjectionPrimaryKey>b__0(TEntity x)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.GetProjectionValue[TEntity,TProjection,TEntityResult,TProjectionResult](TProjection value, Func`2 entityFunc, Func`2 projectionFunc)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.GetProjectionPrimaryKey[TEntity,TProjection,TPrimaryKey](IRepository`2 repository, TProjection projectionEntity)
at DevExpress.Mvvm.DataModel.RepositoryExtensions.FindExistingOrAddNewEntity[TEntity,TProjection,TPrimaryKey](IRepository`2 repository, TProjection projectionEntity, Action`2 applyProjectionPropertiesToEntity)
at DevExpress.Mvvm.ViewModel.CollectionViewModelBase`4.Save(TProjection projectionEntity)
at Template.ViewModels.ClientCollectionViewModel..ctor(IUnitOfWorkFactory`1 unitOfWorkFactory) in C:\Users\edwin\Source\Workspaces\INVASST\Template\Template\ViewModels\Client\ClientCollectionViewModel.cs:line 37
at ClientCollectionViewModel_97cd3897_6fb7_469b_9928_d61260161e61..ctor(IUnitOfWorkFactory`1 unitOfWorkFactory)
InnerException:
I'm using Devexpress 16.1.5 and the project is using the HybridApp template
... my strategy was to get an instance of the CollectionViewModel from my model and use its Save method sending an object from the same model as a parameter.
I'm afraid this strategy is wrong. You should use the New() command provided by the corresponding CollectionViewModel (bound to the Grid).
The real usage depends on your specific needs. In a simple case, you can just add a button above the grid and then bind the New command to this button.
Related DX support thread : Batch Insert/ Edit with Master/Detail
Related
I'm currently developing an app in WPF using the MVVM pattern ( without framework ). I use VS2019,
Each view is an UserControl
The app is connected to a local database MySQLLite.
When I start my program, I have an user connection. When the user connection is successful, it loads my object "Engine" in my global class "BaseViewModel" ( inherit all ViewModel ).
In WinForm when I create a new Form(View), I just transfer my Engine class in parameters and I save the Engine locally in my Form ( not null ) and then I have access to my value in Engine like User.
What is the best way to do it in WPF using MVVM?
I try to transfer the Engine to my ViewModel when I create but it always overwrites it later with null. Because it opens the view without parameter later and calls my constructor without parameter.
You can create Data Access Level class to load your engine from DB. For example, it will be named EngineDataAccess and it will have GetEngine() method. Next in your EngineViewModel you can implement something like this:
private readonly Engine engine;
public string EngineName
{
get {return engine.Name; }
set {engine.Name = value; OnPropertyChanged("EngineName");}
}
public EngineViewModel(EngineDataAccess engineDataAccess)
{
engine = engineDataAccess.GetEngine();
}
In your code:
EngineDataAccess engineDataAccess = new EngineDataAccess();
EngineViewModel engineViewModel = new EngineViewModel(engineDataAccess);
form.DataContext = engineViewModel;
What is not very good in this way and how it can be done better:
1) getting engine from db on creating view model process => it will be better to do it on Load view model (MVVM load data during or after ViewModel construction?)
2) using EngineDataAccess class instead of interface
3) binding datacontext in the codebehind => better to use IoC
Also, I would recommend you to use some MVVM framework like Galasoft.MVVM. It does MVVM using simpler.
I am trying to create a process that will run daily that will import records from another database as Inventory Items. In order to do this, I need to create an extension of the InventoryItemMaint graph (to give me my custom action), as well as an extension of the InventoryItem DAC (to give me a custom field). I have tried to follow the guidelines laid out specifically in the T-300 manual to do this.
Here is the code for my InventoryItemMaint extension:
namespace PX.Objects.IN
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
var invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
foreach (STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>
.Select(this.Base, todaysDate))
{
InventoryItem currentInventoryItem = invItemMaintExtInstance.Base.Item.Current;
PXCache inventoryItemCache = invItemMaintExtInstance.Base.Item.Cache;
InventoryItemExt inventoryItemExtension = inventoryItemCache.GetExtension<InventoryItemExt>(currentInventoryItem);
inventoryItemCache.Clear();
currentInventoryItem.InventoryCD = currentOnixItem.ISBN13;
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
//Assigning to the custom DAC Extension
inventoryItemExtension.UsrFromOnixFile = currentOnixItem.FromFile;
inventoryItemCache.Update(currentInventoryItem);
Base.Actions.PressSave();
}
}
}
}
I am currently getting an error that reads:
Error: Another process has updated the 'InventoryItem' record. Your
changes will be lost.
And here is the error trace text:
9/20/2018 3:26:05 PM Error: Error: Another process has added the
'InventoryItem' record. Your changes will be lost.
at PX.Data.PXCache1.PersistInserted(Object row) at
PX.Data.PXCache1.Persist(PXDBOperation operation) at
PX.Data.PXGraph.Persist(Type cacheType, PXDBOperation operation)
at PX.Data.PXGraph.Persist() at
PX.Objects.IN.InventoryItemMaint.Persist() at
PX.Data.PXSave1.d__2.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXActionCollection.PressSave(PXAction caller) at
PX.Objects.IN.InventoryItemMaint_Extension.dailyOnixImport() at
PX.Data.PXAction1.<>c__DisplayClass3_0.<.ctor>b__0(PXAdapter adapter)
at PX.Data.PXAction1.a(PXAdapter A_0) at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction`1.d__31.MoveNext() at
PX.Web.UI.PXBaseDataSource.tryExecutePendingCommand(String viewName,
String[] sortcolumns, Boolean[] descendings, Object[] searches,
Object[] parameters, PXFilterRow[] filters, DataSourceSelectArguments
arguments, Boolean& closeWindowRequired, Int32& adapterStartRow,
Int32& adapterTotalRows) at
PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName,
DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)
I have done a lot of searching around StackOverflow and other places, but haven't found any answers that seem to address my issue exactly. Tweaks I've made have resulted in other errors like variations on what I'm getting now (another process added vs another process updated) and MoveNext errors.
If anyone is able to help me out, I would be very appreciative.
guys,
I faced the same exception in the EmployeeMaint when we tried to update one of the complex fields in the DAC.
baseAction() throws this exception even though no code runs before saving.
I followed your suggestions #Hugues Beauséjour and found out that reason may be:
Means, the cache is dirty.
So, to resolve my issue I just needed to clear the cache for the object that throws the exception.
IN my case the exception was:
Update employee class error - Another process has added the 'VendorPaymentMethodDetail' record. Your changes will be lost
So I needed to clear the cache for VendorPaymentMethodDetail:
Base.Caches<VendorPaymentMethodDetail>().Clear();
Be careful to clear the cache in the case you need to read from the cache during your code after you clear the cache. In that case, you need to copy objects from the cache before cleaning and used this copy afterwards.
Hope it will be helpful to someone.
There seem to be a logical flaw in the code. You are updating the same current object in a loop. This serve no purpose as it will always overwrite with the last item returned by the loop. Invoking the Save action in a loop can also lead to errors if you're not careful.
As I mentioned in the comment, clearing the cache seems wrong. You want to keep the current data there. When you call clear you're invalidating the main document of the graph, that will lead to errors.
Changing fields closely tied to the key like InventoryCD can lead to the document being cleared and invalidated. If you have to modify key fields considering inserting new record instead of updating the current one.
There are other changes I would recommend.
Code:
// Consider replacing the default namespace to avoid conflicts
namespace MyNamespace
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
// '(CommitChanges = true)' is not necessary
[PXButton]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
InventoryItemMaint_Extension invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
// You need to rethink that 'foreach' logic
STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>.Select(Base, todaysDate);
// You can access Base directly, no need to fetch it from the extension
InventoryItem currentInventoryItem = Base.Item.Current;
// Consider using more null check
if (currentOnixItem != null && currentInventoryItem != null)
{
// Consider using similar names for similar variables
InventoryItemExt currentInventoryItemExt = currentInventoryItem.GetExtension<InventoryItemExt>();
// Avoid setting key related fields like InventoryCD when updating
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
currentInventoryItemExt.UsrFromOnixFile = currentOnixItem.FromFile;
// You fetched the item from the DataView
// you can update it in the DataView too.
Base.Item.Update(currentInventoryItem);
// Is it really needed to save here?
// This coupled with cache clearing and the loop updating
// the same record triggers the error in your question.
Base.Actions.PressSave();
}
}
}
}
I found another reason that can cause the same exception:
That may happen when you select from the same table in different caches, use one for select and another for insert. for instance:
we have a view:
public PXSelect<MPEmployeeWorkSchedule, Where<MPEmployeeWorkSchedule.employeeID, Equal<Current<MPEmployeeTermination.employeeID>>>> EmployeeWorkSchedule;
and in event we have code's segment:
we use the same DAC as in the view above and then we insert into the view:
EmployeeWorkSchedule.Cache.Insert(workSchedule);
Afterwards Persist() throws exception.
Solution was again to create another view instead of query and clear the cache:
This may be helpful, I hope.
Want to share one more way of finding the same error message. In my case the problem was in structure of database.
In order to fix the error message, I've did those steps:
dropped my table
generated sql script from existing table
deleted not needed columns
copy/pasted existing columns, with replacing names
As outcome, I've got error message to disappear.
I am working on WPF application and I am using CefSharp ChromiumWebBrowser.
I am using below code to register browser object into ChromiumBrowser-
browser.RegisterJsObject("bound1", new BoundObject());
But when I am opening multiple windows simultaneously I am getting below error:
{System.ArgumentException: Object already bound with name:bound1
Parameter name: bound1
at CefSharp.Internals.JavascriptObjectRepository.Register(String name, Object value, Boolean isAsync, BindingOptions options)}
I tried below things to fix this-
browser.LegacyJavascriptBindingEnabled = true
browser.RegisterJsObject("bound1", new BoundObject());
But still I am getting the same error. Any suggestion to fix this would appreciated.
In my Xamarin Studio project on Mac, I am using MvvmCross version 3.0.13 from MvvmCross-Binaries, the XS-iOS-Mac Release assemblies, and I am trying to couple my CrossUI Dialog based View with a corresponding ViewModel. Specifically, I define the Root in my dialog view like this:
var bindings = this.CreateInlineBindingTarget<ViewModel>();
Root = new RootElement("New Connection") {
new Section {
new StringElement("Test")
.Bind(bindings, element => (object)element.SelectedCommand, vm => vm.TestConnection)
},
new Section {
new StringElement ("Add")
.Bind (bindings, element => element.Visible, vm => vm.CanAddConnection)
.Bind (bindings, element => (object)element.SelectedCommand, vm => vm.AddConnection)
}
};
In the ViewModel, CanAddConnection is set to true by the TestConnection command if test is successful.
When I run this (in the iOS Simulator) and open the dialog, the Test button is displayed and the Add button is hidden (as intended). When I click the button and the test is successful, the Add button is however not displayed, but instead I get this message in the application output:
How did this happen - CurrentAttachedCell is a child of a non-UITableView
Why is my Visible binding not working?
As far as I can tell, I have not made any code customizations upstream that would lead to this failure in the code (but I might be missing something).
If I bind CanAddConnection to another element property, for example Caption, the boolean value is properly updated in the view.
I think you are probably falling foul of an ios7 change which is addressed as part of https://github.com/MvvmCross/MvvmCross/issues/467
This fix will be included in 3.0.14 (hopefully in the next week) - in the meantime, the easiest workaround is probably to patch UpdateVisibility yourself in your own build - or to implement a custom StringElement
I'm trying to save a collection where a store a diagnostic log temporarily when navigating away from my app - I've looked at other sample code and it seems pretty basic as I've done below:
Saving:
PhoneApplicationService.Current.State["DiagnosticLog"] = DiagnosticLog;
Loading:
if (PhoneApplicationService.Current.State.ContainsKey("DiagnosticLog"))
DiagnosticLog = (ObservableCollection<DiagnosticLogEntry>)
PhoneApplicationService.Current.State["DiagnosticLog"];
However I get this error:
A first chance exception of type
'System.Runtime.Serialization.InvalidDataContractException' occurred
in System.Runtime.Serialization.dll
Any suggestions please?
Usually, when this happens, it means you do not have a default public constructor on your Diagnostic class (or one of it's contained classes).