I have an entity:
public class Tag {
public int Id { get; set; }
public string Word { get; set; }
// other properties...
// and a collection of blogposts:
public ICollection<Post> Posts { get; set; }
}
and a model:
public class TagModel {
public int Id { get; set; }
public string Word { get; set; }
// other properties...
// and a collection of blogposts:
public int PostsCount { get; set; }
}
and I query the entity like this (by EF or NH):
var tagsAnon = _context.Tags
.Select(t => new { Tag = t, PostsCount = t. Posts.Count() })
.ToList();
Now, how can I map the tagsAnon (as an anonymous object) to a collection of TagModel (e.g. ICollection<TagModel> or IEnumerable<TagModel>)? Is it possible?
Update 2019-07-31: CreateMissingTypeMaps is now deprecated in AutoMapper v8, and will be removed in v9.
Support for automatically created maps will be removed in version 9.0.
You will need to explicitly configure maps, manually or using
reflection. Also consider attribute mapping.
Update 2016-05-11: DynamicMap is now obsolete.
Now you need to create a mapper from a configuration that sets CreateMissingTypeMaps to true:
var tagsAnon = Tags
.Select(t => new { t.Id, t.Word, PostsCount = t.Posts.Count })
.ToList();
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
var tagsModel = tagsAnon.Select(mapper.Map<TagModel>)
.ToList();
Yes, it is possible. You would have to use the DynamicMap<T> method of the Automapper's Mapper class for each anonymous object you have. Something like this:
var tagsAnon = Tags
.Select(t => new { t.Id, t.Word, PostsCount = t.Posts.Count() })
.ToList();
var tagsModel = tagsAnon.Select(Mapper.DynamicMap<TagModel>)
.ToList();
I am not entirely sure if this is possible. Suggestions:
Why can't you just do this:
var tagsAnon = _context.Tags
.Select(t => new TagModel { Tag = t, PostsCount = t. Posts.Count() })
.ToList();
This SHOULD work, however it fails (I have read that DynamicMap is iffy on collections.
var destination = Mapper.DynamicMap<IEnumerable<TagModel>>(tagsAnon);
This proves that DynamicMap does work with anon types, just not seemingly with enumerables:
var destination = Mapper.DynamicMap<TagModel>(tagsAnon);
You can create a custom function to achieve this with latest Automapper. It uses the CreateMissingTypeMaps property as mentioned in other answers above.
public static List<T> MapDynamicList<T>(IEnumerable<object> obj)
{
var config = new MapperConfiguration(c => c.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
var newModel = obj.Select(mapper.Map<T>).ToList();
return newModel;
}
Then you just call the function with this single line of code:
var viewModel = Models.Helper.MapDynamicList<MatchSubCategory>(model);
where model is the IEnumerable<object> or List<object>.
Related
I want to map source inner classes to string and in other case string array but in both cases they are mapped to null. I want MapNoteFromInnerSourceEntity1 to hold a value of InnerSourceEntity1 Id property and MapValueFromInnerSourceEntity2 to hold values of InnerSourceEntity2 value properties. So far automapper is quite difficult for me to understand.
Code:
internal class Program
{
public class InnerSourceEntity1
{
public string Id { get; set; }
public string Note { get; set; }
}
public class InnerSourceEntity2
{
public string Id { get; set; }
public string Value { get; set; }
}
public class SourceEntity
{
public InnerSourceEntity1 A { get; set; }
public IList<InnerSourceEntity2> B { get; set; }
}
public class DestinationEntity
{
public string MapNoteFromInnerSourceEntity1 { get; set; }
public string[] MapValueFromInnerSourceEntity2 { get; set; }
}
static void Main()
{
var source = new SourceEntity
{
A = new InnerSourceEntity1 { Note = "Note", Id = "Id"},
B = new List<InnerSourceEntity2> { new InnerSourceEntity2 { Id = "Id", Value = "Value" }, new InnerSourceEntity2 { Id = "Id", Value = "Value" } }
};
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<SourceEntity, DestinationEntity>();
cfg.CreateMap<InnerSourceEntity1, string>().ConvertUsing(s => s.Note);
cfg.CreateMap<IList<InnerSourceEntity2>, string[]>();
});
var mapper = new Mapper(config);
DestinationEntity destination = mapper.Map<SourceEntity, DestinationEntity>(source);
Console.ReadLine();
}
}
Since the names of properties between source type SourceEntity and destination type DestinationEntity don't match, you'll have to explicitly indicate them, otherwise AutoMapper will not know how to fill the properties:
cfg.CreateMap<SourceEntity, DestinationEntity>()
.ForMember(
dst => dst.MapNoteFromInnerSourceEntity1,
opts => opts.MapFrom(src => src.A))
.ForMember(
dst => dst.MapValueFromInnerSourceEntity2,
opts => opts.MapFrom(src => src.B));
Also, don't map between concrete collection types:
cfg.CreateMap<IList<InnerSourceEntity2>, string[]>(); // <== Don't do that.
Instead, see what the docs say about mapping collections:
(...) it’s not necessary to explicitly configure list types, only their member types. ~ AutoMapper Docs
So, you only need to specify map like this:
cfg.CreateMap<InnerSourceEntity2, string>();
And since we are mapping to string we also need to instruct the AutoMapper from where it can get the string value. So, we'll use ConvertUsing() again:
cfg.CreateMap<InnerSourceEntity2, string>().ConvertUsing(s => s.Value);
Final configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceEntity, DestinationEntity>()
.ForMember(
dst => dst.MapNoteFromInnerSourceEntity1,
opts => opts.MapFrom(src => src.A))
.ForMember(
dst => dst.MapValueFromInnerSourceEntity2,
opts => opts.MapFrom(src => src.B));
cfg.CreateMap<InnerSourceEntity1, string>().ConvertUsing(s => s.Note);
cfg.CreateMap<InnerSourceEntity2, string>().ConvertUsing(s => s.Value);
});
When querying using Entity Framework Core, I am using expressions to convert to DTO objects, which works well for the object, and any child collections.
A simplified example:
Model:
public class Model
{
public int ModelId { get; set; }
public string ModelName { get; set; }
public virtual ICollection<ChildModel> ChildModels { get; set; }
// Other properties, collections, etc.
public static Expression<Func<Model, ModelDto>> AsDto =>
model => new ModelDto
{
ModelId = model.ModelId,
ModelName = model.ModelName,
ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList()
};
}
Query:
dbContext.Models.Where(m => SomeCriteria).Select(Model.AsDto).ToList();
My question is about trying to find a way to do something similar for a child that is not a collection. If I have added to my model:
public AnotherChildModel AnotherChildModel { get; set; }
I can add a conversion in the expression:
public static Expression<Func<Model, ModelDto>> AsDto =>
model => new ModelDto
{
ModelId = model.ModelId,
ModelName = model.ModelName,
ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
AnotherChildModel = new AnotherChildModelDto
{
AnotherChildModelId = model.AnotherChildModelId
}
};
But, I have not found a good way to avoid repeating this code every time that I need to convert the second child model to a DTO object. The expressions work for the main object and any child collections, but not for single entities. Is there a way to add the equivalent of a .Select() for a single entity?
There are several libraries which allows to do that in intuitive way:
LINQKit
[Expandable(nameof(AsDtoImpl))]
public static ModelDto AsDto(Model model)
{
_asDtoImpl ??= AsDtoImpl() .Compile();
return _asDtoImpl(model);
}
private static Func<Model, ModelDto> _asDtoImpl;
private static Expression<Func<Model, ModelDto>> AsDtoImpl =>
model => new ModelDto
{
ModelId = model.ModelId,
ModelName = model.ModelName,
ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
AnotherChildModel = new AnotherChildModelDto
{
AnotherChildModelId = model.AnotherChildModelId
}
};
dbContext.Models
.Where(m => SomeCriteria).Select(m => Model.AsDto(m))
.AsExpandable()
.ToList();
UPDATE: For EF Core, LINQKit can be confugred globally and AsExpanding() can be omitted.
builder
.UseSqlServer(connectionString)
.WithExpressionExpanding(); // enabling LINQKit extension
NeinLinq - almost the same as in LINQKit
[InjectLambda]
public static ModelDto AsDto(Model model)
{
_asDto ??= AsDto() .Compile();
return _asDto(model);
}
private static Func<Model, ModelDto> _asDto;
private static Expression<Func<Model, ModelDto>> AsDto =>
model => new ModelDto
{
ModelId = model.ModelId,
ModelName = model.ModelName,
ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
AnotherChildModel = new AnotherChildModelDto
{
AnotherChildModelId = model.AnotherChildModelId
}
};
dbContext.Models
.Where(m => SomeCriteria).Select(m => Model.AsDto(m))
.ToInjectable()
.ToList();
UPDATE: For EF Core, NenLinq can be confugred globally and ToInjectable() can be omitted.
builder
.UseSqlServer(connectionString)
.WithLambdaInjection(); // enabling NeinLinq extension
DelegateDecompiler - less verbose than others
[Compute]
public static ModelDto AsDto(Model model)
=> new ModelDto
{
ModelId = model.ModelId,
ModelName = model.ModelName,
ChildModels = model.ChildModels.AsQueryable().Select(ChildModel.AsDto).ToList(),
AnotherChildModel = new AnotherChildModelDto
{
AnotherChildModelId = model.AnotherChildModelId
}
}
dbContext.Models
.Where(m => SomeCriteria).Select(m => Model.AsDto(m))
.Decompile()
.ToList();
All libraries do the same thing - correct expression tree before EF Core processing. All of them need additional call to inject it's own IQueryProvider.
I can include only related entities.
using (var context = new BloggingContext())
{
// Load all blogs, all related posts
var blogs1 = context.Blogs
.Include(b => b.Posts)
.ToList();
}
However, I don't need entire BlogPost entity. I'm interested only in particular properties, e.g:
using (var context = new BloggingContext())
{
// Load all blogs, all and titles of related posts
var blogs2 = context.Blogs
.Include(b => b.Posts.Select(p => p.Title) //throws runtime exeption
.ToList();
foreach(var blogPost in blogs2.SelectMany(b => b.Posts))
{
Console.Writeline(blogPost.Blog.Id); //I need the object graph
Console.WriteLine(blogPost.Title); //writes title
Console.WriteLine(blogPost.Content); //writes null
}
}
You either use Include which loads the entire entity, or you project what you need to a .Select:
var blogs2 = context.Blogs
.Select(x => new
{
BlogName = x.BlogName, //whatever
PostTitles = x.Post.Select(y => y.Title).ToArray()
})
.ToList();
Or, you could do something like this:
var blogs2 = context.Blogs
.Select(x => new
{
Blog = x,
PostTitles = x.Post.Select(y => y.Title).ToArray()
})
.ToList();
A Select is always better when you don't need the entire child, as it prevents querying unneeded data.
In fact what you want is: split an entity in a common, representational part and a special part that you don't always want to pull from the database. This is not an uncommon requirement. Think of products and images, files and their content, or employees with public and private data.
Entity framework core supports two ways to achieve this: owned type and table splitting.
Owned type
An owned type is a type that's wrapped in another type. It can only be accessed through its owner. This is what it looks like:
public class Post
{
public int ID { get; set; }
public Blog Blog { get; set; }
public string Title { get; set; }
public PostContent Content { get; set; }
}
public class PostContent
{
public string Content { get; set; }
}
And the owned-type mapping:
modelBuilder.Entity<Post>().OwnsOne(e => e.Content);
Where Blog is
public class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
}
However, as per the docs:
When querying the owner the owned types will be included by default.
Which means that a statement like...
var posts = context.Posts.ToList();
...will always get you posts and their contents. Therefore, owned type is probably not the right approach for you. I still mentioned it, because I found out that when Posts are Included...
var blogs = context.Blogs.Include(b => b.Posts).ToList();
...the owned types, PostContents, are not included (DISCLAIMER: I'm not sure if this is a bug or a feature...). In this case, when the owned types should be included a ThenInclude is required:
var blogs = context.Blogs.Include(b => b.Posts)
.ThenInclude(p => p.Content).ToList();
So if Posts will always be queried through Blogs, owned type may be appropriate.
I don't think this applies here, but it does when children having owned types have an identifying relationship with their parents (classical example: Order-OrderLine).
Table splitting
With table splitting a database table is split up into two or more entities. Or, from the objects side: two or more entities are mapped to one table. The model is almost identical. The only difference is that PostContent now has a required primary key property (ID, of course having the same value as Post.ID):
public class Post
{
public int ID { get; set; }
public Blog Blog { get; set; }
public string Title { get; set; }
public PostContent Content { get; set; }
}
public class PostContent
{
public int ID { get; set; }
public string Content { get; set; }
}
And the table-splitting mapping:
modelBuilder.Entity<Post>()
.HasOne(e => e.Content).WithOne()
// or .WithOne(c => c.Post) if there is a back reference
.HasForeignKey<PostContent>(e => e.ID);
modelBuilder.Entity<Post>().ToTable("Posts");
modelBuilder.Entity<PostContent>().ToTable("Posts");
Now Posts will always be queried without their contents by default. PostContent should always be Include()-ed explicitly.
Also, PostContent can now be queried without its owner Post:
var postContents = context.Set<PostContent>().ToList();
I think this is exactly what you're looking for.
Of course you can do without these mappings if you'll always use projections when you want to fetch posts without contents.
You can try this :
using (var context = new BloggingContext())
{
var blogProps = context.Blogs
.SelectMany(b =>
b.Posts.Select(p =>
new { Blog = b, PostTitle = p.Title }
)
)
.ToList();
}
EDIT
If you want to stick to your data model, you could try something like this :
using (var context = new BloggingContext())
{
var blogProps = context.Blogs
.Select(b =>
new Blog
{
Name = b.Name,
Posts = new List<Post>(b.Posts.Select(p =>
new Post
{
Title = p.Title
})
}
)
.ToList();
}
I think there's a much easier way to do this. Projection is nice and all, but what if you want all the columns from your parent entity and most of them from the child? When those types have a lot of properties, using projection means you have a lot of lines of code to write just to select everything you want except the few that you don't. Well, since using projection means your entities won't be tracked, it's much easier to use .AsNoTracking() and then just empty out the things you don't want.
var foos = await _context.DbSet<Foo>()
.AsQueryable()
.Where(x => x.Id == id)
.Include(x => x.Bars)
.AsNoTracking()
.ToListAsync();
foreach (var foo in foos)
{
foreach (Bar bar in foo.Bars)
{
bar.Baz = null;
}
}
i would like to make a treelistview for my Data.
Tree should look like this
Accounts
-> Providers
-> Accounts
public sealed class AccountRoot
{
public AccountRoot()
{
Providers = new Collection<Hoster>();
}
public long AccountRootId { get; set; }
public ICollection<Hoster> Providers { get; set; }
}
public sealed class Hoster
{
public Hoster()
{
Accounts = new Collection<Account>();
}
[Key]
public long HosterId { get; set; }
public long AccountRootId { get; set; }
public string Name { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public sealed class Account
{
[Key]
public long AccountId { get; set; }
public long HosterId { get; set; }
public Hoster Hoster { get; set; }
public string Name { get; set; }
}
I would like to order my query.
should be sth like
Accounts
Providers A-Z
Accounts A-Z
what i got until now is..
var query = _entity.AccountRoot.Local
.Select(x => new AccountRoot()
{
AccountRootId = x.AccountRootId,
Providers = x.Providers.OrderBy(y => y.Name).ToList()
}).ToList();
What is missing is the orderby for the next nested collection.
Thank you for your help ! :-)
It can be a bit different approaches depending on if you already have a result set, and want to just sort it in code, or if you want to construct IQueryable<> for EF which will be successfully compiled to SQL and executed with actual sorting in database.
First, assume you already have the collection in code. In this case, you have object AccountRoot, which contains collection of Providers, each of which has collection of Accounts. Obviously, you cannot return the same objects, as you need to reorder collection properties, so all you need is to just construct new ones. I would just sort the collections, but you could construct completely new entities, if you need:
var query = ...
.Select(x => new AccountRoot
{
// add copy properties here
// ....
Providers = x.Providers
.Select(y =>
{
// Here we can construct completely new entity,
// with copying all properties one by one,
// or just reorder existing collection as I do here
var result = y;
result.Accounts = y.Accounts.OrderBy(z => z.Name).ToArray();
return result;
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
Second case, if you need to get it directly from SQL, is a bit different, as you cannot use all that var result = ...; ... return result stuff in lambda - it won't compile to SQL. But idea is the same - you need to construct projection from data sets. It should be something like this:
var query = ...
.Select(x => new AccountRoot
{
AccountRootId = x.AccountRootId,
// Other properties to copy
// ...
Providers = x.Providers
.Select(y => new Hoster
{
HosterId = y.HosterId,
// Other properties to copy
// ...
Accounts = y.Accounts.OrderBy(z => z.Name).ToArray(),
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
I am using AutoMapper in a WCF service to return User objects. User has properties such as AccountTeams which itself has child objects. All of the classes have AutoMapper maps.
Depending on the WCF OperationContract that is called, I want to return different amounts of data. I want one OperationContract to return the User object without its AccountTeams property (and their children) populated and another OperationContract to return the User with the whole chain of properties filled out.
Is there a way to have two different maps between the same two objects or do I need to perform the full mapping and null out the properties I don't want to return from the service?
Kevin Kalitowski raised a good point about wal's answer: If we need two configurations to deal with a mapping that needs to be different, then don't we have to duplicate all the other mappings that are common?
I think I've found a way around this using profiles: Have one profile for each of the unique mappings, and a third profile for the common mappings. Then create two configurations, one for each unique profile but also add the common profile to each configuration as well.
Example, in AutoMapper 4.2:
The classes to be mapped: User and Vehicle:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Vehicle
{
public int FleetNumber { get; set; }
public string Registration { get; set; }
}
The profiles:
public class Profile1 : Profile
{
protected override void Configure()
{
base.CreateMap<User, User>();
}
}
public class Profile2 : Profile
{
protected override void Configure()
{
base.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
}
}
public class CommonProfile : Profile
{
protected override void Configure()
{
base.CreateMap<Vehicle, Vehicle>();
}
}
Then create the configurations and map the objects:
[TestMethod]
public void TestMethod()
{
var user = new User() { Name = "John", Age = 42 };
var vehicle = new Vehicle {FleetNumber = 36, Registration = "XYZ123"};
var configuration1 = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CommonProfile>();
cfg.AddProfile<Profile1>();
});
var mapper1 = configuration1.CreateMapper();
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var mappedVehicle1 = mapper1.Map<Vehicle, Vehicle>(vehicle);//Maps both FleetNumber
//and Registration.
var configuration2 = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CommonProfile>();
cfg.AddProfile<Profile2>();
});
var mapper2 = configuration2.CreateMapper();
var mappedUser2 = mapper2.Map<User, User>(user);//maps only Name
var mappedVehicle2 = mapper2.Map<Vehicle, Vehicle>(vehicle);//Same as mappedVehicle1.
}
I tried this out and it works.
I am assuming you are mapping from User to User (if not then just change the destination type)
Assume this class for the following example:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
You can then use separate AutoMapper.Configuration to define 2 maps:
[TestMethod]
public void TestMethod()
{
var configuration1 = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());
var mapper1 = new MappingEngine(configuration1);
configuration1.CreateMap<User, User>();
var user = new User() { Name = "John", Age = 42 };
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var configuration2 = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());
configuration2.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
var mapper2 = new MappingEngine(configuration2);
var mappedUser2 = mapper2.Map<User, User>(user);
Assert.AreEqual(0, mappedUser2.Age);//maps only Name
}
To avoid mapping every other Type twice you could add a common method that takes a Configuration which maps everything that can be reached from User and call this on both configuration1 and configuration2 after the calls to CreateMap.
Update
For Automapper 4.x use the following:
var configuration1 = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, User>();
});
var mapper1 = configuration1.CreateMapper();
var user = new User() { Name = "John", Age = 42 };
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var configuration2 = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
});
var mapper2 = configuration2.CreateMapper();
var mappedUser2 = mapper2.Map<User, User>(user); //maps only Name
I think you can solve this problem with different Configuration objects as described here, you can find an example of this here