AutoMapper IEnumerable mapping bug - c#

I have a strange thing happening with automapper.
Do someone have a clue why this code is returning a value for the InstitutionsImplantations field :
var result1 = new List<DataModel.Implantations>();
foreach (var c in collection)
{
DataModel.Implantations i = Mapper.Map<DataModel.Implantations>(c);
result1.Add(i);
}
var item1 = result1.Where(x => x.Nom == "Valdor").FirstOrDefault();
Console.WriteLine(item1.InstitutionsImplantations);
While this one (on the same collection), return null for InstitutionsImplantations :
var result2 = Mapper.Map<IEnumerable<DataModel.Implantations>>(collection);
var item2 = result2.Where(x => x.Nom == "Valdor").FirstOrDefault();
Console.WriteLine(item2.InstitutionsImplantations);
Facts :
The mapping done by autommaper on the IEnumerable is correct for the +/- first 300 items in the list, then after, some item collection have a "badly" mapped InstitutionsImplantations property.
InstitutionsImplantations property is an object "proxy" from a EF result.
do you have an idea why this is happening ?
Tx you !

Found the problem, using the automapper source code to see what was going on behind the scenes.
When automapper is processing the IEnumerable collection, its using a caching mechanism to avoid to have to remap an already mapped object that was present in the collection.
You would say, its fair, no reason to have the same object mapped to a different result.
But in my scenario, I forget to add that I was also using an AfterMap delegate at the mapping declaration.
In this AfterMap I was doing this (To avoid circular references problem) :
mappedItem.InstitutionsImplantations.Institution.InstitutionsImplantations = null;
This problem of this is that, I though that it was only impacting the result of the mapping (AfterMap delegate), and nothing else, but I was wrong it is also impacting the "Cache" being put in place !
This is not was I wanted, because in this case it did also impact all following mapping computing in the collection, using "Null" for the following mapped InstitutionsImplantations object, instead of brand new mapping.
My first solution was clean because the caching mechanism did not work between 2 "different" mapping.
And I didn't find an easy way to disable this collection caching mechanism inside automapper !

Related

C#: How to reference dynamic object properties in EF .Where() or Select() query?

I want to return a filtered list from an Entity Framework-Core DBContext, based on multiple query values POSTed to a .NET Core 2.2 Web API.
The API method maps [FromBody] to a QueryObj instance.
The QueryObj class has about 30 properties (mostly, but not all Strings). These are the "searachable" subset of the properties in the larger Entity (For example: "lastName", "serialNumber", "customerID", "purchaseDate", etc.).
Since the query options will likely change, and because it'd be clunky to write & maintain, I'd rather not hard-code ~30 comparisons like:
.Where(x => x.Prop1Name == query.Prop1Name && ... x.Prop30Name == query.Prop30Name);
But I can't figure out how to dynamically reference the properties inside a loop ...
Or if a loop is even required, or ideal. Maybe it's not? I don't have any idea what the "idiomatic" .NET Core approach to filtering is, so if I'm totally off track, any advice is welcome.
In JS I could maybe do something like:
return myDataSvc.filter(x => {
for (prop in query){
if(x[prop] == query[prop]){
return true;
}
}
}
(this is likely wrong, but you get the idea - it's pretty simple ... )
In C#, the bracket notation for referencing object properties doesn't seem to exist, or at least what I tried didn't work.
The only answer I've found is a mess of introspection like:
x.GetType().GetProperty(prop.ToString()).GetValue() ==
query.GetType().GetProperty(prop.ToString()).GetValue()
Any ideas for a better approach?

Updating values in a list that is within a list

I have a list of objects and each object has a property that is a list of another object. Within this logic I'm casting the first object from a base class to one of it's inheriting classes.
It's this final list I want to make my changes to.
I've tried a few things already and I can see stepping through that the logic is executing but the finished product is always unchanged.
I've tried using linq:
Object1BaseObjectList
.Cast<Object1InheritedClass>()
.ToList()
.ForEach(e =>
e.Object2List
.ToList()
.ForEach(p => p.boolValue = true));
And I've tried some more conventional code:
foreach (Object1InheritedClass object1 in Object1BaseObjectList)
{
foreach (Object2Class object2 in object1.Object2List)
{
object2.boolValue = true;
}
}
Like I said the end result for both of these is no change. Anyone have any ideas as to what is going wrong here?
Just answering for the sake of closing this out. The information I provided was not enough to answer the question. Turns out the issue was being caused by the second list being declared as an IEnumerable. Changing it to a list corrected it. I'm assuming this was because IEnumerable wasn't creating a deep copy.

App Settings using Castle Dictionary Adapter - adding behavior at runtime

I'm using the dictionary adapter as described in this blog post:
http://kozmic.net/2013/11/21/on-strongly-typed-application-settings-with-castle-dictionaryadapter/
for getting app setting dependencies.
I have 2 attributes defined:
AppSettingsFromConfigAttribute - for holding a keyprefix
AppSettingsBehavior : KeyPrefixAttribute, IDictionaryPropertyGetter, IPropertyDescriptorInitializer
which is a carbon copy of the AppSettingsAttribute attribute class in the blog post.
This is the registration:
Configure(component => component.UsingFactoryMethod(
() =>
{
var attrib = (AppSettingsFromConfigAttribute)Attribute.GetCustomAttribute(component.Implementation, typeof(AppSettingsFromConfigAttribute));
var prop = new PropertyDescriptor();
prop.AddBehavior(new AppSettingsBehavior(attrib.KeyPrefix));
return configFactory.GetAdapter(component.Implementation, new NameValueCollectionAdapter(ConfigurationManager.AppSettings), prop);
})));
So i use my custom attribute to avoid dependencies to Castle.Core throughout my codebase, but try and add the same behavior at runtime through the registration. This is working, the keyprefix part - but not the fetch part. This fails only on first use, not on construction.
If i use the AppSettingsBehavior statically on the interface, it works correctly, fetches and fails on construction. So where am i going wrong in adding behavior to the dictionary adapter?
After a few hours of looking at the source, scratching my head and a coffee. Found a solution :)
Basically in the addbehavior call i get to add dictionary behaviors, while what i need is an interface / property behavior to trigger (pre)fetch. In the source, the guys are checking for attributes on the supplied type all over the place, despite what the method signatures may say - but only taking dictionary initializers from the prop descriptor object, nothing for the interface / properties. Hence, even though the behavior i was adding had the interface behavior - it never got read, only the dictionary behaviors.
So, i use a different call. Instead of calling factory.GetAdapter - i instead get factory.GetAdapterMeta() - which gives me a meta object with a nice Properties getter - which has the collection for the actual interface properties.
So the code becomes:
Configure(component => component.UsingFactoryMethod(
() =>
{
var attrib = (AppSettingsFromConfigAttribute)Attribute.GetCustomAttribute(component.Implementation, typeof(AppSettingsFromConfigAttribute));
var prop = new PropertyDescriptor();
prop.AddBehavior(new AppSettingsBehavior(attrib.KeyPrefix));
var meta = configFactory.GetAdapterMeta(component.Implementation);
foreach (var entry in meta.Properties)
{
entry.Value.Fetch = true;
}
return meta.CreateInstance(new NameValueCollectionAdapter(ConfigurationManager.AppSettings), prop);
})));

