DDS Delete property in nested type error

Vote:
 

Hi!

I have a model within a model wich is working fine until I delete public property from the nested model. This generates server error when saving it to the store. 

Shouldn't the class attribute handle cases like that?

Is there a way of manually mapping a nested model like that?

[EPiServerDataStore(AutomaticallyCreateStore = true, AutomaticallyRemapStore = true)]
public class ConfigurationItemDataModel
{
    [EPiServerDataIndex] public Identity Id { get; set; }
    public ConfigDataModel BasicConfig { get; set; }
}

The nested model:

[EPiServerDataStore(AutomaticallyCreateStore = true, AutomaticallyRemapStore = true)]
public class ConfigDataModel
{
    [JsonIgnore] [EPiServerDataIndex] public Identity Id { get; set; }
    [JsonProperty ( "title" )] public string Title { get; set; }
    [JsonProperty ( "baseUrl" )] public string BaseUrl { get; set; }
    [JsonProperty ( "imageUrl" )] public string BackgroundImageUrl { get; set; } // <-- the deleted property
}

And the saving:

public ConfigurationItemViewModel SaveConfiguration ( ConfigurationItemDataModel data ) 
{
    using ( var store = DynamicDataStoreFactory.Instance.GetOrCreateStore ( typeof ( ConfigurationItemDataModel ) ) )
    {
    var existingItem = store.Items<ConfigurationItemDataModel> ()
        .FirstOrDefault ( m => m.Id.StoreId == data.Id );

    if ( existingItem != null )
    {
        data.Id = existingItem.Id;
        data.BasicConfig.Id = existingItem.BasicConfig.Id;
    }
    //Here be errors:
    store.Save ( data );

    return data;
    }
}

And it all works great, but when I delete property from the nested model(ConfigDataModel), then Episerver does not know what to do at Save() and throws this error:

Server Error in '/' Application.
Method 'Models.DataModels.ConfigDataModel.BackgroundImageUrl' not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.MissingMethodException: Method 'Models.DataModels.ConfigDataModel.BackgroundImageUrl' not found.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:


[MissingMethodException: Method 'DataModels.ConfigDataModel.BackgroundImageUrl' not found.]
   System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) +12977553
   EPiServer.Data.Dynamic.PropertyBagObjectsExtensions.ToPropertyBag(Object value, IEnumerable`1 propertyNames) +202
   EPiServer.Data.Dynamic.DynamicDataStore.InternalSave(Identity id, Object value, TypeToStoreMapper typeToStoreMapper, ProviderCallContext parentContext) +60
   EPiServer.Data.Dynamic.Providers.Internal.DbDataStoreProvider.SaveSubObject(String propertyName, Object value, ProviderCallContext context, String& storeName) +488
   EPiServer.Data.Dynamic.Providers.Internal.<>c__DisplayClass50_0.<Save>b__0() +870
   EPiServer.Data.Providers.Internal.<>c__DisplayClass32_0.<ExecuteTransaction>b__0() +10
   EPiServer.Data.Providers.Internal.<>c__DisplayClass33_0`1.<ExecuteTransaction>b__0() +55
   EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute(Func`1 method) +45
   EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.ExecuteTransaction(Func`1 action) +125
   EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.ExecuteTransaction(Action action) +90
   EPiServer.Data.Dynamic.Providers.Internal.DbDataStoreProvider.Save(ProviderCallContext context) +113
   EPiServer.Data.Dynamic.DynamicDataStore.InternalSave(Identity id, Object value, TypeToStoreMapper typeToStoreMapper, ProviderCallContext parentContext) +343
   EPiServer.Data.Dynamic.Internal.EPiServerDynamicDataStore.Save(Object value) +12
   Helpers.Services.DataDdsService.SaveConfiguration(ConfigurationItemViewModel userInput) +610
   Controllers.EditConfigurationController.SaveConfiguration(ConfigurationItemViewModel input) +144
   lambda_method(Closure , ControllerBase , Object[] ) +104
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +157
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +22
   System.Web.Mvc.Async.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32
   System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +50
   System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +228
   System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +228
   System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +228
   System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +26
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +100
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +576
   System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +132
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +163

#205752
Jul 23, 2019 15:40
Vote:
 

Right, so I have figured out some solution. I hope it is the right way, because I have no idea how all of this works. Documentation is out of date.

    public class Patcher
    {
        public static string[] oldNames = new string[2]{ "oldProperty1", "oldPropert2"};

        public static void Patch()
        {
            using ( var store = DynamicDataStoreFactory.Instance.GetStore( typeof ( ConfigDataModel ) ) )
            {
                var item = store?.StoreDefinition.AllMappings.FirstOrDefault ( x => oldNames.Contains(x.PropertyName) );
                if (item == null)
                    return;

                store.StoreDefinition.Remap ( typeof ( ConfigDataModel ) );
                store.StoreDefinition.CommitChanges ();

            }
        }
    }

I think that nested type is not flaged to be mapped, only the top type is. So I needed to call Remap manually in one of my services implementing [InitializableModule].

#205817
Jul 24, 2019 13:28
Vote:
 

Wojciech,

Please provide link to out of date documentation so we can research the issue.

#205846
Jul 24, 2019 18:38