Reputation: 1043
I am using Shaolinq library for database usage and I have problem with one-to-one relation. I have following database schema:
create table A (
Id INT NOT NULL,
PRIMARY KEY (`Id`)
);
create table B (
Id INT NOT NULL,
PRIMARY KEY (`Id`),
CONSTRAINT `fk_a_b_id` FOREIGN KEY (`Id`) REFERENCES `A` (`Id`)
);
basically those two tables contains info about same things, just different data. That's how someone has designed it, I cannot change it.
However, in the code I would like to have all those data in one object. I have been trying with BackReference, but no luck:
[DataAccessObject]
public abstract class A : DataAccessObject<uint>
{
[AutoIncrement]
[PersistedMember]
public abstract uint Id { get; set; }
[RelatedDataAccessObjects]
public abstract RelatedDataAccessObjects<B> Bs { get; }
}
[DataAccessObject]
public abstract class B : DataAccessObject
{
[BackReference(Name = "", PrefixName = "", SuffixName = "")]
public abstract A Id { get; set; }
[PersistedMember]
public abstract string MoreData { get; set; }
}
This created somehow similar schema (obviously Id in B is not primary key now), but after I fill data in DB, Bs property in A is not filled with references. Simply puting abstract property of type B in A also doesn't seem to work since same column is foreign key and primary key at once (Id).
Any advice is welcome.
Upvotes: 0
Views: 79
Reputation: 1162
Do you need to access a collection of Bs from As? It seems to me that B is the "super class" that encompasses data from both A
and B
. If that's the case, then you can just make A
the primary key of B
.
[DataAccessObject]
public abstract class A : DataAccessObject<uint>
{
[PersistedMember(Name = "Id")]
public abstract override uint Id { get; set; }
[PersistedMember]
public abstract string Data { get; set; }
}
[DataAccessObject]
public abstract class B : DataAccessObject<A>
{
[PersistedMember(Name = "Id", PrefixName = "")]
public abstract override A Id { get; set; }
[PersistedMember]
public abstract string MoreData { get; set; }
}
Note that I've overridden the Id
properties to get the column names matching the SQL. The defaults have meant column names: AId
and BId
Because the primary key is an object it's known in Shaolinq terminology as a "complex primary key". You can use the deflated references feature to retrieve objects just be their primary key without a roundtrip to the database (more here: https://github.com/tumtumtum/Shaolinq/wiki/Deflated-References)
You can query for a specific value of B:
// Need to define primary key using an anonymous object because it's not an uint but rather an A that contains an uint.
model.Bs.GetByPrimaryKey(new { Id = 100 } );
You can also query for a specific value of B using a deflated reference:
model.Bs.GetByPrimaryKey(model.As.GetReference(100));
You can also use references within normal LINQ queries:
model.Bs.Where(c => c.Id == model.As.GetReference(100) || c.Id == model.As.GetReference(101)).ToList();
You can directly access nested/complex primary keys within the query too:
model.Bs.Where(c => c.Id.Id == 100 || c.Id.Id == 101).ToList();
Note that the above three queries don't need to perform an implicit join be cause the complex primary key is flattened out and stored on the table B
.
When you get back B
, you can access B.Id.Id
without a round-trip query but if you access B.Id.Data
then that will cause B.Id
to automatically inflate with a hidden round-trip to the database. To avoid this, you can use Include
to automatically inflate B.Id
with the first query.
model.Bs.Include(c => c.Id).Single(c => c.Id == model.As.GetReference(100));
Or more succinctly:
model.Bs.Include(c => c.Id).Single(c => c.Id.Id == 100);
Upvotes: 1