AutoMapper - mapping properties from one EF4 entity to another in a loop without Before/AfterMap

It appears that AutoMapper's methods BeforeMap and AfterMap have a critical bug, which if one is attempting to iterate over a collection of the source object to populate a property of the destination object, those mapping methods execute more than once. See: Extra iterations in a foreach in an AutoMapper map
What I'm trying to do is a bit complicated, so please bear with me.
I have a EF4 many-to-many graph (Games-to-Platforms) I'm trying to build based on incoming form data. In order to build the graph, I take the raw integer ids that come from the form, and then grab the correct Platforms from my repository in order to add them to the Game's collection. You can see my attempt at doing this within BeforeMap in the link I provided above.
The problem is that I'm not sure how to proceed. I need to be able to grab a hold of the destination (Game) object in order to successfully Add the Platforms to the Game. Is something like this possible in ForMember? From what I've read, it doesn't look like a custom resolver would work for me, and I'm not sure how I'd implement a custom type converter given all the moving parts (two entities, repository).
Any ideas or suggestions?
I simply decided to make my own static mapper. Not an ideal, or even great solution, but it works. It can definitely be made more abstract, but I figure it's a band-aid until AutoMapper is fixed. My solution:
public static class GameMapper
{
public static Game Map(IGameRepository repo, AdminGameEditModel formData, Game newGame)
{
newGame.GameID = formData.GameID;
newGame.GameTitle = formData.GameTitle;
newGame.GenreID = formData.GenreID;
newGame.LastModified = DateTime.Now;
newGame.ReviewScore = (short)formData.ReviewScore;
newGame.ReviewText = formData.ReviewText;
newGame.Cons = String.Join("|", formData.Cons);
newGame.Pros = String.Join("|", formData.Pros);
newGame.Slug = formData.Slug;
if (newGame.Platforms != null && newGame.Platforms.Count > 0)
{
var oldPlats = newGame.Platforms.ToArray();
foreach (var oldPlat in oldPlats)
{
newGame.Platforms.Remove(oldPlat);
}
}
foreach (var platId in formData.PlatformIDs)
{
var plat = repo.GetPlatform(platId);
newGame.Platforms.Add(plat);
}
return newGame;
}
}
Unfortunately, I can't make the third parameter an out parameter due to my need to overwrite existing entity data during updating. Again, it's definitely not a pretty, or even good solution, but it does the job. I'm sure the OO gods will smite me at a later date.

Linq2SQL inherited types and OfType query

I have a setup where I used Linq2SQL inheritance. To make queries easier, I expose the derived types in the DataContext as well, like the following:
public IQueryable<Derived> Derivations
{
get { return Bases.OfType<Derived>(); } // filter list on type
}
Calling this works perfectly, and I can see the SQL being correctly generated. The backing type is DataQuery<T>.
The problem comes in when I assigning this IEnumerable to a datasource (either a control or a BindingSource).
From what I can see, the DataQuery object is queried for an IListSource. And it happily supplies this. Then it proceeds to make a BindingList, which fails as the type parameter of the 2 arguments supplied (IEnumerable<Derived> and Table<Base>) does not match. It raises an exception of MissingMethod as the constructor cannot be found.
The simple workaround is just to call ToList() on the IQueryable<Derived> before assigning to the datasource and then it works, but this is quite tiring.
Any suggestions to handle this without 'loosing' the IQueryable?
Thanks
leppie
UPDATE:
The bug has now been reported to MS. More details here. Thanks Marc!
Confirmed. Looks like a bug to me; you should log it on Connect. The team are fixing LINQ-to-SQL bugs, so it might not be ignored. For now, use .ToList() etc.
Sample code:
using (var ctx = new MyDataContext())
{
var qry = ctx.BaseEntities.OfType<DerivedEntity>();
IListSource ls = (IListSource)qry;
IList list = ls.GetList(); // boom
/* Constructor on type
'System.Data.Linq.Provider.DataBindingList`1[snip]'
not found.*/
}
I had the same issue (still not fixed MS guys!).
To keep the IQueryable I did a .Cast<object>() when assigning to the datasource (i use it to output a xls file from any L2S table i want in a DynamicData website).

Categories