NHibernate on a table with two "primary" keys - database

I'm learning NHibernate in order to layer it over a rather peculiar legacy database. Other applications use the same live database, so I can't make changes that will affect them.
I've run into a problem because one table, which represents hardware devices, has two columns that are used as de facto primary keys. One is the real primary key, an auto-generated row id. The other is a unique and non-null hardware serial number.
Many other tables in the database have a foreign-key relationship with this table. However, some of them use the real primary key - the integer row id - as a foreign key, and some use the hardware id of the device instead.
Note that in practice a hardware ID and row ID, once paired, will remain paired.
Will I be able to create mappings to deal with this in NHibernate, or will I need to create some views to give me a more standardized schema, and use INSTEAD OF triggers to make them updatable?
The DB in use is MSSQL 2000, in case this makes a difference.

In your situation I would do the following:
public class HardwareDevice{
public virtual int Id {get; set;}
public virtual string SerialNumber {get; set;}
//Other stuff
}
public class DomainThingA {
public virtual int Id {get; set;}
public virtual HardwareDevice Device {get; set;}
//Other stuff
}
public class DomainThingB {
public virtual int Id {get; set;}
public virtual HardwareDevice Device {get; set;}
//Other stuff
}
Map out your HardwareDevice class using the AutoGenerated Id as the primary key.
My examples use FluentNhibernate for the class maps.
public class HardwareDeviceMap : ClassMap<HardwareDevice> {
public HardwareDeviceMap(){
Id(x=>x.Id).GeneratedBy.Native().Column("Id"); //Uses auto number
Map(x=>x.SerialNumber).Column("SerialNumber");
//Other mappings
}
}
Now for mapping out the other two classes:
public class DomainThingAMap : ClassMap<DomainThingA> {
public DomainThingAMap(){
Id(x=>x.Id).GeneratedBy.Native(); //Uses auto number
References(x=>x.Device)
.Column("DeviceId"); //Joins on Id in HardwareDevice Table by default
//Other mappings
}
}
public class DomainThingBMap : ClassMap<DomainThingB> {
public DomainThingBMap(){
Id(x=>x.Id).GeneratedBy.Native(); //Uses auto number
References(x=>x.Device)
.Column("SerialNumber") //Column in DomainThingB Table
.PropertyRef("SerialNumber"); //Joins using SerialNumber column (hardware device table)
//Other mappings
}
}
The Property-Ref feature of the class maps allows you to join on columns which are not the primary key for these types of legacy database purposes.

Related

EF Core - code first - multiple cascade paths

I know a lot has been written about that subject, so let me say first I carefully read first 2 pages on Google about this topic.
Many suggest to put DeleteBehavior.Restrict, and dotnet ef database update does not complain anymore. Problem solved until you try to delete record.
I have exactly the same problem as in this SO question, which is duplicate to this one. Proposed solution in second link is:
You are expected to break the cycle. You can do that by turning off
cascade delete (by including WillCascadeOnDelete(false) in the
respective relationship configuration) for at least one of the
relationships customers->payments or customers->billingCenters.
This is what I research so far.
Now let's get to the problem (again). I have diamond relationship:
All ForeignKeys must be not null. So setting one foreign key to allow null is not an option. I would also like that user can delete:
Parameter-Value-Parts -> No problem here
Parameter-Values -> No problem here
OnDelete(DeleteBehavior.Cascade)
Parameter-Parts -> No problem here
OnDelete(DeleteBehavior.Cascade)
Parameters -> Big problem
To follow answer on this SO question, I could break cycle between Parameter-Parts and Parameter-Value-Parts with OnDelete(DeleteBehavior.Restrict).
This would allow me to delete Parameter, but now I can not delete Parameter-Parts.
I could manually delete all Parameter-Value-Parts before deleting Parameter-Parts, but I would like to avoid manually deletion. Is that possible?
I also read on SO, that all cascade delete should be avoided in application and developer should manually take care of deleting dependent table rows before row deletion. What is consider best practice?
Delete cascade seems easy solution, but I am not searching for an easy solution but the right one. The one that would scale easily on large data model in large application.
I hope I understand your requirements correctly.
If you want a real diamond shape, then it would be required for ParameterValueParts to reference ParameterValues and ParameterParts that in turn reference the same Parameter. This would be modelled by composite keys in SQL:
class Parameter
{
public int ParameterId { get; set; }
}
class ParameterValue
{
// composite key of ParameterId, ValueId with ParameterId also being a foreign key
public int ParameterId { get; set; }
public int ValueId { get; set; }
}
class ParameterPart
{
// composite key of ParameterId, PartId with ParameterId also being a foreign key
public int ParameterId { get; set; }
public int PartId { get; set; }
}
class ParameterValueParts
{
// key
public int Id { get; set; }
// three foreign keys:
// ParameterId as foreign key to Parameter
// ParameterId, ValueId are the composite foreign key to ParameterValue
// ParameterId, PartId are the composite foreign key to ParameterPart
public int ParameterId { get; set; }
public int ValueId { get; set; }
public int PartId { get; set; }
}
This way, you can have many combinations of ParameterValue and ParameterPart but each combination is required to belong to a specific Parameter. With this basic design, I never had any cascade issues in my projects.
Side note: you can configure the ValueId and PartId as DatabaseGeneratedOption.Identity in their respective classes in order to not handle ID values manually. (at least in EF6, I hope EF Core works similar in this regard)

