NHibernate load the nested complex object entity in automapper queryable extension - c#

I am having class employee which contain complex property Department.
public Class Employee
{
public int Id { get; set;}
public Department {get; set;}
}
public Class Department
{
public int Id {get;set;}
public string Name { get;set;}
}
i create the map for both
(consider the above both class are available in two namespace 'source', 'destination')
Mapper.CreateMap<source.Employee,destination.Employee>()
Mapper.CreateMap<source.Department,destination.Department>()
when i project it.
empQueryable.Project().To<destination.Employee>();
If i saw the NHProfiler
I found that it loads the entity Department
and create the query
select ... from employee left outer join Department .....
i don't know why it loads entity Department, it should make just the projection.

I'm going to go out on a limb here and assume that "destination.Employee" contains references to the "destination.Department". When AutoMapper builds a projection, it does the same as it would as "Mapper.Map". It crawls the destination type, its properties, and its member's properties all the way down. In short, it will build a Select expression something like:
.Select(e => new destination.Employee {
Id = e.Id,
Department = new destination.Department {
Id = e.Department.Id,
Name = e.Department.Name
}
});
You have a few choices here:
Ignore members you don't want mapped in your employee configuration, namely the "Department" property
Create targeted destination types based on use case, and don't share destination types that require different hydrated data based on different needs.
Use explicit expansion (ForMember(d => d.Department, opt => opt.ExplicitExpansion()), then explicitly expand that member in your projection as needed, with the overload for "Project.To" that takes a list of members to expand.

Related

When Configuring an entity for DbContext in .Net, how do I represent a collection of objects with a unidirectional relationship?

I am doing an object configuration for entity framework where object A has contains a ICollection and object B does not have any relationship to this object. How would I configure this?
Here is an example of my object setup
public class A {
public string id { get; set; }
public ICollection<B> itemsILike { get; set;}
}
public class B {
public string id { get; set; }
}
B doesn't need to know about A at all, but I want A to have a list of B that it can add or remove from (and can also be empty)
This is what I currently have, but I do not think this creates the correct relationship
public void Configure(EntityTypeBuilder<A> builder)
{
builder.HasKey(e => e.id);
builder.HasMany(e => e.itemsILike);
}
public void Configure(EntityTypeBuilder<B> builder)
{
builder.HasKey(e => e.id);
}
Any Idea as to how to configure?
In order to store information about which Bs belong to an A the database needs to store this information somewhere. Without it would not be able to establish which Bs to return when you are retrieving a record of A.
This can be either via a foreign key aId on B or via a many-to-many relationship with an intermediate/join table that contains at least aId and bId.
There also is the option of storing a collection of Bs in a column of A and deserialising it in code. An example would be to store a comma-separated list of ids as a single string and add an access property/function which deseralise the data. For complex types you could use json.
Please note I'm not advocating storing json,, but if you cannot/don't_want to store the relationship as a column of B this could be a solution.
BTW. For EF Core you may want to look at No Foreign Key Property and Shadow Properties mentioned there.

Inisght.Database Auto Repository and Custom Column Mapping

I am trying to implement Insight.Database in a project, and have run into a brick wall trying to utilize the automatic interface implementation AND mapping object properties to odd column names in the database.
I have the following structure...
class Employee
{
string EmployeeCode {get; set;}
string Name {get; set;}
}
class Vacation
{
int VacationId {get; set;}
DateTime VacationDate {get; set;}
string EmployeeCode {get; set;}
string ApprovedByEmployeeCode {get; set;}
Employee Employee {get; set;}
Employee ApprovedByEmployee {get; set;}
}
My database looks like....
Table: Employees (EmployeeCode, [Name])
Table: Vacations (VacationId, VacationDate, EmployeeCode, ApprovedByEmployeeCode)
View: VacationsView (so I don't have to keep writing [and changing]
the same SELECT over and over)
SELECT v.VacationId, v.VacationDate, v.EmployeeCode, v.ApprovedByEmployeeCode, e1.EmployeeCode AS CreatedByEmployeeCode, e1.[Name] AS CreatedByName, e2.EmployeeCode AS ApprovingEmployeeCode, e2.[Name] AS ApprovingName
FROM Vacations v
INNER JOIN Employees e1 ON v.EmployeeCode = e1.EmployeeCode
INNER JOIN Employees e2 ON v.ApprovedByEmployeeCode = e2.EmployeeCode
Stored Procedure: GetAllVacations
SELECT * FROM VacationsView
Finally, with Insight.Database, I am trying to have an interface that will autopopulate my objects and tell it how to use the different column names from my stored procedure for the "employee" properties.
public interface IMyRepository
{
IList<Vacation> GetAllVacations();
}
....
var repo = conn.As<IMyRepository>();
return repo.GetAllVacations();
This works (as in doesn't error) and all the properties of my vacation are correctly mapped, but my two "employee" properties are null (as expected because the column names don't line up to the property names of an employee object). What I can't figure out is how to tell Insight "Use CreatedBy.." fields to build the "Employee" property and "Approving..." fields to build the "ApprovedByEmployee" property.
I have been able to accomplish it using OneToOne with a callback and columnOverride and then use a standard Query(). I.E..
var vacationStructure =
new OneToOne<Vacation, Employee, Employee>(
callback: (vacation, createdBy, approvedBy) =>
{
vacation.Employee = createdBy;
vacation.ApprovedByEmployee = approvedBy;
}, columnOverride: new ColumnOverride[]
{
new ColumnOverride<EmployeeModel>("CreatedByEmployeeCode", "EmployeeCode"),
new ColumnOverride<EmployeeModel>("CreatedByName", "Name"),
new ColumnOverride<EmployeeModel>("ApprovingEmployeeCode", "EmployeeCode"),
new ColumnOverride<EmployeeModel>("ApprovingName", "Name")
});
....
var results = await conn.QueryAsync("GetAllVacations", new {employeeCode}, Query.Returns(_vacationStructure));
However, I'm really trying to utilize the auto interface capabilities of Insight.
Is what I'm trying to do possible?
Assembling repeated child objects isn't something that's currently done automatically, and the interface implementation doesn't give you the right hooks to override the behavior.
Some options:
A. Change the shape of your result set to return the employees as a list with properties.
B. If the classes aren't sealed, derive from Employee so Insight can differentiate between the classes:
public class AssigningEmployee : Employee {
public string AssigningName { get { return Name; } set { Name = Value; } }
...
}
These solutions are all meh. The whole point of Insight.Database is to just work without a lot of extra work. So...
I opened a github issue to track this:
https://github.com/jonwagner/Insight.Database/issues/384

Is there a work around for unioning two entities of the same interface using entity framework?

I have a search model class that searches different entity sets with the entity itself implementing a IAssignable interface. The code looks like this.
public void Search()
{
List<T> lessons = new List<T>();
List<T> courses = new List<T>();
if (ShowLessons)
lessons = db.Set<Lesson>()
.Where(IAssignableExtensions.SearchPredicate(q))
.Select(LessonMapping).ToList();
if (ShowCourses)
courses = db.Set<Course>()
.Where(IAssignableExtensions.SearchPredicate(q))
.Select(CourseMapping).ToList();
Results = lessons.Union(courses).ToList<T>();
}
The static extension is irrelevant, it just searched based on the query. I would prefer to bust this into it's own rather than static extension but eh. Now this works as expected. I am pulling to memory two datasets, lessons and courses, I am unioning them into a IEnumerable of a generic type based on teh Course Mapping or Lesson Mapping Expressions.
public Expression<Func<IAssignable, T>> LessonMapping { get; set; }
public Expression<Func<IAssignable, T>> CourseMapping { get; set; }
The problem is when I want to do any type of paging. As you can see the lessons and courses are searched, brought into memory and then unioned and returned. If I do any paging using an IPagedList for example, it is bringing back ALL lessons and courses then it is only using a subset of the total data in the list for the pages.
If Entity Framework supported interfaces I would just do a cast on the interface and union right at the db call. I haven't changed this code yet but I feel I might have to create a custom stored procedure or use the Query call on the datacontext, but if I use a stored procedure I have to make sure to update it on any changes to the domain, and if I use the Query I have to re-jig the selects, interfaces and still have to worry about inline sql...
Anyone have any ideas?
UPDATE
The solution that I ended up using after thinking about Erik's solution was to just use a projected object that implemented IAssignable.
public class SomeProjection : IAssignable
{
public int ID { get; set; }
public string Name { get; set; }
public string Description {get;set;}
public string Privacy {get;set;}
}
And then used it within the union call queryable
Results = db.Set<Lesson>().Select(p => new SomeProjection() { Privacy = p.Privacy, ID = p.ID, Name = p.Name, Description = p.Description })
.Union(db.Set<Course>().Select(p => new SomeProjection() { Privacy = p.Privacy, ID = p.ID, Name = p.Name, Description = p.Description }))
.Where(IAssignableExtensions.SearchPredicate(q))
.Select(Mapping).ToList<T>();
If Entity Framework supported interfaces I would just do a cast on the interface and union right at the db call.
It has nothing to do with what Entity Framework supports. If you create an interface, it is independent of the SQL technology in the back end and you want EF to somehow magically select properties based on an interface with no mappings or configuration? Not going to happen.
Instead you could simply use inheritance if there are some properties that are the same between objects, then you don't even need to union them, unless you are where-ing on properties that don't exist between both.

EF 5 Code First - Why is explicitly loaded data not accessible from my model?

I have the following two model objects which have a many-to-many relationship:
public class StaffMember
{
public Guid StaffMemberKey {get; set;}
// lots of other properties that aren't relevant
public ICollection<Case> Cases {get; set;}
}
public class Case
{
public int CaseKey {get; set;}
// lots of other properties that aren't relevant
public ICollection<StaffMember> Staff {get; set;}
}
The mapping for the many-to-many relationship is handled in the configuration for the Case entity:
public class CaseMapping : EntityTypeConfiguration<Case>
{
public CaseMapping()
{
// other property and relationship mappings
// Many-to-Many mapping with Staff Members
HasMany(c => c.Staff)
.WithMany(staffMember => staffMember.Cases)
.Map(m =>
{
m.ToTable("Cases_StaffMembers", "dbo");
m.MapLeftKey("CaseKey");
m.MapRightKey("StaffMemberKey");
});
}
}
Everything is working great in terms of being able to query against this relationship, add, delete, etc. However, when trying to explicitly load and filter staff members for a case, as described here, no data is being loaded in to the appropriate collection of related entities.
Here is an example of what I'm attempting do:
var staffMemberKey = Guid.Parse("...");
var caseKey = 5;
using (var context = new CodeFirstContext())
{
var selectedCase = context.Cases.Find(caseKey);
context.Entry(selectedCase).Collection(c => c.Staff).Query().Where(sm => sm.StaffMemberKey == staffMemberKey).Load();
}
I would expect that selectedCase.Staff would contain the staff member that was loaded, but it remains null. If I call ToList() instead of Load when querying for the related data, the resulting list does contain the correct staff member entity. If I simply call context.Entry(selectedCase).Collection(c => c.Staff).Load();, then the data is loaded as expected. Is there something I'm missing? What gives?
As a final note, I have lazy loading and proxy creation disabled for my context, in case that makes any difference in this scenario.
When you call Query(), it returns an IQueryable that gives the entities that would be in that property -- it is not designed to be used to update the property. It is basically a "shortcut" for:
ctx.Staff.Where(staff => staff.Case.Id == caseKey);
Load() will load entities into your context, as if you had called ToList() but without returning anything. It works on any IQueryable, and does not capture anything related to the Entry().

EF 4.1 Code First - Determine What Properties Have Changed

I'm using Entity Framework 4.1 Code First. Is there a built-in way to get a list of what properties have changed since the entity was loaded from the database? I know code first detects that an object was changed, but is there a way to get exactly what properties have changed?
For scalar and complex properties you can use the following to extract the changed property names of an entity myEntity:
var entry = context.Entry(myEntity);
var namesOfChangedProperties = entry.CurrentValues.PropertyNames
.Where(p => entry.Property(p).IsModified);
A few things to note here:
CurrentValues.PropertyNames only contains scalar and complex properties, not navigation properties.
Complex properties means: Only the name of the complex property which is declared on the entity, not the actual individual properties of the complex type itself, for example: If you have this model...
[ComplexType]
public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
... then, if myEntity is a Person, CurrentValues.PropertyNames would contain "Id", "Name" and "Address" but not "Address.Country" or "Address.City" (nor "Country" or "City").
If a complex property is marked as modified (.IsModified in the code above is true) then this means that either the reference (Person.Address in the example above) has changed, no matter if actually the property values (Country and City) inside of the complex type have changed or not. Or that any of the properties of the complex type has changed (Country or City has changed). I believe it's not possible to find out which one, because EF always sends an UPDATE command for all complex type properties to the database, even if only one property has changed and the other remained unchanged. I would conclude from this that EF doesn't track changes of individual complex type properties.

Categories

Resources