What happens when I call Mapper.CreateMap with the same types multiple times?
Does it rewrite the previous map? If so, is it possible to make it throw an exception if I try to create map, that is already created?
When calling Mapper.CreateMap for the same set of source and destination several times, nothing will happen at all as the Mapper.CreateMap<TSource, TDestination>() does not set up any extensions for a mapping configuration.
If you set the overrides for IMappingExpression like this
Mapper.CreateMap<TSource, TDestination>().ConstructUsing(x=>new TDestination(x.SomeField)),
than yes, the configuration for this mapping will be replaced with the new one.
Regarding the second part of your question, I know the way to verify if the map was already created:
public TDestination Resolve<TSource, TDestination>(TSource source)
{
var mapped = Mapper.FindTypeMapFor(typeof(TSource), typeof(TDestination)); //this will give you a reference to existing mapping if it was created or NULL if not
if (mapped == null)
{
var expression = Mapper.CreateMap<TSource, TDestination>();
}
return Mapper.Map<TSource, TDestination>(source);
}
Related
I'm trying to see if its possible to programmatically add advisers to an outboundgateway (amqp:outbound-gateway) defined in xml configuration.
I was exploring the possibilities using BeanFactoryPostProcessor but could not find a way.
I noticed the bean is of the type EventDrivenConsumer when it is retrieved by bean name of the outbound gateway. But the method to add advisers is defined in AbstractReplyProducingMessageHandler. Is there a way to access this method or any other means of achieving this?
Thanks in advance for any help.
Update with the working code based on the accepted answer below:
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// retrieve the gateway bean definition by its name
BeanDefinition gatewayBeanDefinition = beanFactory
.getBeanDefinition("myOutboundgateway");
if (gatewayBeanDefinition != null) {
while (gatewayBeanDefinition.getOriginatingBeanDefinition() != null) {
gatewayBeanDefinition = gatewayBeanDefinition.getOriginatingBeanDefinition();
}
PropertyValue handler = gatewayBeanDefinition.getPropertyValues().getPropertyValue("handler");
if (handler != null) {
String handlerBeanName = ((RuntimeBeanReference) handler.getValue()).getBeanName();
BeanDefinition handlerBeanDefinition = beanFactory.getBeanDefinition(handlerBeanName);
while (handlerBeanDefinition.getOriginatingBeanDefinition() != null) {
handlerBeanDefinition = handlerBeanDefinition.getOriginatingBeanDefinition();
}
ManagedList adviceChain = new ManagedList();
// myAdvice is the advice that extend AbstractRequestHandlerAdvice as recommended and registered as a bean
adviceChain.add(new RuntimeBeanReference("myAdvice"));
// this code simply sets the adviceChain, but other situations might require adding an adviser to an already existing list.
handlerBeanDefinition.getPropertyValues().add("adviceChain", adviceChain);
}
}
}
With a handler that produces replies AbstractReplyProducingMessageHandler (ARPMH), we have to apply the advices internally to the handleRequestMessage method; the reason being that we don't want to advise the downstream flow, just this endpoint.
With handlers that don't produce output, the entire handler is advised.
You can't change the advice chain in an ARPMH after it has been initialized - the chain is applied during afterPropertiesSet().
Yes, you can modify the bean definition in a BFPP but finding the bean name of the handler can be tricky (ClassName + #n where n is the parse order) - it is registered with an alias endpoint.handler but you can't find a bean definition by its alias.
You could get a reference to the bean definition via the handler property on a bean definition for a ConsumerEndpointFactoryBean; once you have a reference to the BD, you can modify the advice chain.
hi does anyone encountered this error? everytime I use PXSelect on a foreach loop in which on the other source code does but on my code does not, could anyone identify the cause? the code below is also the the original source code from Acumatica but I only changed the Datamember from PaymentCharges to OtherCharges
[PXOverride]
public void VoidCheckProc(ARPayment doc)
{
foreach (PXResult<ARPaymentChargeTran> paycharge in PXSelect<ARPaymentChargeTran, Where<ARPaymentChargeTran.docType, Equal<Required<ARPayment.docType>>, And<ARPaymentChargeTran.refNbr, Equal<Required<ARPayment.refNbr>>>>>.
Select(this, doc.DocType, doc.RefNbr))
{
ARPaymentChargeTran charge = PXCache<ARPaymentChargeTran>.CreateCopy((ARPaymentChargeTran)paycharge);
charge.DocType = Document.Current.DocType;
charge.CuryTranAmt = -1 * charge.CuryTranAmt;
charge.Released = false;
charge.CuryInfoID = Document.Current.CuryInfoID;
charge.CashTranID = null;
//PaymentCharges.Insert(charge);
OtherCharges.Insert(charge);
}
}
I believe, you are writing this method in an extension for the base BLC
So instead of using 'this', use 'this.Base'
The Select method is non-static, as the error message says, but you call it on the PXSelect<...>-type. You need to have an instance of that type.
Based on Hybridzz answer, I assume you used the wrong overload of the Select-method. Probably your arguments do not have the correct type, so the compiler selects the best fitting overload of the method. In this case, it selects the one accepting only the argument params object[] o, which is non-static. A bit misleasing design of the API you use.
I want to customise the way AutoMapper converts my types without losing the features already implemented by AutoMapper.
I could create a custom ITypeConverter instance but I can't see how to invoke the default behaviour.
Mapper.CreateMap<MyDomainObject, MyDto>
.ConvertUsing<MyTypeConverter>();
...
public class MyTypeConverter : TypeConverter<MyDomainObject, MyDto>
{
public MyDto ConvertCore(MyDomainObject source)
{
var result = // Do the default mapping.
// do my custom logic
return result
}
}
If I try to call var result = Mapper.Map<MyDto>(source) it gets into an infinite loop. I effectively want AutoMapper to do everything it normally would assuming there was no TypeConverter defined.
Any help greatly appreciated.
If you only want to customise some values on the destination object, then you're better off with a Custom Value Resolver - TypeConverters are designed to handle the whole conversion.
The doc page listed above will have enough to get you started: when you have implemented the CustomResolver you apply it like this, and AutoMapper will do the default mapping for the other properties:
Mapper.CreateMap<MyDomainObject, MyDto>()
.ForMember(dest => dest.TargetProperty,
opt => opt.ResolveUsing<CustomResolver>());
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);
UPDATE
I have to apologize for confusing the readers. After I got totally lost in the code, I reverted all my changes from Mercurial repo, carefully applied the same logic as before -- and it worked. The answers below helped me understand the (new to me) concept better, and for that I gave them upvotes.
Bottom line: if a call to a missing method happens within a closure, and resolution set to DELEGATE_FIRST, methodMissing() will be called on the delegate. If it doesn't -- check you own code, there is a typo somewhere.
Thanks a lot!
Edit:
OK, now that you've clarified what your are doing (somewhat ;--))
Another approach (one that I use for DSLs) is to parse your closure group to map via a ClosureToMap utility like this:
// converts given closure to map method => value pairs (1-d, if you need nested, ask)
class ClosureToMap {
Map map = [:]
ClosureToMap(Closure c) {
c.delegate = this
c.resolveStrategy = Closure.DELEGATE_FIRST
c.each{"$it"()}
}
def methodMissing(String name, args) {
if(!args.size()) return
map[name] = args[0]
}
def propertyMissing(String name) { name }
}
// Pass your closure to the utility and access the generated map
Map map = new ClosureToMap(your-closure-here)?.map
Now you can iterate through the map, perhaps adding methods to applicable MCL instance. For example, some of my domains have dynamic finders like:
def finders = {
userStatusPaid = { Boolean active = true->
eq {
active "$active"
paid true
}
}
}
I create a map using the ClosureToMap utility, and then iterate through, adding map keys (methods, like "userStatus") and values (in this case, closure "eq") to domain instance MCL, delegating the closure to our ORM, like so:
def injectFinders(Object instance) {
if(instance.hasProperty('finders')) {
Map m = ClosureToMap.new(instance.finders).map
m?.each{ String method, Closure cl->
cl.delegate = instance.orm
cl.resolveStrategy = Closure.DELEGATE_FIRST
instance.orm.metaClass."$method" = cl
}
}
}
In this way in controller scope I can do, say:
def actives = Orders.userStatusPaid()
and "eq" closure will delegate to the ORM and not domain Orders where an MME would occur.
Play around with it, hopefully I've given you some ideas for how to solve the problem. In Groovy, if you can't do it one way, try another ;--)
Good luck!
Original:
Your missingMethod is defined on string metaclass; in order for it to be invoked, you need "someString".foo()
If you simply call foo() by itself within your closure it will fail, regardless of delegation strategy used; i.e. if you don't use the (String) delegate, good luck. Case in point, do "".foo() and it works.
I don't fully understand the issue either, why will you not have access to the closure's delegate? You are setting the closure's delegate and will invoke the closure, which means you will have access to the delegate within the closure itself (and can just delegate.foo())
nope, you will not catch a missing method and redirect it to the delegate with metaclass magic.
the closure delegate is the chance to capture those calls and adapt them to the backing domain.
that means...
you should create your own delegate with the methods required by the dsl.
do not try to force a class to do delegate work if it's not designed for the task, or the code will get really messy in not time.
keep everything dsl related in a set of specially designed delegate classes and everything will suddenly become ridiculously simple and clear.