Deep-copy with AutoMapper - c#

I want to create a deep copy of an object. I chose to use the automapper because this way I dont have to edit classes and add there any extra code. Plus I use automapper for mapping my classes to DTOs.
On my surprise when I wanted to do a copy like:
var original = new TrainingSetDto()
var output = _mapper.Map<TrainingSetDto>(original);
the output variable retrieved reference to the original variable (shallow copy).
How to achieve my wanted result (deep copy of the original) to get new instance with same properties ?
E.g. on this blog
https://jshowers.com/create-deep-copies-of-object-in-c-using-automapper/
was mentioned that static call Mapper.Map<Person>(originalPerson); is the way how to do so. But these static methods are not there anymore.
Could you please point me to the right direction ?

As #LucianBargoanu pointed out it is possible to do a deep copy by AutoMapper.
By firstly specifying map for your object e.g.
CreateMap<TrainingSetDto, TrainingSetDto>();
and then just calling your mapping function
var copy = _mapper.Map<TrainingSetDto>(original);
I was able to reproduce the correct behavior in the Console app but in the API project it still didnt work correctly.
Finally the problem in the API project was in the dependency injection package: AutoMapper.Extensions.Microsoft.DependencyInjection. And thats why when I replaced:
services.AddAutoMapper(provider => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
cfg.AddMaps(Assembly.GetExecutingAssembly().GetReferencedAssemblies().Select(x => x.FullName));
}));
by:
services.AddSingleton(provider => new MapperConfiguration(cfg => {
cfg.AddProfile(new AutoMapperProfile());
cfg.AddMaps(Assembly.GetExecutingAssembly().GetReferencedAssemblies().Select(x => x.FullName));
}).CreateMapper());
it started to work correcly.
PS: I put my findings also to the discussion thread on AutoMapper github, but for some reason they deleted my comment and locked the thread.

Related

MongoDocument Object only gets partially populated when querying

