Modify destination object - c#

I have entity with nested collection and according model. Let's say:
class Entity
{
public IList<NestedEntity> Nested { get; set; }
}
and
class Model
{
public IList<NestedModel> Nested { get; set; }
}
and I need something like the following:
var existingEntity = service.GetEntity(id);
Mapper.Map<Model, Entity>(model, existingEntity);
// now existingEntity is an updated entity and we can save it
service.SaveEntity(existingEntity);
So while mapping nested collection mapper should remove the items which are not exist in existing entity, add which are newly created and just update the others.
How should I configure AutoMapper to reach such behavior?
I found that custom ValueResolvers ResolveCore method has no target class parameter so it can only create but not update collection.

Here is the solution for Model -> Entity mapping.
Mapper.CreateMap<NestedModel, NestedEntity>();
Mapper.CreateMap<Model, Entity>()
.ForMember(x => x.Nested, opt => opt.ResolveUsing<Resolver>());
public class Resolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
var targetCollection = ((Entity) source.Context.DestinationValue).Nested;
// TODO: Custom mapping here.
return source.New(targetCollection, typeof(NestedEntity[]));
}
}

Related

Automapper: Map property in list of objects

I have a list of DTOs and want to map this list to a list of entites. The entity itself has one property which comes from another source. Can I map this property to all items of the list with one map.
My classes:
Entity:
public class Account
{
public int Id {get;set;}
public string Name {get;set;}
public Guid ExternalId {get;set;}
}
DTO:
public class ExternalAccountDto
{
public int Id {get;set;}
public string Name {get;set;}
}
My Service:
public class AccountService
{
public async Task AddExternalAccounts(Guid externalId, List<ExternalAccountDto> accounts)
{
var entities = _mapper.Map(accounts);
// TODO: Map 'externalId' to all entities
// _mapper.Map(externalId, entities); // DOES NOT WORK!
_context.Create(entities);
}
}
Mapping
public class AccountProfile: Profile
{
public AccountProfile()
{
CreateMap<ExternalAccountDto, Account>();
// TODO: CreateMap for Guid on every Account
}
}
Can anyone give me some advice!
You should use the AfterMap function to do some postprocessing on the mapped items.
There are two ways to go about this. One is using something statically defined in the mapping profile. But in your case, you have something that's dynamic at runtime, like the ExternalId. Doing the aftermap in your AccountService then makes perfect sense.
I've found these kind of constructions very useful, especially when I want to consult other injected services for additional information.
public void AddExternalAccounts(Guid externalId, List<ExternalAccountDto> accounts)
{
var entities = _mapper.Map<List<ExternalAccountDto>, List<Account>>(accounts,
options => options.AfterMap((source, destination) =>
{
destination.ForEach(account => account.ExternalId = externalId);
}));
}
Two more cents regarding the AccountProfile class:
You can check upon creation of the mapping profile if the mapping profile is correct. This will save you a headache running into this problem later at runtime. You'll know immediately that there is a problem with the configuration.
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MappingProfile>();
cfg.AllowNullDestinationValues = false;
});
// Check that there are no issues with this configuration, which we'll encounter eventually at runtime.
config.AssertConfigurationIsValid();
_mapper = config.CreateMapper();
This also notified me that an .Ignore() on the ExternalId member of the Account class was required:
CreateMap<ExternalAccountDto, Account>().ForMember(d => d.ExternalId, a => a.Ignore());

Programmatically obtain metadata about indexes and constraints from Entity Framework 6's object context

