NameTransformer and context

Topics: Conventions, Extensibility
Nov 22, 2011 at 3:15 AM

Well, I started using the latest version (its been awhile) and was confused why my views weren't loading. Finally figured out that the conventions changed in the NameTransformer from replacing "Model" with "" to "Model$" with "". See, I used to place my view models in a folder called "ViewModels" and my views in "Views" but didn't decorate my classes with ViewModel or View at the end. I dislike all extra stuff and would much rather differentiate by namespace. I did figure out that I could add my own NameTransformer rule and got it all fixed.

Now, the problem at hand. When using a context (i.e. cal:View.Context), it assumes that I have "View" at the end of my class name and does a Regex.Replace with "View$" with "."+Context. I didn't want to name my views that way, hence my rule, but no easy way to replace this behavior without recreating LocateTypeForModelType. I would much rather have a lighter weight mechanism for this. Perhaps something similar to the NameTransformer, or perhaps built into the NameTransformer itself.

Any thought on any of this?

Coordinator
Jan 19, 2012 at 2:32 PM

I'm not sure if we can get it fixed for 1.3 since that's going to release either this afternoon or tomorrow. We will see.

Jan 19, 2012 at 2:50 PM

I can change the code to append the ".<context>" if "View$" doesn't match. I'm not sure what kind of implications that would have.

Jan 19, 2012 at 5:25 PM

Yeah, this is going to be a little more involved. I have an idea for a solution that I'll post shortly.

Coordinator
Jan 19, 2012 at 9:05 PM

Do you think you can get a fix in today? for v1.3? or should we push this issue to the next release? (I have to release v1.3 tomorrow)

Jan 19, 2012 at 9:41 PM

I should have something within the next 1 or 2 hours? Is that OK?

Jan 19, 2012 at 10:09 PM

This new behavior required variabilizing several values used internally, so I created a configuration class (TypeMappingConfiguration) as a means to pass the values to the locator classes. I exposed a ConfigureTypeMappings() method to override the defaults in the bootstrapper. The code below shows how this works:

string typename, result;

ViewLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        DefaultSubNSViewModels = "MyViewModels",    //default value is "ViewsModels"
        DefaultSubNSViews = "MyViews",              //default value is "Views"
        UseNameSuffixesInMappings = true,           //this is the default value
        ViewModelSuffix = "ViewModel",              //default value is "ViewModel"
        ViewSuffixList = new List<string>(new string[] { "View", "Page" })      //default values are "View", "Page"
    }
);

typename = "Name.Space.MyViewModels.CustomerViewModel";
result = String.Join(", ", ViewLocator.TransformName(typename).ToArray());
//Name.Space.MyViews.CustomerView

typename = "Name.Space.MyViewModels.CustomerPageViewModel";
result = String.Join(", ", ViewLocator.TransformName(typename).ToArray());
//Name.Space.MyViews.CustomerPage

//Reconfigure for Spanish
ViewLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        DefaultSubNSViewModels = "ModelosDeVista",
        DefaultSubNSViews = "Vistas",
        ViewModelSuffix = "ModeloDeVista",
        ViewSuffixList = new List<string>(new string[] { "Vista", "Pagina" })
    }
);
typename = "Espacio.De.Nombres.ModelosDeVista.Otros.ClienteModeloDeVista";
result = String.Join(", ", ViewLocator.TransformName(typename).ToArray());
//Espacio.De.Nombres.Vistas.Otros.ClienteVista

typename = "Espacio.De.Nombres.ModelosDeVista.Otros.ClientePaginaModeloDeVista";
result = String.Join(", ", ViewLocator.TransformName(typename).ToArray());
//Espacio.De.Nombres.Vistas.Otros.ClientePagina


//Reconfigure with defaults
ViewLocator.ConfigureTypeMappings(new TypeMappingConfiguration());

typename = "Name.Space.ViewModels.CustomerViewModel";
result = String.Join(", ", ViewLocator.TransformName(typename, "MyContext").ToArray());
//Name.Space.Views.Customer.MyContext"

typename = "Name.Space.ViewModels.Customer";
result = String.Join(", ", ViewLocator.TransformName(typename, "MyContext").ToArray());
//(none) Note that there's no "ViewModel" name suffix. This is the current behavior

//Reconfigure to ignore name suffixes in mappings
ViewLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        UseNameSuffixesInMappings = false
    }
);

typename = "Name.Space.ViewModels.Customer";
result = String.Join(", ", ViewLocator.TransformName(typename, "MyContext").ToArray());
//Name.Space.Views.Customer.MyContext

I'm now working on the equivalent changes for the ViewModelLocator.

Jan 19, 2012 at 11:18 PM

Here is the updated ViewModelLocator in action:

typename = "Name.Space.CustomerView";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.ICustomerViewModel, Name.Space.ICustomer, Name.Space.CustomerViewModel, Name.Space.Customer

typename = "Name.Space.CustomerPage";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.ICustomerPageViewModel, Name.Space.CustomerPageViewModel

ViewModelLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        DefaultSubNSViewModels = "MyViewModels",    //default value is "ViewsModels"
        DefaultSubNSViews = "MyViews",              //default value is "Views"
        UseNameSuffixesInMappings = true,           //this is the default value
        ViewModelSuffix = "ViewModel",              //default value is "ViewModel"
        ViewSuffixList = new List<string>(new string[] { "View", "Page" })      //default values are "View", "Page"
    }
);

