In order to automatically trim all input strings and remove new lines, I developed an implementation of JsonConverter<string> and registered it in Startup with
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Converters = new List<JsonConverter>
{
new StringTrimmer()
}
};
This works, but the string trimmer is negatively impacting third-party assemblies. Is there a way I can scope the default settings to my application's assembly, or register an instance with JsonConvert.Create(mySettings)? Or is there another method which I could automatically trim all input without JsonConvert?
If you only care about changing how ASP.NET Core serializes JSON, then you can configure the JsonSerializerSettings for the MVC framework explicitly using AddJsonOptions:
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new StringTrimmer());
});
You can programmatically apply your converter only within certain assemblies by using a custom ContractResolver like this:
public class CustomResolver : DefaultContractResolver
{
private Assembly[] TargetAssemblies { get; set; }
public CustomResolver(params Assembly[] targetAssemblies)
{
TargetAssemblies = targetAssemblies;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization ms)
{
JsonProperty prop = base.CreateProperty(member, ms);
if (prop.PropertyType == typeof(string) &&
TargetAssemblies.Contains(prop.DeclaringType.Assembly))
{
prop.Converter = new StringTrimmer();
}
return prop;
}
}
Then use it within your default settings like this, where Foo is replaced with one of the known classes in your application's assembly:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CustomResolver(typeof(Foo).Assembly)
};
You can add more than one assembly if you need to, e.g.:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CustomResolver(typeof(Foo).Assembly, typeof(Bar).Assembly)
};
Make your StringTrimmer aware of the types (and their assemblies) it's dealing with. WriteJson() and ReadJson() have access to the type of object. Apply the trimming only to the types you care about.
Related
I use DictionaryAdapter to retrieve settings from appSettings section of my asp.net website.
The IoC configuration is done once, at the startup time and all kinds of different interfaces with getters are being registered with using single Configuration.AppSettings object:
var dictionaryAdapterFactory = new DictionaryAdapterFactory();
container.Register(
Types
.FromAssemblyNamed(assemblyName)
.Where(t => t.Name.EndsWith("AppSettings"))
.Configure(
component => component.UsingFactoryMethod(
(kernel, model, creationContext) =>
dictionaryAdapterFactory.GetAdapter(creationContext.RequestedType, ConfigurationManager.AppSettings))));
The appSettings section hosted in Web.config file works fine, but it has its drawback when I want to update some settings during runtime. As it is web.config file, the whole app is restarted. I would like to be able to modify configuration at runtime without restarting website as a side effect. Therefore, I moved into separate file:
<appSettings configSource="AppSettings.config">
Now, changes are being reflected when retrieving them via ConfigurationManager.AppSettings["key"], but they are not reflected when accessing via
dynamic interfaces from DictionaryAdapter.
Is there any way to have tell DA to watch for the changes in source and not cache the values?
Although I didn't find the exact answer, I found a workaround. Instead of 'binding' DA directly to ConfigurationManager, i bind to a simple proxy that wraps CM:
public class AppSettingsProxy : NameValueCollection
{
public override string Get(string name)
{
return ConfigurationManager.AppSettings[name];
}
public override string GetKey(int index)
{
return ConfigurationManager.AppSettings[index];
}
}
Then jus tchange binding to my proxy instance:
container.Register(
Types
.FromAssemblyNamed(assemblyName)
.Where(t => t.Name.EndsWith("AppSettings"))
.Configure(
component => component.UsingFactoryMethod(
(kernel, model, creationContext) =>
dictionaryAdapterFactory.GetAdapter(creationContext.RequestedType, appSettingsProxy))));
The above works for me. While I can modify my website's settings at runtime without an restart, value changes now are reflected via dynamically generated proxes over my settings interfaces.
DictionaryAdapter does not itself by default cache the values. Here's a passing test to prove that.
public interface IFoo
{
string Foo { get; set; }
}
[Test]
public void Adapter_does_not_cache_values_once_read()
{
var dict = new NameValueCollection { { "Foo", "Bar" } };
var adapter = (IFoo)factory.GetAdapter(typeof(IFoo), dict);
var value = adapter.Foo;
dict["Foo"] = "Baz";
var value2 = adapter.Foo;
Assert.AreNotEqual(value, value2);
Assert.AreEqual("Baz", value2);
}
Are you sure you're not caching the value yourself in your code? Can you reproduce the behaviour in a test?
here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}
I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.
Interfaces in C# are great to ensure my controllers' action methods take the same number, data type and order of parameters. Unfortunately, that doesn't help keep the urls generated by the routing mechanism consistent. How can I ensure the names of parameters are the same?
For example:
How do I ensure that
sportswear/products
and
carsandtrucks/products
both take the parameter productId?
I'd like to try to avoid a lot of routes in global.asax.cs, as I feel that they're not intuitive, but I'm open to ideas.
I'd suggest that the best option would be a unit test that finds all implementations of your controller interfaces and ensures that the implementation parameter names match those on the interface.
So something like
public interface IController
{
ActionResult GetProducts(string productId);
}
[TestFixture]
public class IControllerTest
{
[Test]
public void EnsureImplementationsUseCorrectParameterNames()
{
// Assuming all required assemblies have been loaded
var implementations = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)));
var interfaceMethods = typeof(IController).GetMethods().ToList();
foreach (var implementation in implementations)
{
var methodPairs = interfaceMethods.Join(implementation.GetMethods(), mi => mi.ToString(), mi => mi.ToString(), (inner, outer) => new { InterfaceMethod = inner, ImplementationMethod = outer });
foreach (var methodPair in methodPairs)
{
using (var interfaceParameters = methodPair.InterfaceMethod.GetParameters().Cast<ParameterInfo>().GetEnumerator())
using (var implementationParameters = methodPair.ImplementationMethod.GetParameters().Cast<ParameterInfo>().GetEnumerator())
{
while (interfaceParameters.MoveNext() && implementationParameters.MoveNext())
{
Assert.AreEqual(interfaceParameters.Current.Name, implementationParameters.Current.Name);
}
}
}
}
}
}
Hope this helps.
I've just started to use AutoMapper in my MVC 3 project and I'm wondering how people here structure their projects when using it. I've created a MapManager which simply has a SetupMaps method that I call in global.asax to create the initial map configurations. I also need to use a ValueResolver for one of my mappings. For me, this particular ValueResolver will be needed in a couple of different places and will simply return a value from Article.GenerateSlug.
So my questions are:
How do you manage the initial creation of all of your maps (Mapper.CreateMap)?
Where do you put the classes for your ValueResolvers in your project? Do you create subfolders under your Model folder or something else entirely?
Thanks for any help.
i won't speak to question 2 as its really personal preference, but for 1 i generally use one or more AutoMapper.Profile to hold all my Mapper.CreateMap for a specific purpose (domaintoviewmodel, etc).
public class ViewModelToDomainAutomapperProfile : Profile
{
public override string ProfileName
{
get
{
return "ViewModelToDomain";
}
}
protected override void Configure()
{
CreateMap<TripRegistrationViewModel, TripRegistration>()
.ForMember(x=>x.PingAttempts, y => y.Ignore())
.ForMember(x=>x.PingResponses, y => y.Ignore());
}
}
then i create a bootstrapper (IInitializer) that configures the Mapper, adding all of my profiles.
public class AutoMapperInitializer : IInitializer
{
public void Execute()
{
Mapper.Initialize(x =>
{
x.AddProfile<DomainToViewModelAutomapperProfile>();
x.AddProfile<ViewModelToDomainAutomapperProfile>();
});
}
}
then in my global.asax i get all instances of IInitializer and loop through them running Execute().
foreach (var initializer in ObjectFactory.GetAllInstances<IInitializer>())
{
initializer.Execute();
}
that's my general strategy.
by request, here is the reflection implementation of the final step.
var iInitializer = typeof(IInitializer);
List<IInitializer> initializers = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => iInitializer.IsAssignableFrom(p) && p.IsClass)
.Select(x => (IInitializer) Activator.CreateInstance(x)).ToList();
foreach (var initializer in initializers)
{
initializer.Execute();
}