I would use AutoMapper to create an expression to use with LINQ (e.g. LINQ To SQL):
void Main()
{
Mapper.CreateMap<Person, PersonDto>();
Mapper.Engine.CreateMapExpression<Person, PersonDto>().ToString().Dump();
}
public class Person
{
public string Name { get; set; }
public IEnumerable<Person> Children { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public IEnumerable<PersonDto> Children { get; set; }
}
But a StackOverflowException occurs mapping the "Children" property (I had already written to Jimmy in the past).
The author told that the problem is that the root class has a self reference, but it's not supported by many LINQ providers.
If the root class has not self-references the problem is another with AutoMapper 3,
because the produced LINQ expression is:
x => new PersonDto() { Name = x.Name, Children = x.Children.Select(y => new AddressDto { Name = y.Name })}
Instead of:
x => new PersonDto() { Name = x.Name, Children = IIF((x.Children == null), null, x.Children.Select(y => new AddressDto { Name = y.Name }))}
So if the child property of the root class is null, the projection will fail.
At the moment is not possible to use AutoMapper for this purpose.
Related
I have four classes Offer, Section, Field, and Option:
Where the offer has sections and every section has some fields and each field has some options as shown:
public class Offer
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Section> Sections { get; set; }
}
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Field> Fields { get; set; }
}
public class Field
{
public int Id { get; set; }
public string Type { get; set; } //[question, group]
public ICollection<Option> Options { get; set; }
}
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
}
I tried to get the offer by id including the nested entities and this code works perfectly:
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
The problem is when I try to filter the Fields by 'type' like this:
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields.Where(f => f.Type == "question")
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
and I get this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
I've reviewed lots of questions and still cannot achieve that :(
Linq: query with three nested levels
EF LINQ include nested entities [duplicate]
Using LINQ to query three entitites. - Include path expression must refer to a navigation property defined on the type
Include() is used for Eager loading. It is the process whereby a query for one type of entity also loads related entities as part of the query, so that we don't need to execute a separate query for related entities. Where() is currently not supported inside Include.
If you want to just filter the result you can do something like this :
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
var filtered_offer =
new Offer
{
Sections = offer.Sections.Select(S => new Section
{
Id = S.Id,
Name = S.Name,
Fields = S.Fields.Where(f => f.Type == "question").ToList()
}).ToList(),
Id = offer.Id,
Name = offer.Name
};
I have a flattened DTO which I need to map to a Parent with Children relationship. I'd like to do this via AutoMapper as I'm using it in other places and it works great. I've seen examples of mapping a Parent and Child but not when the Child is a collection and the source is a flattened DTO. I've created some classes that I can use for getting the configuration correct. Below are my sample classes:
public class Parent
{
public int ParentId { get; set; }
public string ParentName { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public int ChildId { get; set; }
public string ChildName { get; set; }
}
public class ParentChildDTO
{
public int ParentId { get; set; }
public string ParentName { get; set; }
public int ChildId { get; set; }
public string ChildName { get; set; }
}
I'm performing the mapper initialization on startup. I'm not getting any errors until I try to perform the mapping. Below is my mapper initialization code. I've kept in the commented out line to show the other way that I've tried to accomplish this:
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<ParentChildDTO, Child>();
cfg.CreateMap<ParentChildDTO, Parent>()
.ForMember(dest => dest.Children, opt => opt.MapFrom(src => src));
//.ForMember(dest => dest.Children, opt => opt.MapFrom(src => new Child { ChildId = src.ChildId, ChildName = src.ChildName }));
});
Below is my code that I'm using for trying to perform the mapping configuration:
ParentChildDTO parentChildDTO = new ParentChildDTO { ParentId = 1, ParentName = "Parent Name", ChildId = 2, ChildName = "Child Name" };
Parent parent = AutoMapper.Mapper.Map<ParentChildDTO, Parent>(parentChildDTO);
List<LienActivity> mapTest = AutoMapper.Mapper.Map<List<BaseActivityUploadDTO>, List<LienActivity>>(request.Activities);
I've considered using a Custom Value Resolver, but was hoping to avoid the complexity and extra code if what I'm doing is possible with the correct configuration.
Here's the error that I get with the above code:
Error mapping types.
Mapping types: ParentChildDTO -> Parent
Type Map configuration: ParentChildDTO -> Parent
Property: Children
Here is another option where you define custom mapping just for the Children property.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ParentChildDTO, Parent>()
.ForMember(d => d.Children,
opt => opt.MapFrom(src => new List<Child>() { new Child() { ChildId = src.ChildId, ChildName = src.ChildName } }));
});
Given what you are going to use this for based on your comments - the below configuration should do the job (parent properties are resolved by default AutoMapper conventions so no need to explicitly map):
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<ParentChildDTO, Parent>()
.ConstructUsing(item => new Parent
{
Children = new List<Child>
{
new Child
{
ChildId = item.ChildId,
ChildName = item.ChildName
}
}
});
});
I am trying to make a select from the database using the entity framework 5.0.
I have a table called Persons that is referenced by PersonsImages, so basically one record from Persons can have many PersonsImages.
I've made a select statement that gives the Persons, but I would also like to get the PersonsImages as a List<PersonsImages>, and put them in a custom object.
This is the code that I have so far:
var person = new Persons();
using (var context = new PersonEntities())
{
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages // there's an error here
})
.FirstOrDefault();
}
The Persons and the PersonsImages classes look like that (they are copies of the ones generated by the entity framework):
public partial class Persons
{
public Persons()
{
this.PersonsImages = new HashSet<PersonsImages>();
}
public string personName { get; set; }
public string personLastName { get; set; }
public virtual ICollection<PersonsImages> PersonsImages { get; set; }
}
public partial class PersonsImages
{
public string imageName { get; set; }
public byte[] image { get; set; }
public virtual Persons Persons { get; set; }
}
I know I can make a second select and "manually" find them, but isn't it possible to do it in one, just as what the entity framework normally does?
Assuming your error is "can't construct an object in a LINQ to Entities query" - project into an anonymous type, call the ToArray() method to enumerate the results, then project into new instances of Persons:
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.ToArray() // data now local
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.FirstOrDefault();
I feel like I am missing something simple, but I have not found the documentation that answers my question.
I have recently been decomposing some of the linq projections into reusable expressions. It works great when operating on a collection, but I can't seem to figure out how to apply an expression to a single object in another expression. Below is an example of what I am trying to accomplish:
public class Person
{
public string ID { get; set; }
public string Name { get; set; }
}
public class PersonDto
{
public string ID { get; set; }
public string Name { get; set; }
}
public class Department
{
Person Manager { get; set; }
List<Person> Employees { get; set; }
}
public class DepartmentDto
{
PersonDto Manager { get; set; }
List<PersonDto> Employees { get; set; }
}
public Expression<Func<Person, PersonDto>> CreatePersonDto = p => new PersonDto
{
ID = p.ID,
Name = p.Name
};
public Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = d => new DepartmentDto
{
Manager = d.Manager // How do I transform this `Person` using `CreatePersonDto`
Employees = d.Employees.Select(CreatePersonDto) //Does not work either
};
EDIT: To be clear, I am using Linq-to-Entities that needs to use this Expression to generate a SQL statement. As a result, I cannot Compile the expression to a Func as I might be able to using Linq-to-Objects.
You can use LINQKit to expand the expressions that you have within other expressions:
private static Expression<Func<Department, DepartmentDto>> CreateDepartmentDtoUnexpanded = d => new DepartmentDto
{
Manager = CreatePersonDto.Invoke(d.Manager),
Employees = d.Employees.Select(employee => CreatePersonDto.Invoke(employee))
.ToList(),
};
public static Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = CreateDepartmentDtoUnexpanded.Expand();
You can:
public static Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = d =>
new DepartmentDto
{
Manager = CreatePersonDto.Compile()(d.Manager),
Employees = d.Employees.Select(CreatePersonDto.Compile()).ToList() // Or declare Employees as IEnumerable and avoid ToList conversion
};
Clearly, instead of calling two times Compile method you can store the compiled Func
I have the following entity collections in RavenDB:
public class EntityA
{
public string Id { get; set; }
public string Name { get; set; }
public string[] Tags { get; set; }
}
public class EntityB
{
public string Id { get; set; }
public string Name { get; set; }
public string[] Tags { get; set; }
}
The only thing shared is the Tags collection: a tag of EntityA may exist in EntityB, so that they may intersect.
How can I retrieve every EntityA that has intersecting tags with EntityB where the Name property of EntityB is equal to a given value?
Well, this is a difficult one. To do it right, you would need two levels of reducing - one by the tag which would expand out your results, and another by the id to collapse it back. Raven doesn't have an easy way to do this.
You can fake it out though using a Transform. The only problem is that you will have skipped items in your result set, so make sure you know how to deal with those.
public class TestIndex : AbstractMultiMapIndexCreationTask<TestIndex.Result>
{
public class Result
{
public string[] Ids { get; set; }
public string Name { get; set; }
public string Tag { get; set; }
}
public TestIndex()
{
AddMap<EntityA>(entities => from a in entities
from tag in a.Tags.DefaultIfEmpty("_")
select new
{
Ids = new[] { a.Id },
Name = (string) null,
Tag = tag
});
AddMap<EntityB>(entities => from b in entities
from tag in b.Tags
select new
{
Ids = new string[0],
b.Name,
Tag = tag
});
Reduce = results => from result in results
group result by result.Tag
into g
select new
{
Ids = g.SelectMany(x => x.Ids),
g.First(x => x.Name != null).Name,
Tag = g.Key
};
TransformResults = (database, results) =>
results.SelectMany(x => x.Ids)
.Distinct()
.Select(x => database.Load<EntityA>(x));
}
}
See also the full unit test here.
There is another approach, but I haven't tested it yet. That would be to use the Indexed Properties Bundle to do the first pass, and then map those results for the second pass. I am experimenting with this in general, and if it works, I will update this answer with the results.