Specify RequiredCreationPolicy for non-Attributed Imports - c#-4.0

I have an IoC wrapper that uses MEF as it's DI container, an applicable snippet of the wrapper is shown below.
public static bool TryGetComponent<T>(out T component)
{
CompositionContainer container = RetrieveContainer();
T retrievedComponent = container.GetExportedValueOrDefault<T>();
if (retrievedComponent.Equals(default(T)))
{
component = default(T);
return false;
}
component = retrievedComponent;
return true;
}
Most of the exported components in the CompositionContainer specify a CreationPolicy of "Any".
[PartCreationPolicy(CreationPolicy.Any)]
For types that I create I can easily use the following import attribute to get MEF to serve the exported types as NonShared instances.
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
However, since my IoC wrapper must also be used by classes that do not use MEF or any of its Import attributes and must use my IoC API to obtain instances exported types. I need a way to specify the CreationPolicy when I programmatically use the CompositionContainer to GetExports and GetExportedValues. Is this even possible without using import attributes?

If you really want to query the container exactly like as if you had a ImportAttribute with RequiredCreationPolicy=NonShared then try creating your own custom ContractBasedImportDefinition. One of the parameters for to the contructor is a CreationPolicy that represents the required creation policy.
Something like:
container.GetExports(new ContractBasedImportDefinition(
AttributedModelServices.GetContractName(type),
AttributedModelServices.GetTypeIdentity(type),
null,
ImportCardinality.ZeroOrMore,
false,
false,
CreationPolicy.NonShared));
Of course you can adjust the parameters as necessary but this will get you moving in the right direction and will cause the container to create NonShared versions of any part that is marked as Any (which is the default).

Well, CreationPolicy is passed as part of a component's metadata. This means, you should be able to query the metadata for the part, and see if it exists. The way CreationPolicy is specified in metadata is to use the full type name System.ComponentModel.Composition.CreationPolicy as the key, and the enum result as the value. So, knowing this we can build an extension method:
public static T GetExportedValueOrDefault<T>(this CompositionContainer container, CreationPolicy creationPolicy)
{
var metadataKey = typeof(CreationPolicy).FullName;
var lazy = container.GetExportedValueOrDefault<T, IDictionary<string, object>>();
if (lazy == null)
return default(T);
if (lazy.Metadata.ContainsKey(metadataKey))
{
// If the creation policy matches the required, return.
if (((CreationPolicy)lazy.Metadata[metadataKey]) == creationPolicy)
return lazy.Value;
}
else
{
// Return the value as we assume it satisfies the default CreationPolicy = Any
return lazy.Value;
}
return default(T);
}
Now, firstly we create our expected key, and then we grab a Lazy<T, TMetadata> instance which includes the type and any associated metadata as a Lazy<T, IDictionary<string, object>> instance. If the lazy comes back as null, we can fail early because there were no matching parts at all.
Next, we can check the metadata dictionary Lazy.Metadata to determine if the metadata exists. If it does, we need to cast and compare against our chosen metadata. If that succeeds, return our part instance.
If that doesn't succeed (e.g., if the part is using the implicit CreationPolicy of Any [i.e., the PartCreationPolicyAttribute is omitted from the export]), we'll assume that the part can be returned, as we can match on the default Any creation policy, so we can match both NonShared and Shared parts.
You should be able to use this in place of the normal GetExportedValueOrDefault<T> call:
T retrievedComponent = container.GetExportedValueOrDefault<T>(CreationPolicy.NonShared);

Related

How to decorate the final class DocumentGenerator

