In Funq and probably most other IoC containers I can simply do this to configure a type:
container.Register<ISomeThing>(c => new SomeThing());
How could I quickly extend MEF (or use existing MEF functionality) to do the same without using attributes.
Here is how I thought I could do it:
var container = new CompositionContainer();
var batch = new CompositionBatch();
batch.AddExport<ISomeThing>(() => new SomeThing());
batch.AddExportedValue(batch);
container.Compose(batch);
With this extension method for CompositionBatch:
public static ComposablePart AddExport<TKey>(this CompositionBatch batch, Func<object> func)
{
var typeString = typeof(TKey).ToString();
return batch.AddExport(
new Export(
new ExportDefinition(
typeString,
new Dictionary<string, object>() { { "ExportTypeIdentity", typeString } }),
func));
}
If I later do:
var a = container.GetExport<ISomeThing>().Value;
var b = container.GetExport<ISomeThing>().Value;
Both instance are the same. How can I force (configure) them to be different instances?
If this is not the way to go, how would I do this in MEF?
I would imagine the key is to add the delegate to the container, e.g.:
container.AddExportedValue<Func<ISomething>>(() => new Something());
That way you can grab the delegate and execute it:
var factory = container.GetExport<Func<ISomething>>();
ISomething something = factory();
Of course, MEF (Silverlight) does provide a native ExportFactory<T> (and ExportFactory<T,TMetadata> type that supports the creation of new instances for each call to import. You can add support for this by downloading Glen Block's ExportFactory for .NET 4.0 (Desktop) library.
If you don't want to use attributes, you can use this trick (based on Mark Seemann's blogpost).
First, create a generic class like this:
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MefAdapter<T> where T : new()
{
private readonly T export;
public MefAdapter()
{
this.export = new T();
}
[Export]
public virtual T Export
{
get { return this.export; }
}
}
Now you can register any class you want in the container, like this:
var registeredTypesCatalog = new TypeCatalog(
typeof(MefAdapter<Foo>),
typeof(MefAdapter<Bar>),
...);
var container = new CompositionContainer(catalog);
Alternatively, you could implement your own export provider derived from ExportProvider, which allows you to pretty much duplicate Funq's way of working:
var provider = new FunqyExportProvider();
provider.Register<IFoo>(context => new Foo());
var container = new CompositionContainer(provider);
Both instance are the same. How can I force (configure) them to be different instances?
Simply mark the SomeThing class like this:
[Export(typeof(ISomeThing)]
[PartCreationPolicy(CreationPolicy.NonShared]
public class SomeThing : ISomeThing
{
...
}
And then you will get different instances wherever you import ISomeThing.
Alternatively, you can also set a required creation policy on an import:
[Export(typeof(IFoo))]
public class Foo : IFoo
{
[Import(typeof(ISomeThing),
RequiredCreationPolicy = CreationPolicy.NonShared)]
public ISomething SomeThing { private get; set; }
}
In Glen Block's Skydrive directory linked to in Matthew Abbott's answer I found something that seems simple and lightweight: A FuncCatalog. Download it here: FuncCatalogExtension.
Using the few little classes from that project I could now do this:
var funcCatalog = new FuncCatalog();
funcCatalog.AddPart<ISomeThing>(ep => new SomeThing());
var container = new CompositionContainer(funcCatalog);
var batch = new CompositionBatch();
batch.AddExportedObject<ExportProvider>(container);
container.Compose(batch);
var a = container.GetExportedObject<ISomeThing>();
var b = container.GetExportedObject<ISomeThing>();
Related
In the Register method of my Web API 2 project I've added this bit of code so that the returned JSON is automatically camel cased:
public static void Register(HttpConfiguration config) {
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Formatting = Formatting.Indented;
I have one or two methods, though, where I do not want it to do that, and really want the casing to be left alone. From an individual route's method is there a way to override that?
I have hundreds of methods that want it, and just a couple that don't.
You can try something like below to bypass Global Formatters settings,
public HttpResponseMessage Get()
{
Person content = new Person() { PersonID = 1, PersonName = "name" };
HttpResponseMessage resposne = new HttpResponseMessage();
resposne.Content = new ObjectContent(content.GetType(), content, new JsonMediaTypeFormatter());
return resposne;
}
Let's say we have a system that builds articles. The article has some components validator, cleaner, storage.... In the client to build an article I have to instantiate each component:
$title = 'title';
$description = 'Description in html';
//Cleaner just clean some things from each field.
$cleaner = new Cleaner();
//Validator throw exception if something is not correct
$validator = new Validator();
// Storage save files and article itself
$storage = new Storage();
//Dom Class get some files from description field
$dom = new Dom();
$files = $dom->getFiles($description);
$storage->files($files);
$article = new ArticleBuilder();
$article->addTitle($validator->title($cleaner->title($title)));
$article->addDescription($validator->description($cleaner->description($description)));
$article->add....
Without these it's impossible to build an article.
My question is:
Can I use the factory pattern to create all of these like this:
class ArticleFactory
{
private $article;
public function __construct()
{
$this->article = new ArticleBuilder();
}
public function setTitle(string $title)
{
$title = ($this->validator())->title($title);
$title = ($this->cleaner())->title($title);
$this->article->addTitle($title);
}
public function setDescription(string $des)
{
$des = ($this->validator())->title($des);
$des = ($this->cleaner())->title($des);
$this->article->addDescription($des);
}
public function getArticle(): ArticleBuilder
{
return $this->article;
}
public function getFiles($description)
{
return ($this->dom())->getFiles($description);
}
public function storeFile($files)
{
($this->storage())->files($files);
}
public function validator(): ValidatorInterface
{
return new Validator();
}
public function cleaner(): CleanerInterface
{
return new Cleaner();
}
public function storage(): StorageInterface
{
return new Storage();
}
public function dom(): DomInterface
{
return new Dom();
}
}
In the client is more convenient to create an article with the above factory:
$myTitle = 'my title';
$myDes = 'mty description';
$article = new ArticleFactory();
$article->setTitle($myTitle);
$article->setDescription($myDes);
$files = $article->getFiles($description);
$article->storeFile($files);
Is this violates any of the SOLID principles?
Is there any better approach about this?
The class ArticleFactory seems to be violating SRP because ArticleFactory is concerned with more than one thing (building, storing, validating and cleaning articles).
There also seems to be a confusion here between the Factory pattern and the Builder pattern. If the class ArticleFactory also builds articles then it would be cleaner if it had (composition) a builder and delegated the building process to it. Do you really need a builder? Is the process of creating new articles so complicated/expensive that the builder pattern will add value?
The usage of nouns in the names of functions (function Validator, Cleaner, Storage) makes the code difficult to understand. What was your intention there?
Use verbs for functions and nouns for classes.
I am unsure on how I should be naming my View pages, they are all CamelCase.cshtml, that when viewed in the browser look like "http://www.website.com/Home/CamelCase".
When I am building outside of .NET my pages are named like "this-is-not-camel-case.html". How would I go about doing this in my MVC4 project?
If I did go with this then how would I tell the view to look at the relevant controller?
Views/Home/camel-case.cshtml
Fake edit: Sorry if this has been asked before, I can't find anything via search or Google. Thanks.
There are a few ways you can do this:
Name all of your views in the style you would like them to show up in the url
This is pretty simple, you just add the ActionName attribute to all of your actions and specify them in the style you would like your url to look like, then rename your CamelCase.cshtml files to camel-case.cshtml files.
Use attribute routing
Along the same lines as above, there is a plugin on nuget to enable attribute routing which lets you specify the full url for each action as an attribute on the action. It has convention attributes to help you out with controller names and such as well. I generally prefer this approach because I like to be very explicit with the routes in my application.
A more framework-y approach
It's probably possible to do something convention based by extending the MVC framework, but it would be a decent amount of work. In order to select the correct action on a controller, you'd need to map the action name on its way in to MVC to its CamelCase equivalent before the framework uses it to locate the action on the controller. The easiest place to do this is in the Route, which is the last thing to happen before the MVC framework takes over the request. You'll also need to convert the other way on the way out so the urls generated look like you want them to.
Since you don't really want to alter the existing method to register routes, it's probably best write a function in application init that loops over all routes after they have been registered and wraps them with your new functionality.
Here is an example route and modifications to application start that achieve what you are trying to do. I'd still go with the route attribute approach however.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
WrapRoutesWithNamingConvention(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
private void WrapRoutesWithNamingConvention(RouteCollection routes)
{
var wrappedRoutes = routes.Select(m => new ConventionRoute(m)).ToList();
routes.Clear();
wrappedRoutes.ForEach(routes.Add);
}
private class ConventionRoute : Route
{
private readonly RouteBase baseRoute;
public ConventionRoute(RouteBase baseRoute)
: base(null, null)
{
this.baseRoute = baseRoute;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var baseRouteData = baseRoute.GetRouteData(httpContext);
if (baseRouteData == null) return null;
var actionName = baseRouteData.Values["action"] as string;
var convertedActionName = ConvertHyphensToPascalCase(actionName);
baseRouteData.Values["action"] = convertedActionName;
return baseRouteData;
}
private string ConvertHyphensToPascalCase(string hyphens)
{
var capitalParts = hyphens.Split('-').Select(m => m.Substring(0, 1).ToUpper() + m.Substring(1));
var pascalCase = String.Join("", capitalParts);
return pascalCase;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var valuesClone = new RouteValueDictionary(values);
var pascalAction = valuesClone["action"] as string;
var hyphens = ConvertPascalCaseToHyphens(pascalAction);
valuesClone["action"] = hyphens;
var baseRouteVirtualPath = baseRoute.GetVirtualPath(requestContext, valuesClone);
return baseRouteVirtualPath;
}
private string ConvertPascalCaseToHyphens(string pascal)
{
var pascalParts = new List<string>();
var currentPart = new StringBuilder();
foreach (var character in pascal)
{
if (char.IsUpper(character) && currentPart.Length > 0)
{
pascalParts.Add(currentPart.ToString());
currentPart.Clear();
}
currentPart.Append(character);
}
if (currentPart.Length > 0)
{
pascalParts.Add(currentPart.ToString());
}
var lowers = pascalParts.Select(m => m.ToLower());
var hyphens = String.Join("-", lowers);
return hyphens;
}
}
}
I am trying to get an enum to serialize to it's int value when posting to Solr.
So I have implemented a ISolrFieldSerializer to do this, As suggested here. But I can seem to register it within the Windsor container in a way that it then gets used by SolrNet
Here is what I have:
This works fine apart from the serializer does not get used, although it appears in the containers components list. Any ideas?
container.Register(Component.For<ISolrFieldSerializer>().ImplementedBy<SolrEnumSerializer>());
Startup.Init<SearchBox>("http://10.10.10.10:0000/solr/boxes");
container.Register(Component.For<ISolrOperations<SearchBox>>()
.UsingFactoryMethod(k => ServiceLocator.Current.GetInstance<ISolrOperations<SearchBox>>()));
I sorted this by removing the default implementation and replace it with a custom one:
Startup.Container.Remove<ISolrFieldSerializer>();
var fieldSerializer = new CustomSerializer();
Startup.Container.Register<ISolrFieldSerializer>(c => fieldSerializer);
Custom Sertializer:
public class CustomSerializer : ISolrFieldSerializer
{
private readonly AggregateFieldSerializer _serializer;
public CustomSerializer()
{
_serializer = new AggregateFieldSerializer(new ISolrFieldSerializer[]
{
new MyCustom1Serializer(),
new MyCustom2Serializer(),
new CollectionFieldSerializer(this),
new GenericDictionaryFieldSerializer(this),
new NullableFieldSerializer(new BoolFieldSerializer()),
new NullableFieldSerializer(new DateTimeFieldSerializer()),
//new MoneyFieldSerializer(),
new FormattableFieldSerializer(),
new TypeConvertingFieldSerializer(),
});
}
public bool CanHandleType(Type t)
{
return _serializer.CanHandleType(t);
}
public IEnumerable<PropertyNode> Serialize(object obj)
{
return _serializer.Serialize(obj);
}
}
How do I do constructor injection when I'm manually initializing the class?
public class ApiKeyHandler : DelegatingHandler
{
private IApiService apiService;
public ApiKeyHandler(IApiService apiService)
{
this.apiService = apiService;
}
}
Initializing:
var apiKey = new ApiKeyHandler(/*inject here */);
How do I accomplish this? My bindings and everything is already setup.
You want to do something like this:
var apiKey = new ApiKeyHandler(kernel.Get<IApiService>());
However, why not inject the ApiKeyHandler itself?
var apiKey = kernel.Get<ApiKeyHandler>();
Here is an article about Ninject:
You basically want to set this up at the beginning of your code and have it available globally:
public IKernel kernel = new StandardKernel();
...
kernel.Bind<IApiService>()
.To<SomeConcreteApiService>();