Given the example at http://notherdev.blogspot.com/2012/01/mapping-by-code-inheritance.html
I have a base class Party and two concrete classes (Company, Person)
I would like to use Table Per Hierachy (Single Table), but my ids are only unique per concrete type.
i.e. Company and Person may have matching Id's
Is there any way to include the discriminator value in the Id as a composite id?
But still be able to call Get<>(id)?
How about this (Fluent):
public class PartyMap : ClassMap<Party>
{
public PartyMap()
{
Table("Parties");
CompositeId<CompositeIdType>(x => x.Id)
.KeyProperty(x => x.IdCompositePart)
.KeyProperty(x => x.Discriminator);
DiscriminateSubClassesOnColumn("Discriminator");
}
}
Related
I'm using code first to create my DB but have to match a given schema.
I have an Org entity (simplified here) an Org can have many children of type org
public class Org
{
public int Id {get;set;}
public virtual IList<Org> Children{get;set;}
}
I want to generate an Org table and a table called OrgRelationship which has two columns ParentId and ChildId. The data is being provided to us in that format but I'd really like EF to expand the table into this model...
Is it possible to generate this join table just with an EntityTypeConfiguration on model builder? Do I have to have an OrgRelationship class?
This doesn't seem to cut it
public class OrgMap : EntityTypeConfiguration<Org>
{
public OrgMap()
{
HasKey(n => n.Id);
HasMany(n => n.Children);
}
}
You can configure the join table using the following calls.
HasMany(n => n.Children).WithMany().Map(
m => m.ToTable("OrgRelationship").MapLeftKey("ParentId").MapRightKey("ChildId"));
Suppose say I have Order, Items, OrderItems tables with Order and Items having n:n relationship and OrderItems being associative table. I have seen below two approaches for defining them.
1.Create Order and Items entities with "HasMany" relationship with OrderItems.
2.Create Order, Items and OrderItems entities with Order and Items having "ManytoMany" relationship and "OrderItems" containing Order and Item properties.
I have approach 1 which works fine but would like to know what approach 2 does.
If the relationship between Items and Orders is simple (merely that the relationship exists), then you would do a ManyToMany mapping between Items.Orders and Orders.Items. This would result in NHibernate generating and managing a simple cross reference table containing the two foreign keys.
On the other hand, if there is additional information that you need to record along with the two foreign keys, you must use a distinct entity or value object to capture that information, using HasMany on both sides.
Classes:
Order
Id
Name
OrderItems
Item
Id
Name
OrderItems
OrderItem
Id
Order
Item
Quantity
Mappings:
Order:
Id(c => c.Id);
Map(c => c.Name);
HasMany(c => c.OrderItems).KeyColumn("OrderId");
Item:
Id(c = c.Id);
Map(c => c.Name);
HasMany(c => c.OrderItems).KeyColumn("ItemId");
OrderItem:
Id(c => c.Id);
Map(c => c.Quantity);
References(c => c.Order);
References(c => c.Item);
I'm modeling accounting where I have accounts with transactions that debit one account and credit another.
Here are the details of the situation (simplified). My tables (in SQL Server 2008) are:
CREATE TABLE Account
(
AccountID int IDENTITY(1,1) NOT NULL,
AccountNumber nvarchar(10) NOT NULL
)
CREATE TABLE [Transaction]
(
TransactionID [bigint] IDENTITY(1,1) NOT NULL,
DebitAccountID [int] NOT NULL,
CreditAccountID [int] NOT NULL,
Amount money NOT NULL
)
My classes are:
public class Account
{
public int Id { get; set; }
public string AccountNumber { get; set; }
public IList<Transaction> Transactions { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public Account DebitAccount { get; set; }
public Account CreditAccount { get; set; }
}
So the question is "How do I map the Transactions collection in the Account class using fluent NHibernate?"
What I want (for performance reasons) is for the accessing of the transactions collection to execute the query:
SELECT ...
FROM [Transaction]
WHERE DebitAccountID=#accountID OR CreditAccountID=#accountID
The important part there is the OR in the where clause.
So the code I need is:
public class AccountMap : SubclassMap<Account>
{
public AccountMap()
{
Id(x => x.Id).Column("AccountID");
Map(x => x.AccountNumber);
HasMany(x => x.Transactions)
// What goes here to explain this mapping to NHibernate?
.Inverse().Cascade.AllDeleteOrphan()
.Access.CamelCaseField();
}
}
Note: I am aware that I could map the transactions as two separate collections, one for "debits" and the other for "credits". That is not an acceptable answer because of performance issues. In particular, there is actually a second type like this related to the account (resulting in even more queries) and mapping as two collections prevents the use of eager loading with Fetch(). The second type is PaymentScheduleLine which contains a plan of all the correct payment transactions over the life of the account. It is associated to the account in the same way as transaction i.e. PaymentScheduleLine has a DebitAccount and CreditAccount and Account has a PaymentSchedule collection. Typically, complex calculations involve the relationship between the transactions and the payment schedule.
I might revise my answer once I hear more about this "second type like this related to the asset," but for now...
Decide what's important and be willing to make sacrifices.
It sounds like performance is your primary concern here, right? You might have to relax your demands that the domain model not be changed or that the query look a certain way.
Batch queries using .Future() to avoid Cartesian products.
You're right that having collections named Debits and Credits could lead to performance problems. Maybe this is the query you're thinking of:
// BAD QUERY - DO NOT USE - cartesian product of rows - Debits X Credits.
var account = session.QueryOver<Account>()
.Fetch(x => x.Debits).Eager
.Fetch(x => x.Credits).Eager
.Where(x => x.Id == accountId)
.SingleOrDefault();
If the account had 1,000 transactions, 500 of them being debits, and 500 credits, then this query would result in 250,000 rows (500 * 500), which is clearly unacceptable!
You don't have to write the query that way, though. This one is better:
var futureAccount = session.QueryOver<Account>()
.Fetch(x => x.Debits).Eager
.Where(x => x.Id == accountId)
.FutureValue();
session.QueryOver<Account>()
.Fetch(x => x.Credits).Eager
.Where(x => x.Id == accountId)
.Future();
var account = futureAccount.Value;
Even though this is really two queries, it will be executed in one round-trip to the database, with only 1,000 rows returned (500 + 500). NHibernate will reuse the same Account instance for the two queries, and just populate whatever data was eagerly fetched. The result will be an Account with fully populated Debits and Credits.
Only fetch the data you need.
Now, 1,000 rows is still a lot. Are you absolutely sure you need to load all of the transactions for a given account? What are you using them for? For the scenario you mentioned, calculating how much money moved from account A to account B over a given timeframe, you would achieve much better performance if you wrote a query to calculate exactly that, or at the very least, only loaded the transactions you were actually interested in using a more specific query. A query like this...
var transactions = session.QueryOver<Transaction>()
.Where(x => x.DebitAccount.Id == fromId
&& x.CreditAccount.Id == toId
&& x.Date >= startDate
&& x.Date < endDate.AddDays(1))
.List();
... could easily cut the number of transactions you're working with from 1,000 to 20 or less.
Here's my table structure
Places
PlaceId PK
Name
...
PlaceCategories
CatId PK
Name
...
PlaceCats
PlaceId PK
CatId PK
Here's my query that pulls Places based on category id (table join)
public static IQueryable<Places> ByPlaceCat(this Table<Places> table, Expression<Func<PlaceCats, bool>> predicate) {
var db = (DataContext)table.Context;
var innerBizBase = db.PlaceCats.Where(predicate);
return db.Places.Join(innerBizBase, a => a.PlaceId, ab => ab.PlaceId, (a, ab) => a);
}
I use it like this:
places = Db.Places.ByPlaceCat(a => a.CatId == 5);
But I want to be able to pull based on a List<int> of category id's. Looking through the generated PLINQO code, a query that pulls by multiple PlaceId's (but not using a joined table) looks like this:
public static IQueryable<Places> ByPlaceId(this IQueryable<Places> queryable, IEnumerable<long> values)
{
return queryable.Where(p => values.Contains(p.PlaceId));
}
How could I essentially merge those two queries, to let me pass in a List<int> of CatId's to query by? This LINQ/PLINQO query is melting my brain. Thanks in advance!
You would need to write a extension method like this:
public static IQueryable<Places> ByPlaceCats(this Table<Places> table, IEnumerable<int> catIds)
{
var db = (TestDataContext)table.Context;
var places = (from placeCat in db.PlaceCats
join place in db.Places on placeCat.PlaceId equals place.PlaceId
where catIds.Contains(placeCat.CatId)
select place);
return places;
}
Please note that the PlaceCats table could be made into a ManyToMany relationship by adding two foreign keys to the proper tables. Once this change has been made than PLINQO will automatically generate the correct code and will create a link between the two tables skipping the intermediary table. So you could get a collection of PlaceCategories associated to the current Places entity by accessing a property on the Places entity.
Please remember to contact us if you have any questions and be sure to check out the community forums located here and PLINQO forums here.
Thanks
-Blake Niemyjski (CodeSmith Support)
I have a database that has a one-to-one relationship modeled between a Person and a Address (that uses person id). However, I cannot find a way to make the map using NHibernate.
My table structure is the following:
PersonTable
PersonId
PersonName
PersonAge
AddressTable
PersonId
CountryName
StreetName
StateName
And I would like to have something like this as the final class:
PersonClass
int Id
string Name
int Age
Address HomeAddress
AddressClass
string Street
string Country
string State
Person Owner
I tried with HasOne relationship but I couldn´t reuse PersonId as Address Identifier.
Thanks!
Edit: I forgot to mention that I´m using FluentNHibernate so both fluent mapping and XML will be fine.
The problem is that your database schema does not represent a "Has One" relationship between Person and Address. It represents a "Has Many" relationship; You may be artificially limiting it to be one address per person, but that doesn't change the fact that the model is multiple addresses per person.
To get a "Has One" relationship, you would put the AddressID on the the PersonTable.
I did it using Id().GeneratedBy.Foreign() in Address class map referencing it to the Person´s ID and it worked.
Thanks!
I would map it as a component of Person. In the person ClassMap add the following:
Component(x => x.Address, m =>
{
m.Map(x => x.Street, "Street");
m.Map(x => x.State, "State");
// more here
});
Cheers