typename = "Name.Space.MyViews.CustomerView";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.MyViewModels.ICustomerViewModel, Name.Space.MyViewModels.ICustomer, Name.Space.MyViewModels.CustomerViewModel, Name.Space.MyViewModels.Customer

typename = "Name.Space.MyViews.CustomerPage";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.MyViewModels.ICustomerPageViewModel, Name.Space.MyViewModels.CustomerPageViewModel

//Reconfigure for Spanish
ViewModelLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        DefaultSubNSViewModels = "ModelosDeVista",
        DefaultSubNSViews = "Vistas",
        ViewModelSuffix = "ModeloDeVista",
        ViewSuffixList = new List<string>(new string[] { "Vista", "Pagina" })
    }
);
typename = "Espacio.De.Nombres.Vistas.ClienteVista";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Espacio.De.Nombres.ModelosDeVista.IClienteModelosDeVista, Espacio.De.Nombres.ModelosDeVista.ICliente, Espacio.De.Nombres.ModelosDeVista.ClienteModelosDeVista, Espacio.De.Nombres.ModelosDeVista.Cliente

typename = "Espacio.De.Nombres.Vistas.ClientePagina";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Espacio.De.Nombres.ModelosDeVista.Otros.IClientePaginaModelosDeVista, Espacio.De.Nombres.ModelosDeVista.Otros.ClientePaginaModelosDeVista

//Reconfigure with defaults
ViewModelLocator.ConfigureTypeMappings(new TypeMappingConfiguration());
typename = "Name.Space.Views.Customer";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//(none) Note that there's no "View" name suffix. This is the current behavior

//Reconfigure to ignore name suffixes in mappings
ViewModelLocator.ConfigureTypeMappings
(
    new TypeMappingConfiguration()
    {
        UseNameSuffixesInMappings = false
    }
);

result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.ViewModels.ICustomer, Name.Space.ViewModels.Custome
Jan 19, 2012 at 11:36 PM

Updates have been pushed to trunk.

Coordinator
Jan 20, 2012 at 3:12 AM

Fancy. Cool.

Jan 20, 2012 at 2:45 PM
Edited Jan 20, 2012 at 2:46 PM

I can certainly change the method into a Func, but your View and ViewModel prefixes seem to present an issue beyond just the context scenario. How are any of your Views or ViewModels resolving correctly when locator classes assume that suffixes are used?

Jan 20, 2012 at 3:11 PM

I pushed the changes. I don't know whether or not Rob has already started creating the Nuget package for the release though.

Coordinator
Jan 20, 2012 at 4:51 PM

I'm going to begin creating the package in another hour or so...so it looks like it make it in ;)

Jan 20, 2012 at 5:34 PM

I'm actually currently testing some changes that would make your scenario work out of the box.

However, there was some trickiness to applying the filter for cases when you don't want interfaces returned from ViewModelLocator. I had to create a special RegEx capture group as a marker to apply this filter in the getReplaceStr Func. This could possibly cause your code to break if you had to add custom rules directly to NameTransfomer as you have. You obviously would no longer need to add custom rules directly to NameTransformer.

I'll let Rob decide if he wants to introduce this change. As I said, I'm ready to push these changes, but I'm currently testing now.

Coordinator
Jan 20, 2012 at 6:14 PM

If your tests prove that it works, go ahead and push it. I need to start putting together the release in the next hour or so.

Jan 20, 2012 at 6:38 PM
Edited Jan 20, 2012 at 6:39 PM

This is the new behavior:

string typename, result;

var config = new TypeMappingConfiguration()
{
    ViewModelSuffix = "M",
    ViewSuffixList = new List<string>(new string[] { "V" }),
    NameFormat = "{1}{0}", //default is "{0}{1}",
    IncludeViewSuffixInVMNames = false, //default is true
};

ViewLocator.ConfigureTypeMappings(config);
ViewModelLocator.ConfigureTypeMappings(config);

typename = "Name.Space.ViewModels.MCustomer";
result = String.Join(", ", ViewLocator.TransformName(typename, null).ToArray());
//Name.Space.Views.VCustomer

result = String.Join(", ", ViewLocator.TransformName(typename, "MyContext").ToArray());
//Name.Space.Views.Customer.MyContext

typename = "Name.Space.Views.VCustomer";
result = String.Join(", ", ViewModelLocator.TransformName(typename, true).ToArray());
//Name.Space.ViewModels.IMCustomer, Name.Space.ViewModels.ICustomer, Name.Space.ViewModels.MCustomer, Name.Space.ViewModels.Customer

//Concrete types only
result = String.Join(", ", ViewModelLocator.TransformName(typename, false).ToArray()); //Name.Space.ViewModels.MCustomer, Name.Space.ViewModels.Customer
Coordinator
Jan 20, 2012 at 6:42 PM

@pauli7

Can you confirm that this will enable your scenario. It seems pretty flexible...

Jan 20, 2012 at 6:57 PM

It's been pushed.

Jan 20, 2012 at 7:00 PM

To be honest, I personally use naming conventions like your example quite often (ViewCustomer vs. CustomerView) since it makes grouping files easier, so it was for my personal benefit as well. ;)

Coordinator
Jan 20, 2012 at 7:07 PM

I think this is probably fairly common. There are a lot of small variations in the way people like to organize things. So, on the whole, I think these changes will make the entire system much more "personal" for developers.

Coordinator
Jan 20, 2012 at 7:11 PM

@cb55555

If you don't mind, I'd appreciate it if you would update the docs. You should have edit rights.

 

That's it! Preparing the build now.

Jan 20, 2012 at 7:27 PM

Will do. Good thing I didn't start making any changes last night. :)