I have this line of code in my global.asax
Mapper.CreateMap<Order, OrderDTO>();
These are my classes:
public class Customer
{
public string Name {get; set;}
}
public class Order
{
public int OrderId { get; set; }
public Customer Customer { get; set; }
}
public class OrderDTO
{
public int OrderId { get; set; }
public string Name { get; set; }
}
And this is my code:
Customer cust = new Customer { Name = "Jaggu" };
Order order = new Order { Customer = cust, OrderId = 123 };
OrderDTO dto = Mapper.Map<Order,OrderDTO>(order);
my dto contains OrderId but Name is null. As per documentation it should work:
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
If I change my global.asax's mapping to this:
Mapper.CreateMap<Order, OrderDTO>().ForMember(dest => dest.Name,
mapping => mapping.MapFrom(order => order.Customer.Name));
it works! This make me curious. Is the doc wrong? or am I doing it wrong?
It will work if you follow the standard naming convention:
public class OrderDTO
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
}
Notice that the property is called CustomerName and not Name. When flattening the Order model into a Dto, the Customer.Name goes into CustomerName.
Related
I'm new to automapper and I'm trying very simple map. I have the following class and I', trying to map personmodel to person.
It keeps failing at orders. Sorry if this silly but I could not figure out. I removed all properties on orders to see what is wrong
public class Person
{
public Person()
{
Orders = new List<Orders>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<Orders> Orders { get; set; }
}
public class Orders
{
public string OrderName { get; set; }
}
public class PersonModel
{
public PersonModel()
{
Orders = new List<OrderModel>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public List<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public string OrderName { get; set; }
}
I have the mapper defined as Mapper.Initialize(x => x.CreateMap<PersonModel, Person>());
The error that I get is:
Error mapping types.
Mapping types:
PersonModel -> Person
PersonModel -> Person
Type Map configuration:
PersonModel -> Person
PersonModel -> Person
Property:
Orders
Create Map between Orders and OrderModel.
Mapper.CreateMap<Orders, OrderModel>().ReverseMap();
I use this code for map a list<>
var result = NewMapperHelper.MapList<OrderModel, Orders>(YourSource);
and:
public static class NewMapperHelper
{
public static List<TDestination> MapList<TDestination, TSource>
(List<TSource> entity)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TSource, TDestination>().ReverseMap();
});
var _mapper = config.CreateMapper();
return _mapper.Map<List<TDestination>>(entity);
}
}
i have 2 tables that each one has a one-to-many relation to the table between and the table between has ids of 2 other tables
dbo.Posts dbo.Posts_Categories dbo.Categories
-ID -ID -ID
-Title -PostID -Name
-CategoryID
result i expect is :
Title = post1 Categories = web,mobile,desktop
Title = post2 Categories = app,game
...
i know how to query this in sql using Stuff function and For Xml Path but i have no idea how do i do this in entity framework!
any suggestion or book for how to do works in this way might help!
Edit: EF classes added:
public class Post : ReportingBase {
public Post() { }
[Required, MaxLength(500)]
public string Title { get; set; }
[Required, MaxLength(500)]
public string Address { get; set; }
[Required]
public string Body { get; set; }
[Required, MaxLength(500)]
public string Tags { get; set; }
[Required]
public int Visit { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
public virtual ICollection<Post_AttachedFile> Posts_AttachedFiles { get; set; }
[ForeignKey("Image")]
public virtual int? ImageID { get; set; }
public virtual Image Image { get; set; }
}
public class Post_Category {
public Post_Category() { }
[Key, Column(Order = 0)]
public int PostID { get; set; }
[Key, Column(Order = 1)]
public int CategoryID { get; set; }
public virtual Post Post { get; set; }
public virtual Category Category { get; set; }
}
public class Category : EntityBase {
public Category() { }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, MaxLength(150)]
public string Address { get; set; }
public int? ParentID { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
}
thank you in advance
Edit : According to #IvanStoev answer i did following :
List<P> p = context.Posts.Select(post => new {
Title = post.Title,
Categories = post.Posts_Categories.Select(pc => pc.Category.Name).ToList()
}).ToList();
and created a class called P :
public class P {
public string Title { get; set; }
public List<string> Categories { get; set; }
}
but it doesn't work correctly and the problem is how to return the result.
In EF it's even easier than in SQL thanks to the concept of so called navigation properties. All you need to know is a basic LINQ query syntax and just follow them (navigate) to get the data needed. For instance:
var result = db.Posts
.Select(post => new
{
Title = post.Title,
Categories = post.Posts_Categories
.Select(pc => pc.Category.Name)
.ToList()
})
.ToList();
The result is a list of anonymous type having string Title property and List<string> Categories property containing the related category names.
You can use Linqpad (software) to get familiarize with the Linq query it builds lambda expression for you by connecting to the database and provides output too to cross verify.
The below one is the lambda expression for joining the tables you have mentioned.
p - Post
pc - post_categories
c - categories
Code:
Posts.Join(Post_Categories, p => p.ID, pc => pc.ID, ( p, pc) => new { p = p, pc = pc})
.Join(Categories, pcc => pcc.pc.CategoryID, c => c.ID, ( pcc, c) => new { pcc = pcc, c = c})
.Select(p.Title)
.Select(c.Name)
You should be using .Include() for any join in EF Core.
I've come up with this simple example: one person can have many dogs.
public class Person
{
public Guid Id { get; set; }
public ICollection<Dog> Dogs { get; set; } // One Person can have many Dogs
}
public class Dogs
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
}
Generate the migrations after creating the models. Not going over how to do that in this answer.
Here's how you use .Include() to join upon the two different tables:
public class PersonRepository : RepositoryBase
{
public IEnumerable<Person> FetchPeopleWithManyDogs()
{
return DatabaseContext.Person
.Include(x => x.Dogs)
.Where(x => x.Dogs.Count() > 1).ToList();
}
}
Hi I have a list of people that also have a List of contacts.
I'm trying to link my Contact Methods so that it'll be populated with the correct Person_Id
Example
Id Name Method Value Person_Id
1 John Phone 7777777777 2
2 Joan Phone 8888888888 8
3 Jack Phone 9999999999 9
Currently it displays Person_Id as all nulls, I believe I didn't create my ContactMethod Class correctly. If I can get help establishing a proper foreign key. I think that's my issue.
// Primary Keys -------------------------------------------------------
public int Id { get; set; }
// Associations -------------------------------------------------------
public virtual Person Person { get; set; }
// Fields -------------------------------------------------------------
public string Name {get; set;
public string Value { get; set; }
I populate the information through a migration script, here's a snippet
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person = person }));
Working Method not utilizing foreign keys.
ContactMethod Class
// Associations -------------------------------------------------------
public int? Person_Id { get; set; }
MigrationScript
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person = person.Id }));
I'm going to suppose that you have a model like this:
public class Contact
{
public int Id { get; set; }
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
Now,to achieve the escenario that you want, you need to configure a one-to many relationship between Contact and Person. There are two ways to do that, using Data Annotations or using Fluent Api. I'm going to use Fluent Api in this case. The easy way is override the OnModelCreating method of your Context to configure the relationship, for example, at this way:
public class YourContext : DbContext
{
//...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(p => p.Contacts)
.WithRequired(c => c.Person)
.Map(f => f.MapKey("Person_Id"));
}
}
As you can see, I'm specifying a PK that is not declared in your Contact class, that is the escenario that you want. Now with this configuration, you could do this, for example:
Person john=new Person(){Name = "John"};
var contactList = new List<Contact>() {new Contact(){Name = "Phone",Value = "555-444-333",Person = john},
new Contact() { Name = "email", Value = "john#gmail.com",Person = john}};
using (YourContext db = new YourContext())
{
db.Contacts.AddRange(contactList);
db.SaveChanges();
}
Update
If you want to do the same configuration using Data Annotations, your model would be like this:
public class Contact
{
[Key]
public int Id { get; set; }
[InverseProperty("Contacts")]
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class Person
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you want to use a FK related to Person in your Contact class, you could do this:
public class Contact
{
[Key]
public int Id { get; set; }
public int? Person_Id { get; set; }
[ForeignKey("Person_Id"), InverseProperty("Contacts")]
public virtual Person Person { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
This way you can use directly the Person_Id FK in your second migration script:
var person = people.Find(x => x.ContactBase_GUID == contactBaseGuid);
contactMethods.AddRange(channels.Select(pair => new { pair, method = reader.Get<string>(pair.Key) })
.Where(x => x.method != null)
.Select(x => new ContactMethod { Value = x.method, Person_Id = person.Id }));
When mapping a DAL object to DTO object I get an unexpected query.
I have some DAL objects:
public class MainDalObject
{
public Guid Id { get; set;}
public string Description { get; set; }
public ICollection<SubDalObject> Subs { get; set; }
}
public class SubDalObject
{
public Guid Id { get; set;}
public string Description { get; set; }
public MainDalObject Main { get; set; }
}
And my DTO classes:
public class MainObject
{
public Guid Id { get; set;}
public string Name { get; set; }
public IEnumerable<SubObject> Subs { get; set; }
}
public class SubObject
{
public Guid Id { get; set;}
public string Name { get; set; }
public MainDalObject Main { get; set; }
}
My MainObject controller contains a IQueryable method:
public IQueryable<MainObject> Get()
{
return (from m in Context.Get<MainDalObject>()
select new MainObject
{
Id = m.Id,
Name = m.Description,
Subs = m.Subs.Select(s => new SubObject
{
Id = s.Id,
Name = s.Name
}
});
}
This works fine, however the query is not optimal. When i trigger the query on /api/MainObject , I am not selecting the subitems at all. But when i look at the query it is selecting the subItems anyway.
However when I change the query to /api/MainObject?$select=Id,Name , the query is not selecting the SubObjects.
So what I am expecting is that somewhere in the WebApi framework, when no SelectExpandFilter is used, the responsewriter is doing a ToList(), without specifying the Select statement.
I am looking for the best place to fix this problem, I could probably set a select expand ODataQueryOption that could fake an select or expand call, but I am not sure if that is the way to go.
If someone else runs in this problem, this is what I have done to fix it. The select expand query should be derived from the edmmodel in the request context, but this is basically how it works.
public virtual IQueryable<TDTO> Get(ODataQueryOptions queryOptions)
{
if (queryOptions.SelectExpand == null)
{
var selectOption = new SelectExpandQueryOption("Id,Name", string.Empty, queryOptions.Context);
Request.SetSelectExpandClause(selectOption.SelectExpandClause);
}
return (from m in Context.Get<MainDalObject>()
select new MainObject
{
Id = m.Id,
Name = m.Description,
Subs = m.Subs.Select(s => new SubObject
{
Id = s.Id,
Name = s.Name
}
});
}
I have a mapped model with Nhibernate like this:
public class A
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
/* and other 20 properties... */
}
public class B
{
public virtual long Id { get; set; }
public virtual A ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
I would like to create a query (queryover, linq, hql etc... anyway) to get a List<B> and fill ItemA property with just Id and Number properties because I need only this properties (I have lots of properties in A class and I will not use it on my results). Is there any way to do this or should I create a ViewModel? If I need to create a ViewModel, how can I do this with QueryOver?
Thank you!
Yes, you have to create a view model, like following:
public class BViewModel
{
public virtual long Id { get; set; }
public virtual AViewModel ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
public class AViewModel
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
}
Now you oculd query it with linq like this:
var listOfB = session.Query<B>()
.Select(b => new BViewModel
{
Id = b.Id,
Name = b.Name,
Date = b.Date,
ItemA = new AViewModel
{
Id = b.ItemA.Id,
Number = b.ItemA.Number,
},
}).ToList();
using QueryOver, you can use JoinAlias to join A with B, and SelectList to retrieve only the properties that you want.
Then use TransformUsing to transform the result into whatever format you want.
Something along the lines of:
A aTemplate = null;
s.QueryOver<B>().
.JoinAlias(() => aTemplate)
.Where (/*whatever conditions*/)
.SelectList(list => list
.Select(() => aTemplate.Id)
.Select(() => aTemplate.Number)
.TransformUsing(Transformers.AliasToBeanTransformer)
.List<A/*or some DTO*/>();