I have the following model:
public class SomeObject1 {
public virtual Guid Id {get; set; }
public string Property1 {get; set; }
}
public class SomeObject2 {
public virtual Guid Id {get; set; }
public string Property2 {get; set;}
}
and the table
SOME_OBJECTS
PK_SOME_OBJECTS Guid
WHICH_OBJECT Integer
PROPERTY1 varchar2
PROPERTY2 varchar2
when the WHICH_OBJECT column = 1 the row contains information for SomeObject1, when the WHICH_OBJECT column = 2 the row contains information for SomeObject2.
How would I go about doing these mappings? I've found the discriminator feature but it seems to only apply when you have subclasses in an inheritance hierarchy.
I'm pretty sure it's not possible to map two unrelated entities to the same table; however, you may be able to map them to two different views that reference the same table.
Related
Using NHibernate 3.3.1.400, I'm having problems expressing what is a simple SQL statment using NHibernate's Linq provider.
My domain model looks like this:
public class Site
{
public virtual Guid Id {get; set;}
public virtual string Name {get; set;}
}
// a site has many filers
public class Filer
{
public virtual Guid Id {get set;}
public virtual Site Site {get; set;}
public virtual string Name {get; set;}
}
// a filer has many filings
public class Filing
{
public virtual Guid Id {get set;}
public virtual Filer Filer {get; set;}
public virtual DateTime FilingDate {get; set;}
}
//a filing has many items
public class Item
{
public virtual Guid Id {get set;}
public virtual Filing Filing {get; set;}
public virtual DateTime Date {get; set;}
public virtual decimal Amount {get; set;}
}
public class SearchName
{
public virtual Guid Id {get set;}
public virtual string Name {get; set;}
}
// there are potentially many NameLink objects tied to a single search name
public abstract class NameLink
{
public virtual Guid Id {get set;}
public virtual SearchName SearchName {get; set;}
}
public class NameLinkToFiler: NameLink
{
public virtual Filer Filer {get; set;}
}
public class NameLinkToItem: NameLink
{
public virtual Item Item {get; set;}
}
My query is supposed to return a list of matching Item elements:
var query = session.Query<Item>()
.Where(x => x.Filing.Filer.Site == mySite);
The joins connecting Site -> Filer -> Filing -> Item are working great through my mappings, but the problems start when I try to join the NameLinkToFiler or NameLinkToItem classes based on user input.
If the user wants to filter the Query results with a filer name, I want to join the results of the Item query with the results of this query:
var filerNameQuery = session.Query<NameLinkToFiler>()
.Where(q=>q.SearchName.Contains('some name'));
I want the results of the NameLinkToFiler.Filer property to join the Item.Filing.Filer property, so my list of returned items is reduced.
Note: The 'Contains' keyword above is a full-text index search I'm using as described here. It's working fine, and let's just assume the filerNameQuery is an IQueryable<NameLinkToFiler>.
It's pretty easy to do this in straight SQL:
select filer.Name, filing.FilingDate, filer.Name, item.Date, item.Amount
from Search_Name searchNameForFiler, Search_Name searchNameForItem, Name_Link_Filer nameLinkFiler,
Name_Link_Item nameLinkItem, Item item, Filing filing, Filer filer, Site s
where
contains(searchNameForFiler.Name, :filerName) and searchNameForFiler.Id = nameLinkFiler.SearchNameId and nameLinkFiler.FilerId = filer.Id and
contains(searchNameForItem.Name, :itemName) and searchNameForItem.Id = nameLinkItem.SearchNameId and nameLinkItem.ItemId = item.Id
and item.FilingId = filing.Id
and filing.FilerId = filer.Id
and filing.SiteId = :site
...but I don't want to lose the compile-time checks for this sort of query.
Thanks.
Apparently, the answer is to not use lambda syntax.
This works fine:
query = from t in parentItemQuery
join l in Session.Query<NameLinkToFiler>() on t.Filing.Filer.Id equals l.Filer.Id
join n in Session.Query<SearchName>() on l.SearchName.Id equals n.Id
where sn.Contains(request.FilerName)
select t;
i have a legacy database (which is still used by another legacy application) where the group is denormalized and duplicated into the child rows
table parent
(
id
)
table child
(
id
parent_id
group_id
group_name
group_Flag
group_type
name
)
and i would like to map them to
class Parent
{
public long Id { get; private set; }
public ICollection<Group> Groups { get; private set; }
}
class Group
{
public long Id { get; set; }
public string Name { get; set; }
public GroupType Type { get; set; }
public bool Flag { get; set; }
public ICollection<Child> Childs { get; private set; }
}
class Child
{
public long Id { get; private set; }
public string Name { get; set; }
}
Is this possible?
How to do that in any of NHibernate's mapping methods (xml, MbC, Fluent, ...)
Update: Some additional info
the schema can't be changed because of the legacy application
additional views in the database are an option
leaking in the class model is possible
Here are some starters :
You could try to have a look at the "mapping collections" section of NHibernate reference :
- http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-collections
Then, you could try to map the Groups property of Parent class using the "where" clause of the collection mapping :
(9) where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving or removing the collection (useful if the collection should contain only a subset of the available data)
And map the Childs property of the Group class the same way.
Otherwise, you could create some views to present your data differently, and map your objects to those views (setting update="false" and insert="false" to your identifier property mapping)
I have next entities:
class Topic
{
public virtual int Id {get; private set;}
public virtual ICollection<Vote> Votes {get; private set; }
}
class Vote
{
public virtual Topic Topic {get; private set;}
public virtual VoteType VotedTo {get; private set;} // enum VotedUp, VotedDown
}
I need to load from the db the next info - all topics (IDs, actually Names, but in this demo case it does not matter) and two more fields CountOfVotedUp, CountOfVotedDown (aggregates). So as I understand in SQL world we need joins, group by, case and count.
Is it posible to get this info with LINQ with less operations with db? I mean N+1, additional selects, connections etc.
All that I tried - is to use NH's LINQ, but it's Query aggregates only on Topic.Id and I could not count any of Votes collection.
Provided you have a summary class to store your result :
public class SummaryDTO
{
public int TopicId { get; set; }
public VoteType TypeOfVote { get; set; }
public int VoteCount { get; set; }
}
then
Vote voteAlias = null;
SummaryDTO result = null;
youNHSession.QueryOver<Topic>()
.JoinAlias(x=> x.Votes, ()=>voteAlias)
.SelectList(
list=>list
.SelectGroup(topic=>topic.Id).WithAlias(()=>result.TopicId)
.SelectGroup(()=>voteAlias.VotedTo).WithAlias(()=>result.TypeOfVote)
.SelectCount(()=>voteAlias.VotedTo).WithAlias(()=>result.VoteCount ))
.TransformUsing(Transformers.AliasToBean<SummaryDTO>())
.List<SummaryDTO>();
I guess that's not exactly what you are looking for, but hope this will set you on a good track.
I've got a question that may reek of code/design stink but I'm going to ask it anyway.
Say I've got the two trivial classes below:
public class PostOffice
{
public virtual string ZipCode {get; set;}
public virtual string StreetAddress {get; set;}
}
public class Person
{
public virtual in ID {get; protected set;}
public virtual string FirstName {get; set;}
public virtual string LastName {get; set;}
public virtual PostOffice ZipCode {get; set;}
}
and I've used FluentNHibernate to set PostOffice.ZipCode as the ID and provided the mapping override below:
model.Override<Person>(p=> p.References( z=> z.ZipCode, "ZipCode"));
Person.ZipCode in the database is just a VARCHAR and I would normally write some SQL like
SELECT *
FROM Person
WHERE ZipCode = '90210'
but if I have an NHibernate session and do something like the following:
session.Query<Person>().Where(p=>p.ZipCode == '90210')
the resulting SQL (pretty-printed) looks like
SELECT p.*
FROM Person p INNER JOIN PostOffice o
ON p.ZipCode = o.ZipCode
WHERE o.ZipCode = '90210'
My question - if entities are lazy-load by default, is it possible to specify some mapping properties such that NHibernate will generate the SQL without the join? The example is trivial but in my actual situation, the table being joined to has millions of rows so I'd like to avoid the join if possible.
Thanks in advance!
If the fields ZipCode and StreetAddress are actually in the Person table then you need to map PostOffice as a Component and not a Reference.
http://wiki.fluentnhibernate.org/Auto_mapping#Components
Edit:
You may be able to map a separate property to just that ZipCode field.
public class Person
{
public virtual in ID {get; protected set;}
public virtual string FirstName {get; set;}
public virtual string LastName {get; set;}
public virtual string ZipCode { get; set; }
public virtual PostOffice PostOffice {get; set;}
}
model.Override<Person>(p=>
p.References( z=> z.PostOffice, "ZipCode")
.Not.Update()
.Not.Insert()
);
I'm having trouble getting the Fluent Nhibernate Automapper to create what I want. I have two entities, with a one-to-many relationship between them.
class Person
{
public string name;
IList<departments> worksIn;
}
class Department
{
public string name;
}
The above is obviously bare bones, but I would be expecting to generate the fleshed out schema of:
Person{id, name}
Department{id, name}
PersonDepartment{id(FK person), id(Fk Department)}
Unfortunately, I am instead getting:
Person{id, name}
Department{id, name, personid(FK)}
I don't want the FK for Person included on the department table, I want a separate join/lookup table (PersonDepartment above) which contains the primarykeys of both tables as a composite PK and also Fks.
I'm not sure if I am drawing up my initial classes wrong (perhaps should just be LIst workIn - representing ids, rather than List worksIn), or if I need to manually map this?
Can this be done?
The way the classes have been structured suggests a one-to-many relationship (and indeed that's how you describe it in your question), so it should not be a surprise that FNH opts to model the database relationship in that way.
It would be possible, as you suggest, to manually create a many-to-many table mapping. But, is this definitely what you want?
I tend to find that pure many-to-many relationships are quite rare, and there is usually a good case for introducing an intermediate entity and using two one-to-many relationships. This leaves open the possibility of adding extra information to the link (e.g. a person's "primary" department, or perhaps details of their office within each of their departments).
Some example "bare-bones" classes illustrating this kind of structure:
public class Person
{
public int Id { get; set;}
public string Name { get; set;}
public IList<PersonDepartment> Departments { get; set; }
}
public class PersonDepartment
{
public int Id { get; set; }
public Person Person { get; set; }
public Department Department { get; set; }
public bool IsPrimary { get; set; }
public string Office { get; set; }
}
public class Department
{
public int Id { get; set; }
public IList<PersonDepartment> Personnel { get; set; }
public string Name { get; set; }
}