I am having problems to decorate the final class "DocumentGenerator" (in vendor/shopware/core/Checkout/Document/Service/DocumentGenerator.php) and overwrite the "generate" function inside of it.
I tried to decorate it the usual way, but an error is thrown because the "DocumentController" class excepts the original class and not my decorated one?
Argument 2 passed to Shopware\Core\Checkout\Document\DocumentGeneratorController::__construct() must be an instance of Shopware\Core\Checkout\Document\Service\DocumentGenerator
Its also not possible to extend from the class in my decorated class, because the "DocumentGenerator" is a final class.
My goal is to execute additional code, after an order document is generated. Previously I successfully used to decorate the "DocumentService" Class, but its marked as deprecated and shouldnt be used anymore. Also the "DocumentGenerator" class is used for the new "bulkedit" function for documents as of Version 6.4.14.0
I'm grateful for every tip.
As #j_elfering already wrote it's by design that you should not extend that class and therefore also shouldn't decorate it.
To offer a potential alternative:
Depending on what you want to do after a document has been generated it might be enough to add a subscriber to listen to document.written, check if it was a new document created and then work with the data from the payload for fetching/persisting data depending on that.
public static function getSubscribedEvents()
{
return [
'document.written' => 'onDocumentWritten',
];
}
public function onDocumentWritten(EntityWrittenEvent $event): void
{
foreach ($event->getWriteResults() as $result) {
if ($result->getOperation() !== EntityWriteResult::OPERATION_INSERT) {
// skip if the it's not a new document created
continue;
}
$payload = $result->getPayload();
// do something with the payload
}
}
Probably not what you want to hear but: The service is final in purpose as it is not intended to be decorated.
So the simple answer is you can't. Depending on your use case there may be other ways that don't rely on decoration.

How to create instance of WCMUsePojo in my Prosper spec?

I have a functioning WCMUsePojo Groovy class which is called from a sightly html component. I am trying to create an instance of my WCMUsePojo class for testing based on the content from the Prosper setup method.
It's basically the same type of question as How can I create an instance of WCMUsePojo in a servlet for a specific page? But I don't see it answered and this is specifically about how to unit test methods in WCMUsePojo classes within the Prosper framework. Is there a Java or Groovy equivalent to Sightly's data-sly-use attribute?
def setupSpec() {
pageBuilder.content {
page_with_new_gridwrapper {
'jcr:content'{
'gridpar' {
'my_gridwrapper'('sling:resourceType':'my/components/my_gridwrapper') {
}
}
}
}
}
}
def "Test Page with New Grid Container"(){
Page page = pageManager.getPage("/content/page_with_new_gridwrapper")
// the following 2 lines return null :-(
// but I would prefer these to return an object with the type MyGridContainerHelper
MyGridContainerHelper cmp = page.getContentResource().getChild("gridpar/my_gridwrapper").adaptTo(MyGridContainerHelper.class)
Component cmp2 = WCMUtils.getComponent(page.getContentResource().getChild("gridpar/my_gridwrapper"))
expect:
page != null //passes
page.getContentResource().getChild("gridpar/my_gridwrapper") != null //passes
cmp != null // fails
cmp2 != null // fails
cmp.resourceType == "my/components/my_gridwrapper" // fails
}
To adapt an instance of MyGridContainerHelper from a resource object, you can implement your helper class using Sling Models rather than extending WCMUsePojo. Prosper supports registration of #org.apache.sling.models.annotations.Model-annotated classes by using the following syntax in the setupSpec block of your specification:
slingContext.addModelsForPackage("helper.class.package.name")
This eliminates the need to manually construct a Bindings object and initialize the POJO; the Sling model factory does all the work for you. Since Sightly's "use" attribute supports any class that is adaptable from a Resource or SlingHttpServletRequest, no additional changes are required for your existing Sightly template.
I ended up instantiating the object and calling the init method passing in a SimpleBindings object containing the resource I was testing with. This seems to work well for my purposes.
MyGridContainerHelper gridContainer = new MyGridContainerHelper();
SimpleBindings bindings = new SimpleBindings()
bindings.put("resource", page.getContentResource().getChild("gridpar/my_gridwrapper"))
gridContainer.init(bindings)

Setting types of parsed values in Antlr

I have a rule that looks like this:
INTEGER : [0-9]+;
myFields : uno=INTEGER COMMA dos=INTEGER
Right now to access uno I need to code:
Integer i = Integer.parseInt(myFields.uno.getText())
It would be much cleaner if I could tell antler to do that conversion for me; then I would just need to code:
Integer i = myFields.uno
What are my options?
You could write the code as action, but it would still be explicit conversion (eventually). The parser (like every parser) parses the text and then it's up to "parsing events" (achieved by listener or visitor or actions in ANTLR4) to create meaningful structures/objects.
Of course you could extend some of the generated or built-in classes and then get the type directly, but as mentioned before, at some point you'll always need to convert text to some type needed.
A standard way of handling custom operations on tokens is to embed them in a custom token class:
public class MyToken extends CommonToken {
....
public Integer getInt() {
return Integer.parseInt(getText()); // TODO: error handling
}
}
Also create
public class MyTokenFactory extends TokenFactory { .... }
to source the custom tokens. Add the factory to the lexer using Lexer#setTokenFactory().
Within the custom TokenFactory, override the method
Symbol create(int type, String text); // (typically override both factory methods)
to construct and return a new MyToken.
Given that the signature includes the target token type type, custom type-specific token subclasses could be returned, each with their own custom methods.
Couple of issues with this, though. First, in practice, it is not typically needed: the assignment var is statically typed, so as in the the OP example,
options { TokenLabelType = "MyToken"; }
Integer i = myFields.uno.getInt(); // no cast required
If Integer is desired & expected, use getInt(). If Boolean ....
Second, ANTLR options allows setting a TokenLabelType to preclude the requirement to manually cast custom tokens. Use of only one token label type is supported. So, to use multiple token types, manual casting is required.

