I'm not very familiar with AutoMapper but am trying to update AutoMapper 4 to AutoMapper 10 (last version to support .Net Framework) on a project, and I have run into a problem: The ResolutionContext changed in the 4-5 upgrade so that it no longer has a Parent property.
The existing code works with an attribute that is put on properties in the view model where zero is supposed to be treated as a null, and the existing type converter has this code in it:
var prop = context.Parent.Parent.DestinationType.GetProperty(context.MemberName);
var attributes = prop.GetCustomAttributes(false);
var zeroAttr = attributes.Where(a => a.GetType() == typeof(ZAttribute)).ToList();
But as of AutoMapper 5, ResolutionContext.Parent and ResolutionContext.Member no longer exists.
This allows mapping zero to null for any/all view model, as long as the property has the attribute, there doesn't need to a specific mapping for that type (I think that is how it works). If the attribute isn’t on the property the normal mapping is done (zero stays zero).
How can this be done with AutoMapper 10?
I'm thinking it might be some type of ValueResolver...
Related
My question is regarding properties in haxe.
If I understand it correctly, if I make property like this
var propertyInt(get, set):Int; according to haxe documentation it's not really a variable. But I can make it a variable by modifying it this way
#:isVar var propertyInt(get, set):Int;. Otherwise I need to make two methods:
function get_propertyInt()
return propertyInt;
function set_properyInt(i)
return propertyInt = i;
So my question is: does it matter if I'm using exclusively #:isVar var propertyInt(get, set):Int; ? For example I have more than 5 properties in my class and as you can imagine making this methods for every property could be huge pain in the rear. So what is the best approach?
Here's what the documentation has to say about physical properties (also known as backing fields):
A field is considered to be physical if it is either
variable
a property with the read-access or write-access identifier being default or null
a property with :isVar metadata
So you can set up a property that fully consists of calculated values. Think a read-only property giving you the area of a rectangle as a function of width and height, or think of a property that is backed by some other property and just returns/sets width and height in a different unit. Or maybe you just want to name your backing fields differently, say m_width and m_height.
The :isVar is helpful in situations where the property access rules etc. laid out above would let the compiler think that there is no backing field needed. In that case, the code would fail (from the docs again):
// This field cannot be accessed because it
// is not a real variable
public var x(get, set):Int;
function get_x() {
return x;
}
function set_x(x) {
return this.x = x;
}
By adding :isVar you basically tell the compiler that you absolutely want the backing field. The other option for your particular case would be to use default,default, in which case the compiler knows there is an automatic backing field required and access should be restricted according to the access level of the property (public or private):
public var propertyInt(default, default):Int;
In that case you might also use a variable directly, because the net effect is in essence the same:
public var propertyInt : Int;
Trying to exclude properties from a model from being included during serialization.
I am using the following syntax:
JsConfig<MyTestClass>.ExcludePropertyNames = new[] { "ShortDescription" };
Just after that I have the following:
return (from o in __someProvider.GetAll() select (new
{
o.Name,
o.ShortDescription
o.InsertDate
}).TranslateTo<MyTestClass>()).ToList()
However once result is returned from the method, it still contains "ShortDescription" field in the Json. Am I doing something wrong?
JsConfig<T>.ExcludePropertyNames appears to be checked only once for each type, in a static constructor for TypeConfig<T>. Thus, if you are configuring ExcludePropertyNames in your service class, just before returning your response, it might be too late -- the TypeConfig properties may already be set up and cached for MyTestClass. I was able to reproduce this.
A more reliable alternative is to move all of your JsConfig<T> configuration to your AppHost setup code.
If you really do need to do this in your service class, e.g. if you are only conditionally excluding property names, then an alternative approach would be to ensure that JsConfig.IncludeNullValues is false (I believe it is by default) and in your service code set ShortDescription to null when appropriate.
Is there a way we can use ObjectContext with DbContext's ModelBuilder? We don't want to use POCO because we have customized property code that does not modify entire object in update, but only update modified properties. Also we have lots of serialisation and auditing code that uses EntityObject.
Since poco does create a proxy with EntityObject, we want our classes to be derived from EntityObject. We don't want proxy. We also heavily use CreateSourceQuery. The only problem is EDMX file and its big connection string syntax web.config.
Is there any way I can get rid of EDMX file? It will be useful as we can dynamically compile new class based on reverse engineering database.
I would also like to use DbContext with EntityObject instead of poco.
Internal Logic
Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
Most of times we need to only check for Any condition on Navigation Property for example
User.EmailAddresses.CreateSourceQuery()
.Any( x=> x.EmailAddress == givenAddress);
Access Property Attributes, such as XmlIgnore etc, we rely heavily on attributes defined on the properties.
A proxy for a POCO is a dynamically created class which derives from (inherits) a POCO. It adds functionality previously found in EntityObject, namely lazy loading and change tracking, as long as a POCO meets requirements. A POCO or its proxy does not contain an EntityObject as the question suggests, but rather a proxy contains functionality of EntityObject. You cannot (AFAIK) use ModelBuilder with EntityObject derivatives and you cannot get to an underlying EntityObject from a POCO or a proxy, since there isn't one as such.
I don't know what features of ObjectContext does your existing serialisation and auditing code use, but you can get to ObjectContext from a DbContext by casting a DbContext to a IObjectContextAdapter and accessing IObjectContextAdapter.ObjectContext property.
EDIT:
1. Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
You can achieve this with POCOs by using DbContext.ChangeTracker. First you call DbContext.ChangeTracker.DetectChanges to detect the changes (if you use proxies this is not needed, but can't hurt) and then you use DbCotnext.Entries.Where(e => e.State != EntityState.Unchanged && e.State != EntityState.Detached) to get DbEntityEntry list of changed entities for auditing. Each DbEntityEntry has OriginalValues and CurrentValues and the actual Entity is in property Entity.
You also have access to ObjectStateEntry, see below.
2. Most of times we need to only check for Any condition on Navigation Property for example:
User.EmailAddresses.CreateSourceQuery().Any( x=> x.EmailAddress == givenAddress);
You can use CreateSourceQuery() with DbContext by utilizing IObjectContextAdapter as described previously. When you have ObjectContext you can get to the source query for a related end like this:
public static class DbContextUtils
{
public static ObjectQuery<TMember> CreateSourceQuery<TEntity, TMember>(this IObjectContextAdapter adapter, TEntity entity, Expression<Func<TEntity, ICollection<TMember>>> memberSelector) where TMember : class
{
var objectStateManager = adapter.ObjectContext.ObjectStateManager;
var objectStateEntry = objectStateManager.GetObjectStateEntry(entity);
var relationshipManager = objectStateManager.GetRelationshipManager(entity);
var entityType = (EntityType)objectStateEntry.EntitySet.ElementType;
var navigationProperty = entityType.NavigationProperties[(memberSelector.Body as MemberExpression).Member.Name];
var relatedEnd = relationshipManager.GetRelatedEnd(navigationProperty.RelationshipType.FullName, navigationProperty.ToEndMember.Name);
return ((EntityCollection<TMember>)relatedEnd).CreateSourceQuery();
}
}
This method uses no dynamic code and is strongly typed since it uses expressions. You use it like this:
myDbContext.CreateSourceQuery(invoice, i => i.details);
After spending a year working with the Microsoft.Xrm.Sdk namespace, I just discovered yesterday the Entity.FormattedValues property contains the text value for Entity specific (ie Local) Option Set texts.
The reason I didn't discover it before, is there is no early bound method of getting the value. i.e. entity.new_myOptionSet is of type OptionSetValue which only contains the int value. You have to call entity.FormattedValues["new_myoptionset"] to get the string text value of the OptionSetValue.
Therefore, I'd like to get the crmsrvcutil to auto-generate a text property for local option sets. i.e. Along with Entity.new_myOptionSet being generated as it currently does, Entity.new_myOptionSetText would be generated as well.
I've looked into the Microsoft.Crm.Services.Utility.ICodeGenerationService, but that looks like it is mostly for specifying what CodeGenerationType something should be...
Is there a way supported way using CrmServiceUtil to add these properties, or am I better off writing a custom app that I can run that can generate these properties as a partial class to the auto-generated ones?
Edit - Example of the code that I would like to be generated
Currently, whenever I need to access the text value of a OptionSetValue, I use this code:
var textValue = OptionSetCache.GetText(service, entity, e => e.New_MyOptionSet);
The option set cache will use the entity.LogicalName, and the property expression to determine the name of the option set that I'm asking for. It will then query the SDK using the RetrieveAttriubteRequest, to get a list of the option set int and text values, which it then caches so it doesn't have to hit CRM again. It then looks up the int value of the New_MyOptionSet of the entity and cross references it with the cached list, to get the text value of the OptionSet.
Instead of doing all of that, I can just do this (assuming that the entity has been retrieved from the server, and not just populated client side):
var textValue = entity.FormattedValues["new_myoptionset"];
but the "new_myoptionset" is no longer early bound. I would like the early bound entity classes that gets generated to also generate an extra "Text" property for OptionSetValue properties that calls the above line, so my entity would have this added to it:
public string New_MyOptionSetText {
return this.GetFormattedAttributeValue("new_myoptionset"); // this is a protected method on the Entity class itself...
}
Could you utilize the CrmServiceUtil extension that will generate enums for your OptionSets and then add your new_myOptionSetText property to a partial class that compares the int value to the enums and returns the enum string
Again, I think specifically for this case, getting CrmSvcUtil.exe to generate the code you want is a great idea, but more generally, you can access the property name via reflection using an approach similar to the accepted answer # workarounds for nameof() operator in C#: typesafe databinding.
var textValue = entity.FormattedValues["new_myoptionset"];
// becomes
var textValue = entity.FormattedValues
[
// renamed the class from Nameof to NameOf
NameOf(Xrm.MyEntity).Property(x => x.new_MyOptionSet).ToLower()
];
The latest version of the CRM Early Bound Generator includes a Fields struct that that contains the field names. This allows accessing the FormattedValues to be as simple as this:
var textValue = entity.FormattedValues[MyEntity.Fields.new_MyOptionSet];
You could create a new property via an interface for the CrmSvcUtil, but that's a lot of work for a fairly simple call, and I don't think it justifies creating additional properties.
I am working with the classes in the System.Windows.Documents namespace, trying to write some generic code that will conditionally set the value of certain dependency properties, depending on whether these properties exist on a given class.
For example, the following method assigns an arbitrary value to the Padding property of the passed FrameworkContentElement:
void SetElementPadding(FrameworkContentElement element)
{
element.SetValue(Block.PaddingProperty, new Thickness(155d));
}
However, not all concrete implementations of FrameworkContentElement have a Padding property (Paragraph does but Span does not) so I would expect the property assignment to succeed for types that implement this property and to be silently ignored for types that do not.
But it seems that the above property assignment succeeds for instances of all derivatives of FrameworkContentElement, regardless of whether they implement the Padding property. I make this assumption because I have always been able to read back the assigned value.
I assume there is some flaw in the way I am assigning property values. What should I do to ensure that a given dependency property assignment is ignored by classes that do not implement that property?
Many thanks for your advice.
Tim
All classes that derive from Block have the Padding property. You may use the following modification:
void SetElementPadding(FrameworkContentElement element)
{
var block = element as Block;
if (block == null) return;
block.Padding = new Thickness(155d);
}
Even without this modification everything would still work for you because all you want is for Padding to be ignored by classes that do not support it. This is exactly what would happen. The fact that you can read out the value of a Padding dependency property on an instance that does not support it is probably by design but you shouldn't care. Block and derivatives would honor the value and all others would ignore it.