I have a collection of users that I'm trying to read from the database, but for some reason some strange behavior takes place that I can't really figure out. Hopefully, somebody can suggest or help me find the root of this problem.
So basically, what happens is that whenever I call this code in my HomeController.cs:
var users = await _database.GetCollection<User>("ApplicationUsers").FindAsync(_ => true);
var userList = users.ToList();
it only populates userList partially, meaning only the ID and ConcurrencyStamp properties get filled, but the other properties always end up being null (as seen in: https://i.imgur.com/RTF8ljL.png)
But whenever I add this line right after the database connection initialization in the Startup.cs:
database.GetCollection<User>("ApplicationUsers");
Then suddenly userList does get populated with all the other information (as seen in https://i.imgur.com/f5IV7fh.png)
So in order for it to work, I have to get the collection right after the connection gets initialized which I'm not really fond of, because I don't have to do this for other collections. So my mongo connection code would have to look like this in the Startup.cs:
var mongoUrl = new MongoUrl(config.GetSection("DatabaseSettings:ConnectionString").Value);
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
mongoClientSettings.ClusterConfigurator = cb => ConfigureCluster(cb);
var client = new MongoClient(mongoClientSettings);
var database = client.GetDatabase(config.GetSection("DatabaseSettings:DatabaseName").Value);
database.GetCollection<User>("ApplicationUsers"); // TODO: This is needed just to let Mongo Driver know to which class to deserialize this collection
services.AddSingleton<IMongoDatabase>(database);
var pack = new ConventionPack()
{
new CamelCaseElementNameConvention(),
new IgnoreExtraElementsConvention(true),
new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)
};
ConventionRegistry.Register("DatabaseConventions", pack, t => true);
I'm guessing something happens between the execution of Startup.cs and HomeController.cs that causes the deserialization to mess up?
Update:
The same behavior seems to happen on a clean project, the only nuget packages I installed are the official mongodb driver and AspNetCore.Identity.Mongo by Matteo Fabbri. This strange deserialize behavior does not happen when I use getCollection for other classes. The problem lies with ApplicationUser which extends from MongoUser (a class made available by the AspNetCore.Identity.Mongo library)
Update 2:
Turns out that MongoUser class from the AspNetCore.Identity.Mongo library is allergic to the ConventionPack that was registered. I tested this by getting the collection before and after the registration of the database conventions. Now finding a proper solution for this.
Update 3:
Also I found out that documents are saved with properties named in Upper Camel Case, which could be the cause for mongodb driver's confusion. It seems that the conventions set to save them in CamelCase is being ignored for this particular class (ApplicationUser).
I managed to solve the problem. The issue was the order of code execution. The convention pack was registered after MongoIdentity and the database was initialized. During the initialization of MongoIdentity and the Database, the ApplicationUser was configured to not have the CamelCaseElementNameConvention in the pack, which led to the class being saved in UpperCamelCase and therefore cause confusion when retrieving the collection when the registered ConventionPack was active.
For whoever struggles with strange behavior where you may think your code should work, remember that the order of code execution is very important and could very well be the cause of the behavior. Good luck to y'all!

Disable AutoMapper built-in enum mapper

Is it possible to disable the built-in mapper for enums in AutoMapper, or replace it with one that always throws an exception?
I find the built in mapper to be highly unreliable as it will try it's best to map an input to any enum you give it which increases the risk of introducing, difficult to trace, bugs in your code.
I'd much rather have it fail with an exception telling me that I'm missing a mapper/converter than have it just work and then several steps down the call stack the code fails because the value isn't right in the current context.
Based on the comment from Lucian i added the following code to my configuration:
services.AddAutoMapper(config =>
{
var enumMapper = config.Mappers.Single(m => m is AutoMapper.Mappers.EnumToEnumMapper);
config.Mappers.Remove(enumMapper);
}, typeof(Startup));
This removes the default EnumToEnum mapper and gives me the exception when no mapping is configured.
From what you write I can think of a few options:
If you have an enum property on an object, you can ignore it
explicitly by using:
CreateMap<Foo, Bar>().ForMember(dest => dest.EnumProperty, opt => opt.Ignore());
If you create mappings for the properties you want to map and leave out the enum properties you can use:
CreateMap<Foo, Bar>().ForMember(...).ForAllOtherMembers(opt => opt.Ignore())
If you want to replace the mapping between to enum types you can overwrite it with:
Mapper.CreateMap<EnumSrc,EnumDst>().ConvertUsing(value => {
throw new Exception();
});

How to get Entity Framework DbContext name with reflection

Say we have
using (var lobo = new MyGymEntities())
{
}
I would like to get the DbContext name (in this case is MyGymEntities) with reflection, is this possible?
I would like to store the string value into a variable..
Edit:
I do not want to type MyGymEntities.GetType().Name/FullName to do this.
I'm asking if it is possible to somehow obtain this information from the assembly such as
Assembly.GetExecutingAssembly().GetName().Name; //Return Projectname
UPDATE
The following solved my problem, in case anyone
var a = Assembly.GetExecutingAssembly()
.DefinedTypes
.Where(x => x.BaseType?.Name == "DbContext").ToList();
Console.WriteLine(a.First().FullName);
Ok, guys, I'll explain why this question is not a duplicate and why I want to get the name of the DbContext.
I'm well aware that the name property of an object can be accessed by calling the GetType().Name on it, but this method can only be called by an object itself meaning I need to specify which objects name attribute I would like to retrieve.
Ex:
MyObject.GetType().Name;
However what I want to do is: to get the DbContextName(if there's any) that is on the project, solution.
why do I need this? because I have generic Forms that handle my queries
inside my Custom Forms currently, I have something like this
MyGymEntities context = new MyGymEntities();
now If I copy and paste the current solution folder to create a new project I'll have to rename the DbContext shown above to the name of the new DbContext for my solution.
however, If I use this to get the ContextName, than I'll create a new instance of the DbContext of this name and use it on my Custom Forms, therefore will need less configuration for each project I'll be creating...
var ContextName = Assembly.GetExecutingAssembly()
.DefinedTypes
.Where(x => x.BaseType?.Name == "DbContext").ToList().First().Name;
Thanks!
BaseType only returns the immediate base type, and will break if you later introduce a custom base DbContext. You can get all the DbContext types in an assembly with something like:
var dbContextTypes = Assembly.GetExecutingAssembly()
.DefinedTypes.Where(t => typeof(DbContext).IsAssignableFrom(t))
.ToList();

Do I need to write all the properties explicitly when I am using AutoMapper, if these properties have the same name?

I have been trying to get this going, but when i debug my test, the objects return null. I want to do complex object to object mapping but i cant get it to work.
Instead of:
cfg.CreateMap<Payments, Customer.Payments>()
.ForMember(to => to.SomeName, opts => opts.MapFrom(from => from.SomeName))
.ForMember(to => to.SomeDate, opts => opts.MapFrom(from => from.SomeDate));
We want to do:
cfg.CreateMap<Payments, Customer.Payments>();
I'd definitely checkout their Wiki if you haven't already.
Based on your comment above, it looks like you're confused about the signature of mapper.map.
This is what you could do:
var dest = mapper.Map<Dest>(new Source());
Checkout this simple fiddle for a working example based on the code you posted.

Is there a LINQ Query Provider for querying C# files?

Is there such a thing as a LINQ Query Provider for querying C# files?
I have a Winforms app that I use to assist me in generating code as well as to supplement Visual Studio's editing capabilities for existing code. One thing I would like to be able to do is to query a given class to see if a method exists. Or query for a list of classes, or query for classes with a particular type in the method signature, etc.
Further, I would love to be able to run the usual CRUDs on said C# files, but I realize that this may be out of scope for this question.
SOLVED!
Thanks to the folks who suggested Roslyn, and especially thanks to the code sample provided by Konrad Kokosa below, I was able to get exactly what I needed.
First thing you need to do is download the Roslyn DLLs (I used NuGet). Then query away. Here is another example for getting an alphabetized list of all methods in a class:
static List<string> GetMethodList(string filename, string className)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var #class = root.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault(md => md.Identifier.ValueText.Equals(className));
return (List<string>) #class.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList().OrderBy(m => m.Identifier.ValueText).Select(m => m.Identifier.ValueText);
}
I've found Roslyn very intuitive. Here is an example of parsing source file for a specified methodName within specified class className:
static void GetClassMethod(string filename, string className, string methodName)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var #class = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(className))
.FirstOrDefault();
var method = #class.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(methodName))
.FirstOrDefault();
}
From this example you can easily build querying all classes withing a file.
Microsoft is working on a project called Roslyn which allows you to interact with C# code via ordinary .NET objects:
http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been officially released, though, and I'm honestly not sure that it would include things like manipulating the source files (for example, to add a function to a class).
If you're working with code that is already compiled, you could probably use a combination of a few tricks, such as loading an assembly at runtime and then using reflection to interrogate it.
However, I would suggest taking a look at tools that are already available for generating code, like T4 Templates. There might be a better way to solve the underlying problem than interrogating C# source files.

Categories

Resources