Is there a way to ignore some entity properties when calling EdmxWriter.WriteEdmx

I am specifically using breezejs and the server code for breeze js converts the dbcontext into a form which is useable on the clientside using EdmxWriter.WriteEdmx. There are many properties which I have added JsonIgnore attributes to so that they don't get passed to the client side. However, the metadata that is generated (and passed to the clientside) from EdmxWriter.WriteEdmx still has those properties. Is there any additional attribute that I can add to those properties that I want ignored so that they are ignored by EdmxWriter.WriteEdmx? Or, would I need to make a separate method so as not to have any other unintended side effects.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
public class NorthwindMetadataContext : NorthwindContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Hide from clients
modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
// Ignore UserSessionId in metadata (but keep it in base DbContext)
modelBuilder.Entity<Customer>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Employee>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Order>().Ignore(t => t.UserSessionId);
// ... more of the same ...
}
}
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
public class NorthwindRepository
{
public NorthwindRepository()
{
_contextProvider = new EFContextProvider<NorthwindContext>();
}
public string Metadata
{
get
{
// Returns metadata from a dedicated DbContext that is different from
// the DbContext used for other operations
// See NorthwindMetadataContext for more about the scenario behind this.
var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
return metaContextProvider.Metadata();
}
}
public SaveResult SaveChanges(JObject saveBundle)
{
PrepareSaveGuard();
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Category> Categories {
get { return Context.Categories; }
}
// ... more members ...
}
Pretty clever, eh?
Just remember that the UserSessionId is still on the server-side class model and could be set by a rogue client's saveChanges requests. DocCode guards against that risk in its SaveChanges validation processing.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
Pretty clever, eh?
If you use the [NotMapped] attribute on a property, then it should be ignored by the EDMX process.

Grails: How to make everything I create Upper Case?

I am currently using CSS to change everything I write to upperCase when I create an entry, but that is not enough. When I save things, the text shown in the text fields is upper case, but the real value that Grails stores stays in lower case.
I am assuming I'd need to change something in the controller or anything.
Maybe transforming the $fieldValue CSS could work??
Any ideas would help!
Thnks!
You could just write setters for your domain object?
class Domain {
String aField
void setAField( String s ){
aField = s?.toUpperCase()
}
}
I think you are asking how to change values on your domain objects to uppercase. If this is not the case please clarify the question.
You have a bunch of options. I would recommend
1) In a service method, before you save, using String.toUpperCase() to modify the appropriate values on the domain object.
or
2) You can use the underlying Hibernate interceptors by defining a beforeInsert method on your domain object, and doing the toUpperCase there. (see 5.5.1 of the grails documentation)
or
3) You could do this client side. However, if it is a "business requirement" that the values are stored as upper, then I recommend doing the translation server side. It is easier to wrap tests around that code....
Using annotations is cleanest approach
import org.grails.databinding.BindingFormat
class Person {
#BindingFormat('UPPERCASE')
String someUpperCaseString
#BindingFormat('LOWERCASE')
String someLowerCaseString
}
Here is link for it: Grails doc for data binding
You can use Groovy metaprogramming to change the setter for all domain class String-typed properties without actually writing a custom setter for each property.
To do this, add something like the following to the init closure of Bootstrap.groovy
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.class.metaClass.setProperty = { String name, value ->
def metaProperty = delegate.class.metaClass.getMetaProperty(name)
if (metaProperty) {
// change the property value to uppercase if it's a String property
if (value && metaProperty.type == String) {
value = value.toUpperCase()
}
metaProperty.setProperty(delegate, value)
} else {
throw new MissingPropertyException(name, delegate.class)
}
}
}
}

Resources