I'm working with unique indexes in a code-first environment. They are defined like this:
modelBuilder.Entity<Foo>().HasIndex(foo => foo.Bar).IsUnique()
Using Entity Framework's metadata model, I would like to programmatically get a list of indexes (or constraints) given a specified type (Foo in this case).
Doing similar things with EF have proven to be relatively easy. For example, you can get a list of property names that are the primary key of a specified set, like so:
Entity Framework code first. Find primary key. Entity Framework also convieniently exposes navigation properties through similar methods: EF5 How to get list of navigation properties for a domain object.
Is there something similar available for (unique) indexes?
EDIT: Example DbContext:
public class Foo
{
public int Id { get; set; }
[Index]
public int SomeNumber { get; set; }
}
public class FooContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public FooContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Using this DbContext, #Kahbazi's method of obtaining the PropertyIndexes doesn't work, it will be an empty collection.
When I use the fluent API instead of an explicit DbSet, with the following code:
modelBuilder.Entity<Foo>().HasIndex(foo => foo.SomeNumber);
... then "Configuration" (see #Kahbazi's answer) will disappear from the MetadataProperties, resulting in System.InvalidOperationException: 'Sequence contains no matching element'
You can use this code to get the indexes
Context context = new Context();
IObjectContextAdapter objectContextAdapter = (IObjectContextAdapter)context;
object configuration = objectContextAdapter.ObjectContext.CreateObjectSet<Foo>()
.EntitySet
.MetadataProperties
.Where(x => x.Name == "Configuration")
.First()
.Value;
var propertyIndexes = configuration.GetType().GetProperty("PropertyIndexes", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(configuration);
var list = (System.Collections.IEnumerable)propertyIndexes;
foreach (var item in list)
{
Console.WriteLine(item);
}
But since most of the classes are internal, you have to use Reflection

Mapping viewModel object to ICollection entity

I have a basic table with a few FK references. So when I retrieve an entity for an update operation; that entity contains ICollections of related entites. My main viewModel contains Lists which correspond to these ICollections. However, since some other models represent 1-1 mapping, I have object instead of List. But inside the Entity they continue to be represented as ICollections.
This is giving me some problems when trying to map between viewModel and Entity. I am using Automapper for the mapping. I have
mapper.Map(viewModel, entity);
Currently I am leaving out the problematic models from this mapping and adding them separately. Is there a way to handle everything in one mapping? Is there a way to deal with the ICollections which ideally should be a single object?
EDIT
public class MainViewModel
{
public EntityVM1 vm1 { get; set; }
public List<EntityVM2> vm2 { get; set; }
public List<EntityVM3> vm3 { get; set; }
}
public class MainEntity
{
... some scalar props...
public virtual ICollection<Entity1> e1 { get; set; }
public virtual ICollection<Entity2> e2 { get; set; }
public virtual ICollection<Entity3> e3 { get; set; }
}
Entity1 and EntityVM1 are causing the problem.
Thanks
You can always override the default mapping system in the mapping config of AutoMapper, you should have a peek at the runtime polymorphism in the mapping inheritance section of the documentation.
If what you want on the entity is a straight object, why not take Automapper out of the equation and just force EF to map it using a one to one system... i.e
modelBuilder.Entity<MainEntity>()
.HasOne(p => p.Entity1)
.WithOne(i => i.MainEntity)
.HasForeignKey<Entity1>(b => b.MainEntityForignKey);
HAve a peek at the EF docs, under section one-to-one for more info

Shadow Properties to access related entity with Table-Per-Hierarchy inheritance model

I'm building a menu system for a website in ASP.Net Core. Let's assume I have a couple of database tables, one for Pages and one for Articles, although it only really matters that they are different entities. Each of them have a Name and Permalink property.
In my menu, which I want to also store in the database, I want to refer to the Name and Permalink of each entity. I have devised a simple menu class/model structure as follows:
Abstract MenuItem
public abstract class MenuItem
{
[Key]
public int MenuItemId { get; set; }
public int MenuPosition { get; set; }
public abstract string Name { get; }
public abstract string Permalink { get; }
}
Concrete ArticleMenuItem
public class ArticleMenuItem : MenuItem
{
public ArticleMenuItem() {}
public ArticleMenuItem(Article article)
{
Article = article;
}
public string Name => Article.Name;
public string Permalink => Article.Permalink;
[Required]
public int ArticleId { get; set; }
[ForeignKey("ArticleId")]
public Article Article { get; set; }
}
Concrete PageMenuItem
public class PageMenuItem : MenuItem
{
public PageMenuItem() {}
public PageMenuItem(Page page)
{
Page = page;
}
public string Name => Page.Name;
public string Permalink => Page.Permalink;
[Required]
public int PageId { get; set; }
[ForeignKey("PageId")]
public Page Page{ get; set; }
}
I then override onModelCreating(ModelBuilder modelBuilder) for the relevant DbContext as I don't want to make the individual DbSet<T>'s available:
modelBuilder.Entity<PageMenuItem>();
modelBuilder.Entity<ArticleMenuItem>();
As well as add the relevant DbSet<T> for the menu:
public virtual DbSet<MenuItem> MenuItems { get; set; }
Add a couple of sample records to the database when the app loads (assume I've got some articles and pages initialised too):
List<MenuItem> items = new List<MenuItem>()
{
new PageMenuItem(pages[0]) { MenuPosition = 1 },
new ArticleMenuItem(articles[0]) { MenuPosition = 2 }
};
items.ForEach(item => context.MenuItems.Add(item));
A simple repository method to get the menu items from the database:
public IEnumerable<MenuItem> GetAllMenuItems() => _context.MenuItems;
With all this in place I was hoping that I could get the Name and Permalink for each item as follows (in a view, for instance):
#foreach (MenuItem item in Model)
{
#item.Name
}
Sadly, this results in a null object exception, and then I remembered EF Core doesn't support lazy loading. So I want to eagerly load the shadow properties, specifically the related entities, when I get the menu items in the repository.
There are two approaches to accessing shadow properties. The first approach I took updating my repository method looked like this:
public IEnumerable<MenuItem> GetAllMenuItems() => _context.MenuItems
.Include(item => context.Entry(item).Property("Page").CurrentValue)
.Include(item => context.Entry(item).Property("Article").CurrentValue)
This results in:
InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression1' to type 'System.Linq.Expressions.MemberExpression'.
Casting to (Page) and (Aticle) respectively results in:
InvalidOperationException: The property expression 'item => Convert(value(InfoSecipediaWeb.Infrastructure.EntityFramework.ApplicationDbContext).Entry(item).Property("Page").CurrentValue)' is not valid. The expression should represent a property access: 't => t.MyProperty'.
The second method for accessing shadow properties only seems to enable accessing a single property value:
public static TProperty Property<TProperty>([NotNullAttribute] object entity, [NotNullAttribute][NotParameterized] string propertyName);
However, giving it a try:
public IEnumerable<MenuItem> GetAllMenuItems() => _context.MenuItems
.Include(item => EF.Property<Page>(item, "Page"))
.Include(item => EF.Property<Article>(item, "Article"));
Results in:
InvalidOperationException: The property expression 'item => Property(item, "Page")' is not valid. The expression should represent a property access: 't => t.MyProperty'.
I'd like to know whether it is possible to use shadow properties for navigation with an inheritance model? If so, how do I include the related entities so that it is accessible in my concrete MenuItem classes? e.g. for public string Name => Page.Name.
Unfortunately currently there is no syntax for eager loading derived class properties (note that they are different from shadow properties). This along with the lack of lazy loading leaves the explicit loading to be the only option. See for instance ef-core load collection property of nested tph inherited member how you can use it for a single item, for collection of items I'm afraid you have to materialize the result into a list, then using explicit loading of the concrete types and rely on EF navigation property fix up.
For your example it could be something like this:
public IEnumerable<MenuItem> GetAllMenuItems()
{
var menuItems = _context.MenuItems.ToList();
_context.MenuItems.OfType<ArticleMenuItem>().Include(e => e.Article).Load();
_context.MenuItems.OfType<PageMenuItem>().Include(e => e.Page).Load();
return menuItems;
}
Another workaround (did I say only one) is to use manual union query, which basically kills the TPH idea:
public IEnumerable<MenuItem> GetAllMenuItems() =>
_context.MenuItems.OfType<ArticleMenuItem>().Include(e => e.Article)
.AsEnumerable() // to avoid runtime exception (EF Core bug)
.Concat<MenuItem>(
_context.MenuItems.OfType<PageMenuItem>().Include(e => e.Page));

EFCore with NewtonSoft Json serialization from scaffolded database : ignore not loaded navigation properties

I am creating an ASP.NET Core application and I have scaffolded my database to an EFCore DbContext.
All navigation properties one-to-many are defined as public virtual ICollection<Type> NavProp { get; set; }, and the scaffolder add some initialization in the entity constructor as NavProp = new HashSet<Type>().
I have a data controller that returns data from the DbContext : return this.DbContext.MyData.First();
How can I avoid the Json serializer to serialize the navigation properties that I have not explicitely included in my query ? This will avoid loading unneeded data, and also reference looping.
If you do not want to transfer them then you would not need to include them in your query...
Query only the columns you need and store them in DTOs (Data Transfer Objects) like:
return this.DbContext.MyData.First().Select(x => new MyDataDto { Id = x.Id, Name = x.Name });
and a DTO class like:
public class MyDto {
// All the properties you want to tranfer
public int Id { get; set }
public string Name { get; set; }
// your props...
}
With this approach you have control over what is transferred.

Categories