Using proper relations for product and attribute

I am about to implement a database for simple ecommerce platform. I want to implement the following:
Each product belongs to one product category;
Each product category has its own attributes;
Each product has one value for each attribute of this products type.
What relations should I use to store this kind of information?
Here is the logical model -- the way I understood it; you should be able to tweak it.
From this you can derive the physical model and the SQL code. The word KEY here means UNIQUE NOT NULL and you may use them for primary keys. Should you choose to introduce integers as primary keys, make sure you keep these UNIQUE.
Note that everything should be NOT NULL, once you get to the SQL.
Category named (CAT) exists.
Category {CAT}
KEY {CAT}
Attribute named (ATR) exists.
Attribute {ATR}
KEY {ATR}
Category (CAT) has attribute (ATR).
Each category has more than one attribute, it is possible for the same attribute to belong to more than one category.
CategoryAttribute {CAT, ATR}
KEY {CAT, ATR}
Product named (PRD) belongs to category (CAT).
Each product belongs to exactly one category, each category may have more than one product.
ProductCategory {PRD, CAT}
KEY {PRD}
KEY {PRD, CAT} -- seems redundant here, but is
-- needed for the FK from the next table
FOREIGN KEY {CAT} REFERENCES Category {CAT}
Product (PRD) from category (CAT) has attribute (ATR) that belongs to that category.
For each attribute that belongs to a category, that attribute may belong to more than one product from that category.
ProductCategoryAttribute {PRD, CAT, ATR}
KEY {PRD, CAT, ATR}
FOREIGN KEY {PRD, CAT} REFERENCES ProductCategory {PRD, CAT}
FOREIGN KEY {CAT, ATR} REFERENCES CategoryAttribute {CAT, ATR}
I don't know what database platform you are using, but for small numbers of products, and for queries that do not depend on the value of the per-category attributes, I'd use the following strategy:
CREATE TABLE "Category" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE "Product" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"categoryId" INTEGER NOT NULL REFERENCES "Category" ("id"),
"attributes" TEXT NOT NULL
);
In this example, the categories are used mainly to enforce referential integrity and to provide a list of categories for navigation.
The attributes are stored inside the attributes column as JSON (most modern databases tend to support this natively).
If there are any attributes common to all types of products, we'd create specific columns in Product. For example, you could add creationDate, deletionDate, price, or whatnot.
This allows you to perform the typical Select * From Product Where id = #Id to get a specific product and Select * From Product Where categoryId = #CategoryId to get all products in a category.
A creationDate could be useful to sort the products by creation date and take the top N, if necessary, when filtering by category. However with small quantities like thousands of products you might as well get all products by category and do this in code.
Regarding the code aspect, products like Dapper have specific extensions helping you deal with these discriminated unions, but writing code to support it is fairly easy. Here's an how. I'll write pseudo-C#, but I'm sure you can adapt.
We have an abstract class taking care of the Product table rows
public abstract class ProductBase
{
// only the fields in the Product table here
public int CategoryId { get; set; }
protected string Attributes { get; set; }
// serialize extra fields to JSON in Attributes
protected abstract void Prepare();
// load the common fields from a data row
protected static ProductBase(DataRow dr)
{
CategoryId = int.Parse(dr["categoryId"]);
Attributes = dr["attributes"] as string;
}
// save to DB
public void Save()
{
Prepare();
// save to SQL
}
}
We also have specific classes per category which have the extra attributes and handle serialization and deserialization.
public class FooProduct: ProductBase
{
public string Color { get; set; }
protected override void Prepare()
{
Attributes = Json.Serialize(new { Color });
}
public FooProduct(DataRow dr): base(dr)
{
// we can only create foo products if the category is foo
if (CategoryId != 23) throw new InvalidOperationException();
var attr = Json.Deserialize(Attributes);
Color = attr.Color;
}
}
This idea works great while you don't need to get the "foo" products by Color. If you can afford to get all "foo" products and filter in code, great. If your database understands JSON and lets you query inside the Attributes field, good it will get slow with large numbers unless the server allows indexes to reference JSON-serialized values.
If all else fails, you'll need to create an index table which contains the color values and the ids of the products which have that color. This is relatively painful and you don't want to do it unless you need it (and you don't right now).

Database: item can have alternatives, how to link in database?

I have a database where I want to store multiple items, every item would be unique, but they can have alternatives from other makers. So items would have a potential M:M relationship with each other.
For example, item A could have 3 alternatives. If I add item B, the alternative, the link for the alternative item should also be reversed, so that when I search item B, I would find item A as an alternative. When I add item C, it should be added as an alternative for both item A and B, and C should have as it's alternatives item A and B.
How's the best/smartest way to make this relationship between a class of its own kind?
The way I'm thinking is making a table in between called alternatives with a unique ID for each new item, if an alternative is added, then it's linked to that parent ID (alternative ID) and thus no new ID would be made in the alternative table. If it's established only later that this is an alternative, then remove the alternative ID of one of the two and add the item to the other alternative ID.
public partial class Item : Entity
{
public Item()
{
Id = GuidComb.GenerateComb();
}
public Guid Id { get; set; }
public string ItemName { get; set; }
public string MakerRef { get; set; }
public string Description { get; set; }
public virtual Maker Maker { get; set; }
public virtual IList<Offer> Offers { get; set; }
//stuck here on the smart way to make the relationship with itself?
}
Any advice would be welcome!
Thanks in advance!
The suggestion I'm thinking about might be similar to what you proposed, but I'll try to formulate it in database terms.
As far as I understand your requirements, your alternatives relationship will be completely transitive. This means your item set is partitioned in equivalence classes of subsets containing mutually alternative items. (If an item has no alternative yet, the subset consists of this item alone.)
If that's true, then the most elegant and redundancy free way to represent this is to choose one of the items of such as subset as a representative of the entire subset. This is reflected by the following table design:
item(id, equivalence_id, other attributes, ...)
where equivalence_id is a foreign key to the representative. Each item gets born with an equivalence id of null. If it is made equivalent to another item,
if the item already present has an equivalence id of null, assign the id of this representative to the equivalence it of both items,
if the item already present has a non-null equivalence id, assign this to the equivalence id of the new item.
Note that this works, in case there are many items in the same equivalence class, no matter which of this items are used to link the new one.
Example:
id equivalence_id name
1 1 abc
2 def
3 1 ghi
4 4 jkl
5 4 mno
This means abc and ghi are equivalent, as well as jkl and mno, but def isn't yet equivalent to anything. Now if pqr comes along and should become equivalent to abc, it would get equivalence id 1. The effect is the same as making it equivalent to ghi.
To find all items equivalent to a specific one, query
select *
from item
where equivalence_id = :my_equivalence_id
If some information pertaining to the equivalence class as a whole should be stored, a separate table for the equivalence classes only should be created.

Entity Framework 6 Code First approach - unique constraint doesn't work

I am using EF 6 with code first approach. In addition to database constraints check at database level, I would like to provide unique constraints check at POCO level. I have followed article Setting unique Constraint with fluent API? where someone suggested to use [Index()] attribute. I have applied the same in my poco class but looks like it is still throwing an exception from Database level.
Here is my code:
[Key]
public decimal OrderId { get; set; }
[Index("ORDER_CC", 2, IsUnique = true)]
public decimal? MemberId { get; set; }
[Index("ORDER_CC", 3, IsUnique = true)]
public decimal? ItemId { get; set; }
[Index("ORDER_CC", 1, IsUnique = true)]
public decimal? OrderNumber { get; set; }
public decimal? Cost { get; set; }
public DateTime Time { get; set; }
I am not sure what I am doing wrong here?
I also want to ask followings:
Do I need to keep the same index name as the one I have on my database table for Order?
How do I know, it is getting validate against EF constraints check or Database constraints check?
I have kept the same order for index on POCO class compare to one defined in Order table in database.
Here is my Database script:
CREATE UNIQUE INDEX xyz.ORDER_CC ON xyz.Order
(OrderNumber, MemberId, ItemId)
LOGGING
TABLESPACE USERS
PCTFREE 10
INITRANS 2
MAXTRANS 255
STORAGE (
INITIAL 128K
NEXT 1M
MINEXTENTS 1
MAXEXTENTS UNLIMITED
PCTINCREASE 0
BUFFER_POOL DEFAULT
FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT
)
NOPARALLEL;
Any idea?
The problem is not in the Index attribute, it's in your expectations of what it does.
The Index attribute doesn't validate a unique constraint, it only instructs Entity Framework code-first to create an index in the database during a migration. From your questions it is clear that you use EF code-first, i.e. POCO's and coded mappings, but you work with a preexisting database. In this case, applying index attributes is useless.
So that also answers your question where the constraint is checked: in the database. And that's why it's still throwing errors. If you want a unique constraint in code you'll have to write it yourself.
Update
I have posted about this at code project
In general, it depends on the attributes that you put on the classes to generate your unique indexes
Thanks to Shimmy in this answer
https://stackoverflow.com/a/15999805/105445
You can do it yourself by creating a custom ValidationAttribute to validate the property in a generic way. Then you can use it like this:
[ShouldBeUnique()]
public string SomeProperty { get; set; }
Take a look at this example
http://blogs.microsoft.co.il/shimmy/2012/01/23/uniqueattribute-that-validates-a-unique-field-against-its-fellow-rows-in-the-database-inherits-dataannotationsvalidationattribute/

Using NHibernate with lookup tables

If you have a set of tables in the database that strictly consist of a string description and an ID, what is the best way to load these via NHibernate?
So suppose I have a Sandwich class, and a sandwich has a meat, a cheese, and a vegetable, where those three things are lookup tables in the DB. It seems to be the most conformant to NHibernate philosophy to have
public class Meat { string name; int id; }
public class Cheese { string name; int id; }
public class Vegetable { string name; int id; }
public class Sandwich { Meat meat; Cheese cheese; Vegetable vegetable; }
But with a few dozen tables like this in the database, classes seem to proliferate quickly. Suppose I set it up like this:
public class NameAndID { string name; int id; }
public class Sandwich { NameAndID meat; NameAndID cheese; NameAndID vegetable; }
Is this feasible? How would it be implemented in Fluent NHibernate?
You'd need another column to determine the type. You could use an enum for that. Then all your lookups need to include that restriction....
CreateCriteria<NameAndID>.Add(Restrictions.Eq("ntype", E.Meat)
However, I'd prefer separate tables so you can have better foreign keys. Otherwise there is nothing in database constraints to stop you from making a sandwich that is simply 3 pieces of cheese.

Resources