I have two entities:
public class Parent
{
public virtual int Id { get; set; }
}
public class Child
{
public virtual int Id { get; set; }
public virtual int ParentId
{
get
{
if (Parent != null)
return Parent.Id;
return -1;
}
set
{
if (Parent != null)
Parent = new Parent();
Parent.Id = value;
}
}
protected virtual Parent Parent
{
get;
set;
}
}
The Parent property is set up like this to simplify the API side, and I do not want to change it to expose this property publicly. I have an override of the mappings for the Child class to accommodate this:
public class ChildMappingOverrides : IAutoMappingOverride<Child>
{
public void Override(AutoMapping<Child> mapping)
{
mapping.References<Parent>(Reveal.Member<Child>("Parent")).Column("Parent_id");
mapping.IgnoreProperty(x => x.ParentId);
}
}
Now, if I want to query all the Child objects for a given parent Id, I would perform this:
session.QueryOver<Child>().Where(c => c.ParentId == 1);
However, this throws a QueryException:
could not resolve property: ParentId of: My.Namespace.Child
How can I retrieve the set of Child objects that have a particular Parent Id?
Untested, but you could try this:
session.QueryOver<Child>()
.Where(Restrictions.Eq(
Projections.SqlProjection(
"{alias}.Parent_id as ParentId",
new[] { "ParentId" },
new[] { NHibernateUtil.Int32 }), 1))
.List<Child>();
You're not going to be able to query on unmapped associations in any NHibernate query, but this at least minimizes the loss of compile-time checking.
This is somewhat limited from what I can tell. {alias} will always be replaced by the alias of the root entity, meaning if you want to do this for a more complex query that doesn't start with Child, you might be out of luck.
I've solved it by using CreateSQLQuery instead of QueryOver:
session.CreateSQLQuery("SELECT C.* FROM CHILD C WHERE C.Parent_id = (:id)")
.AddEntity(typeof(Child))
.SetInt32("id", parentId)
.List<Child>();
I'd like to see a better way if possible, losing the compile-time checking is kind of a downer.
Related
I have the following (simplified) setup:
Public class parent
{
public string name{get;set;}
public list<child> childList {get;set;}
}
Public class child
{
public int id {get;set;}
public bool imported{get;set;}
public dateTime? timeSpan {get;set;}
}
and I have this query:
var relevant = context.parent
.include(x => x.child.OrderByDescending(y => y.id).FirstOrDefaultAsync(z => z.imported == false && timeSpan == null)
.Where(x => x.child != null);
Which does not work.
Basically, I am trying to include all the parents children, but order them by id descending and then check if the first one (eg newest one) has imported == false and timeSpan == null, and only include the parent rows that have a child that meets this condition.
I know I could do this:
var relevant = context.parent
.include(x => x.child);
and then extract the data I need, but is it possible to do it in one using Linq?
As you are using the tag linq-to-entities I assume you are using entity framework.
It seems to me that you have modeled a one-to-many relation between Parent and Child: every Parent has zero or more Children, and every Child belongs to exactly one Parent.
It could also be that you have a many-to-many relation. The classes are slightly different (and the database will have an extra table that you don't have in your DbContext), but the problem remains the same.
It could be because of your simplifications, but I see some odd things in your classes that might cause your problems.
In entity framework a proper one-to-many relation is modelled as follows:
public class Parent
{
public int Id {get;set;}
public string name{get;set;}
// Every parent has zero or more Children
public virtual ICollection<Child> Children {get;set;}
}
public class Child
{
public int id {get;set;}
public bool Imported{get;set;}
public DateTime? TimeSpan {get;set;}
// every Child belongs to exactly one Parent using foreign key
public int ParentId {get; set;}
public Parent Parent {get; set;}
}
The collection of Children in your Parent can't be a List. What would ChildList[3] mean?
Besides, this Collection should be virtual (See SO: Understanding code first virtual properties)
You wrote:
Basically, I am trying to include all the parents children, but order
them by id descending and then check if the first one (eg newest one)
has imported == false and timeSpan == null, and only include the
parent rows that have a child that meets this condition.
A bit difficult to understand, but it seems that you have a sequence of Parents, and you want only those Parents and their children, where the Child with the highest ChildId is not Imported and has a null TimeSpan.
var result = dbContext.Parents
.Select(parent => new
{
// Take all Parent properties you need in your end result, for example
Id = parent.Id,
Name = parent.Name,
Children = parent.Children
.OrderByDescending(child => child.Id),
})
.Select(parent => new
{
Id = parent.Id,
Name = parent.Name,
Children = parent.Childrent,
NewestChild = parent.Children.FirstOrDefault(),
})
// keep only the parents you want to keep:
.Where(parent => parent.NewestChild != null
&& !parent.NewestChild.Imported
&& parent.NewestChild.TimeSpan == null));
So, I have a list of children on my parent object, and I want to persist them on my SQL Server. When I run the application for the first time, all the children get their FK correctly, but when I run it again and no new parent is added, the new child(of an existing parent) doesn't get it's parent FK, just NULL. How can I map the parent FK on my child mapping for those situations?
I've tried the Inverse() method, but as I need the parent key to be generated all children gets null anyway. I need something like, if the parent is new, then the parent will update it's children FK, but when only the child is new I would need it to do the Inverse() method, is it possible?
Some more info:
Every time I call the ParentPersist method, and it cascades as needed. I've added the AddChild() method to set the ParentId when a new child is added to the list, it's working as I debugged it, so the child is setting it's ParentId correctly.
The objects are like the following:
public class Parent
{
public virtual int Id { get; set; }
...
public virtual IList<Child> Children{ get; set; }
public virtual void AddChild(Child ch)
{
ch.IdParent = this.Id;
Children.Add(ch);
}
}
public class Child
{
public virtual int Id { get; set; }
...
public virtual int IdParent {get;set;}
}
And my mapping:
public class ParentMapping : ClassMap<Parent>
{
public ParentMapping ()
{
Id(cso => cso.Id).GeneratedBy.Identity();
...
HasMany(cso => cso.Children).KeyColumn("IdParent").Cascade.SaveUpdate().Not.LazyLoad();
}
}
public class ChildMapping : ClassMap<Child>
{
public ChildMapping ()
{
Id(cso => cso.Id).GeneratedBy.Identity();
...
}
}
Your logic (e.g. Add() method in Parent, Inverse() mapping) was OK. You were almost there. There is only one BUT...
In general, the proper (if not only correct) solution is to use objects to express realtion and not just the ValueType/int values. That's why we call it ORM - Object-relational mapping
Object in C# should look like this:
public class Parent
{
...
// correct mapping of the children
public virtual IList<Child> Children{ get; set; }
// this method uses the below updated Child version
public virtual void AddChild(Child ch)
{
// this is replaced
// ch.IdParent = this.Id;
// with this essential assignment
ch.Parent = this;
Children.Add(ch);
}
}
public class Child
{
...
// instead of this
// public virtual int IdParent {get;set;}
// we need the reference expressed as object
public virtual Parent Parent { get; set; }
}
So, now, once we have objects in place, we can adjust the mapping like this:
// parent
public ParentMapping ()
{
...
HasMany(cso => cso.Children)
.KeyColumn("IdParent")
.Inverse() // this is essential for optimized SQL Statements
.Cascade.SaveUpdate() // All delete orphan would be better
.Not.LazyLoad();
}
...
// Child
public ChildMapping ()
{
...
References(x => x.Parent, "IdParent"); // it is a to use Inverse()
}
With this Business Domain Model and the mapping (Inverse(), assigning bothe relation ends in Add() method...), NHibernat will have enough information to always (insert, update) issue proper SQL statements
NOTE: One could ask why to map Parent Parent { get; set; } and not just the int IdParent { get; set; }... In fact, if we would have existing Parent (with NOT transient ID, i.e. > 0) - there won't be any difference. The trick/problems would appear on a new Parent insertion. Almost always, assignement of the children comes before the Parent is persiseted (flushed), and its ID is recieved from DB (sql server identity). And that could/would cause the child.IdParent == 0 ...
We should remember, that in general - ORM is about objects, i.e. relation is represented by Reference types.
I'm having a bit of an issue. I don't quite know how to handle the situation so I'll just explain a simplified scenario and hopefully you can help me.
I'm trying to map a parent database object to a parent bll object. In this parent database object, there is a foreign key to the ID of the child, and in my parent bll object I use the child bll object (containing more than just an ID).
So here are my bll objects:
public class Parent
{
public int ID { get; set; }
public Child Child { get; set; }
}
public class Child
{
public int ID { get; set; }
public string FirstName { get; set; }
}
And here is my mapper class/method:
public class ParentMapper
{
public Parent MapFromSource(ParentDatabaseObject parentDO)
{
Parent parent = new Parent();
parent.ID = parentDO.ID;
parent.Child = ???;
return parent;
}
}
I don't think it's very important what the ParentDatabaseObject looks like in this case, I'd just like to know how I should map the parent.Child object.
I have considered the following:
parent.Child = new Child();
parent.Child.ID = doParent.Child.Id;
parent.Child.FirstName = doParent.Child.FirstName;
Which doesn't feel right, 'cause I kind of have the urge to put this in my ChildMapper, which leads me to my second way of implementing this (assuming I have a seperate child mapper and have an instance of it called childMapper):
parent.Child = childMapper.MapFromSource(parentDO.Child);
But I kind of have the feeling that using this way of mapping is making my code a bit tightly coupled, because I'd be using my ChildMapper in my ParentMapper.
So I guess my question is: how should I implement this kind of mapping. Is this last method correct or is there something even better? I'm already discarding the first thing I tried.
Thanks for your help!
(I did research before posting this question and this was the closest I could find:
Data Mapper for Child Objects , but I wasn't really content with the only answer in there)
Shouldn't it be better -
parent.Child = childMapper.MapFromSource(parentDO.FoeignKeyToTheChild);
I think you should have methods to get object by Id.
EDIT : If your mapper doesn't DataAccess code, then you have to map the child within your Repository. As your Repository already have DataObjects ready, you can do it the following way -
ParentMapper:
public class ParentMapper
{
public Parent MapFromSource(ParentDo parentDo)
{
Parent parent = new Parent();
parent.Id = parentDo.Id;
return parent;
}
}
ChildMapper:
public class ChildMapper
{
public Child MapFromSource(ChildDo childDo)
{
Child child = new Child();
child.Id = childDo.Id;
child.FirstName = childDo.FirstName;
return child;
}
}
Repository:
public class Repository
{
//you already have parentDo
//you already have childDo
public Parent GetParent()
{
Parent parent = parentMapper.MapFromSource(parentDo);
parent.Child = childMapper.MapFromSource(childDo);
return parent;
}
public Child GetChild()
{
Child child = childMapper.MapFromSource(childDo);
return child;
}
}
Otherwise, your Mapper must have access to DataAccess code.
I need to get a node of a nested collection looking through It's deep with linq.
This is the nested class:
public class Group
{
public int Id { get; set; }
public string Description { get; set; }
public int ParentId { get; set; }
public List<Group> Groups { get; set; }
}
Each instance of this class can have multiple instances inside the Groups method and so on. Each instance is linked through the ParentId property.
I need, having an instance of Group class, retrieve his father. I tried with this:
var parent = _repositoryGroups
.Where(g => g.Id == [my Group instance].ParentId)
.SelectMany(g => g.Groups)
.FirstOrDefault()
I don't know why, but not always it find the father instance and It starts looking from the second level (but this is not a real problem).
What's the best way to find the element through all the deep of this nested class?
Thanks
It now sounds like you want to get all childs recursively of a certain group.
So you can have:
private IEnumerable<Group> EnumerateChildren(Group parent)
{
if (parent.Groups != null)
{
foreach (var g in parent.Groups)
{
yield return g;
foreach (var sub in EnumerateChildren(g))
{
yield return sub;
}
}
}
}
If you just want to get the parent of a certain group:
private Group GetParent(Group child)
{
_repositoryGroups.Where(g => g.Id == child.ParentId).FirstOrDefault();
}
And if you need to get the super-parent of a certain group (parent of parent of parent of...):
private Group GetSuperParent(Group child)
{
parent = GetParent(child);
while (parent != null)
{
child = parent;
parent = GetParent(child);
}
return child;
}
Above it all, I recommend that if you can do that, hold a reference to the parent instead of it's Id. Have it null if it has no father. Saves a lot of trouble. A lot.
If you want to go up your structure and find the last parent of the parents, you can use this piece of code:
var group = [my Group instance];
while(group.ParentId > 0)
{
group = _repositoryGroups.First(g => g.Id == group.ParentId);
}
This assumes your IDs are higher than zero, and that an id>0 will always have a valid parent.
Sorry guys, maybe I didn't explain my question very well. I've developed this solution inspiring to yours answers:
private static Group GetGroupFather(IEnumerable<Group> groups, Group child)
{
foreach (var group in groups)
{
// try to nested search
var result = GetGroupFather(group.Groups, child);
if (result != null) return result;
// check in the current level
if (group.Id == child.ParentId)
return group;
}
return null;
}
If you have a better solution using linq, please let me know.
I have a class that needs a property set inside a LINQ-to-SQL query. My first attempt was to have a "setter" method that would return the object instance and could be used in my select, like this:
public partial class Foo
{
public DateTime RetrievalTime { get; set; }
public Foo SetRetrievalTimeAndReturnSelf ( DateTime value )
{
RetrievalTime = value;
return this;
}
}
....
from foo in DataContext.GetTable<Foo> select foo.SetRetrievalTimeAndReturnSelf();
Unfortunately, such a query throws an exception like this: "System.NotSupportedException: Method 'Foo.SetRetrievalTime(System.DateTime)' has no supported translation to SQL".
Is there any alternative to converting the result to a list and iterating over it? The query is used in a custom "Get" method that wraps the DataContext.GetTable method, so will be used as the base for many other queries. Immediately converting a potentially-large result set to a list would not be optimal.
UPDATE
Here's a better example of what I'm trying to do, updated with Jason's proposed solution:
protected IQueryable<T> Get<T>() where T : class, ISecurable
{
// retrieve all T records and associated security records
var query = from entity in DataContext.GetTable<T> ()
from userEntityAccess in DataContext.GetTable<UserEntityAccess> ()
where userEntityAccess.SysUserId == CurrentUser.Id
&& entity.Id == userEntityAccess.EntityId
&& userEntityAccess.EntityClassName == typeof ( T ).Name
select new { entity, userEntityAccess };
return query.AsEnumerable ()
.Select ( item =>
{
item.entity.CanRead = item.userEntityAccess.CanRead;
item.entity.CanWrite = item.userEntityAccess.CanWrite;
item.entity.CanDelete = item.userEntityAccess.CanDelete;
return item.entity;
} ).AsQueryable ();
}
public interface ISecurable
{
int Id { get; set; }
bool CanRead { get; set; }
bool CanWrite { get; set; }
bool CanDelete { get; set; }
}
UserEntityAccess is a cross-reference table between a user and a business object record (i.e. an entity). Each record contains fields like "CanRead", "CanWrite", and "CanDelete", and determines what a specific user can do with a specific record.
ISecurable is a marker interface that must be implemented by any LINQ-to-SQL domain class that needs to use this secured Get method.
var projection = DataContext.GetTable<Foo>
.AsEnumerable()
.Select(f => f.SetRetrievalTimeAndReturnSelf());
This will then perform the invocation of SetRetrievalTimeAndReturnSelf for each instance of Foo in DataContext.GetTable<Foo> when the IEnumerable<Foo> projection is iterated over.
What do you need to know the time that object was yanked of the database for? That's potentially smelly.