NHibernate dictionary mapping without primary key - c#

Question
How to map dictionary collection cross database with the same .hbm configuration ?
Scenario
I am trying to map a dictionary property:
Dictionary<string, string> Phrases { set; get; }
with the following .hbm configuration:
<map
name="Phrases"
cascade="save-update"
table="ATTRIBUTE_LOCALE"
lazy="true">
<key column="RESOURCE_ID" /> <!-- foreign key -->
<index column="LOCALE_NAME" type="string" />
<element column="PHRASE" type="string" />
</map>
and the following is the table create SQL of [ATTRIBUTE_LOCALE] for MS SQL:
CREATE TABLE ATTRIBUTE_LOCALE (
CUID int IDENTITY(1, 1) NOT NULL,
RESOURCE_ID int NOT NULL,
FIELD_NAME nvarchar(255) DEFAULT 'VALUE' NOT NULL,
LOCALE_NAME nvarchar(255) NOT NULL,
PHRASE ntext NULL
);
but if I change my database from MS SQL Server to Oracle and Oracle database cannot use IDENTITY(1, 1) to generate the primary key automatically. In Oracle, I will have some trouble due to insert table with NULL Primary Key.
How can I solve this problem?

You could map the ATTRIBUTE_LOCALE table with a native generator rather than identity:
For cross-platform development, the native strategy will choose from the identity, sequence and hilo strategies, dependent upon the capabilities of the underlying database (ref).
Oracle doesn't support the notion of an identity column. If you're using SQL Server 2012+, you can use sequences rather than identities. This would be more consistent with Oracle and is, arguably, a better key generation strategy ORM-wise.

My colleague recommend me to adjust the table schema.
Remove column CUID and FIELD_NAME
Let RESOURCE_ID and LOCALE_NAME be the composite key.
Let RESOURCE_ID be composite key and foreign key at the same time.
Indeed, above approach can solve my problem, but I don't this data schema design is good or not.
I would appreciate it if somebody have any advice for me about this data schema design.

Related

How to ignore part of a composite key in Entity Framework

I've been tasked with creating a windows service that transforms and migrates data from one database to another. The database that I am pulling from was developed by one of our sub-departments, and I have no say in the structure of that database.
My problem is that the database that I'm pulling from has some intrinsic flaws that I'm having trouble working around. The biggest one is that on many tables, the primary key is set up as a composite primary key... this wouldn't be that big of an issue, but the other tables that are supposed to be associated only have part of the composite key to reference.
For example, 'People' has ID and Surname as a composite key. ID is a surrogate auto increment integer key, and Surname is a varchar. The ID column alone is enough to uniquely identify a person, but for some reason they also included the surname in the composite key. 'Project' is supposed to reference 'People' to signify who the project is assigned to... but 'Project' just has 'PersonID', not the surname.
I was looking at their database and Access front end, and there is no actual relationship defined between the two.... it's just logically maintained via their front end.
So, how can I add my own navigation property if the table I'm joining from doesn't have all of the columns it needs as specified by the other table's composite primary key? Technically, I have the ID number, which is all I need to logically make the association, but entity framework requires that all of the composite key columns are mapped in the referential constraint.
I could ignore the navigation property and have my program add logic to get around the gaps, but lazy loading means that I would have to build my queries in stages then.
Is there some way to ignore a column of a composite key if it doesn't meaningfully contribute to the identity of the table?
EDIT
I should also note that I will never be writing to their database. I'm only going to be querying their data. The user account I'm connecting with has only been granted select permissions.
EF doesn't have to know about the composite keys. If you know for sure that a column is unique, you can fool EF and only tell it about the key field that matters. This requires some work though.
As you're working against an existing database I think the database-first approach will suit you best. Even then, there's a lot to do, but I think this is easier in an EDMX diagram then in manually typed code.
If you generate a model from your database the first thing you'll notice is that EF does not generate all associations in the EDMX. This is because EF only generates associations that involve the whole key (and nothing but the key) in the database.
Let me show this is in a highly simplified data model based on your example. After generating an EDMX model from this data model the diagram looks like this:
Person has a composite key and there's no association between Person and Project, although the FK is in the database. Now we're going to deceive EF in the following steps:
Remove Person.name from the primary key: right-click Name and uncheck "Entity Key".
Add an association between Person and Project: right-click Person, choose Add New, Association... Fill out the dialog as shown here:
Note that "Add foreign key properties..." is not checked. If it is, EF will add a new PersonId1 field.
Define the foreign key manually: right-click the association and choose "Properties".
In the Properties pane, click the ellipses at the right of "Referential Constraint".
Fill out the dialog:
Now the diagram should look like this:
Done!
Or...? When you build the project or validate the diagram you will see an error appear:
Error 3003: Problem in mapping fragments starting at line 74:All the key properties (People.Id) of the EntitySet People must be mapped to all the key properties (Person.Id, Person.Name) of table Person.
EF isn't fooled that easily. We modified the conceptual model. Not the store model. EF still knows that Person has a composite key in the database. How can we fix that? Unfortunately (or fortunately?) there is no support for that in the EDMX designer. We have to edit the XML.
The first part of the XML contains the SSDL content, the store schema definition. Look for the store definition of Person, which should resemble this:
<EntityType Name="Person">
<Key>
<PropertyRef Name="Id" />
<PropertyRef Name="Name" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Name" Type="nvarchar" MaxLength="50" Nullable="false" />
</EntityType>
Remove the line
<PropertyRef Name="Name" />
Now the deception is complete, EF is happy, the model validates.
I must admit that I don't like this last part. It's a hack, whichever way you look at it. And when you update the model from the database all these carefully applied modifications in the store model are gone.
You won't have this problem if you work code-first. You could re-engineer the model from the database, using Entity Framework Power Tools or the new integrated "add new model" dialog in VS2013, and manually add all associations and remove all redundant keys. I think it's a lot more work, but at least it's "update-safe".

Approach for primary key generation

What is the best approach when generating a primary key for a table?
That is, when the data received by the database is not injective and can't be used as a primary key.
In the code, what is the best way to manage a primary key for the table rows?
Thanks.
First recommendation stay away from uniqueidentifier for any primary key. Although it has some interesting easy ways to generate it client side, it makes it almost impossible to have any idexes on the primary key that may be useful. If I could go back in time and ban uniqueidentifiers from 99% of the places that they have been used, this would have saved more than 3 man years of dba/development time in the last 2 years.
Here is what I would recommend, using the INT IDENTITY as a primary key.
create table YourTableName(
pkID int not null identity primary key,
... the rest of the columns declared next.
)
where pkID is the name of your primary key column.
This should do what you are looking for.
AUTO_INCREMENT in mysql, IDENTITY in SQL Server..
IDENTITY in SQL Server
and if you need to get know what you new ID was while INSERT-ing data, use OUTPUT clause of INSERT statement - so the copy of new rows is put to table-type param.
If for some reason generating unique ID at SQL is not suitable for you, generate GUID's at your app - GUID has a very hight level of uniquness (but it's not guaranteed in fact). And SQL Server has dedicated GUID type for column - it's called uniqueidentifier.
http://msdn.microsoft.com/en-us/library/ms187942.aspx

NHibernate Join Table with Non Primary Key

I am trying to do a one to one join with 2 tables using non-primary fields.
I have 2 tables in the DB.
CREATE TABLE [dbo].[Branch](
[BranchID] [int] IDENTITY(1,1) NOT NULL,
[Branch_Name] [nvarchar](100) NULL)
CREATE TABLE [dbo].[Salesman](
[SalesmanID] [int] IDENTITY(1,1) NOT NULL,
[BranchID] [int] NOT NULL,
[First_Name] [nvarchar](30) NULL,
[Last_Name] [nvarchar](30) NULL)
I basically need the Branch Name whenever I retrieve a row from the salesman table.
I thought I could add a join in the Salesman.hbm.xml file.
<join table="dbo.Branch">
<key column="BranchID" />
<property lazy="true" update="false" insert="false" not-null="false" type="String" name="Branch_Name" />
</join>
This did not work because nHibernate always created a join with the primary key. I read some other posts and they suggested using a view for situations like this. So I created a view like so:
create view dbo.VIEW_Salesman As
SELECT a.[SalesmanID], a.[BranchID], a.[First_Name],a.[Last_Name],
(select [Branch_Name] FROM [dbo].[Branch] WHERE BranchID= a.[BranchID]) As Branch_Name
FROM [dbo].[Salesman] as a
The above view actually works but is there a better solution when you want to join 2 tables using non-primary fields?
Thanks in advance for any suggestions and advice,
Have a great day!
You could either use Dependancy Injection which actually has nothing to do with NHibernate, and would definitely force a change in your mapping file, or perhaps use a named query in the NHibernate configuration file.
Besides, I just thought that you perhaps could use composite mapping while using Dependancy Injection.
Salesman s = new Salesman(branchInstance)
So you should have a Branch property within your Salesman class that could allow you to know the branch name to which this salesman belongs. Otherwise, simply have a BranchName property which would actually return the branchInstance.Name property value.
See the following for Component Mapping using NHibernate:
NHibernate Mapping - ;
NHibernate - Chapter 7 - Component Mapping;
NHibernate Reference Documentation.
Or if you prefer to make it as a view using NHibernate, perhaps a named query should do it with less of a change:
16.2. Named SQL queries.
Hope this helps! Do not hesitate to ask further details, I'll be pleased to assist you furhter if I can! =)

Primary Key issues when Migrating "Legacy" SQLite Databases to use the Entity Framework

I have recently started work on a project that has already been running for several years. The application is written in c# using Windows Forms for the UI and SQLite for the database. The database is currently accessed using ADO.NET via the System.Data.SQLite namespace.
From time-to-time clients have received application and database updates, but not all at once so there are numerous, structurally different versions of the database in existence. To make matters worse, clients are able to add their own fields to the application's tables to enable customized reports, etc. So, the number of different databases is out of control. When feature enhancements have been made there has been more and more "if-then-else" code added to keep the application running for all of these database variations.
Further to this, SQLite has the annoying feature that the values in fields can be stored as any type and not just the type defined in the table create statement. It is common for data to be imported from CSV files out of Excel that have incorrect date/time values that SQLite happily imports, but under code we get all sorts of invalid type exceptions.
I want to put a stop to all of this.
In order to clean up the database I am instigating a standard database design that will not change unless we release an official update along with code to automatically migrate the data.
I have taken the latest "ad-hoc" design and, without changing the table and field names, have made it at least Entity Framework friendly by ensuring that primary keys are defined for the existing fields and that consistent types are used.
I'm now attempting to migrate the various legacy databases one-by-one. I'm using a technique of loading all the records in each table using "SELECT * FROM [LEGACY_TABLE];" and creating new Entity Framework objects using new EF_Table() { ID = GetValue<long>(dr, "ID"), Description = GetValue<string>(dr, "Description"), };. I've defined GetValue to handle converting badly formatted data and DBNull references etc.
My major issue now is that many of the tables have primary keys defined as "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" which won't allow the Entity Framework to assign the current key values from the legacy database.
In the migration process I need to ensure that primary keys remain the same as there are no foreign key relationships defined and existing primary key values are stored outside of the database in application configuration data.
I have looked around for a solution, but can't see one. I would have thought that this kind of a problem - the migration and/or cleansing of an existing SQLite database - would have been a solved problem.
Can anyone point me in a productive direction to solve this?
Well, it was quite simple to get around this issue.
It turns out that the primary keys defined by the Entity Framework in the "edmx" file has a property StoreGeneratedPattern="Identity" for the auto increment primary keys. By changing the value to StoreGeneratedPattern="None" the Entity Framework allows updates to the primary key by simple assignment in code.
So, for example, when I have this table definition:
CREATE TABLE "FM_Location" (
[ID] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Location] TEXT
);
Then the following "EntityType" element is defined in the "edmx" file:
<EntityType Name="FM_Location">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="integer" Nullable="false"
StoreGeneratedPattern="Identity" />
<Property Name="Location" Type="nvarchar" />
</EntityType>
I changed "Identity" to "None" like this:
<EntityType Name="FM_Location">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="integer" Nullable="false"
StoreGeneratedPattern="None" />
<Property Name="Location" Type="nvarchar" />
</EntityType>
I did this with all the auto increment fields and I could do the migration.

Entity Framework doesn't like 0..1 to * relationships

I have a database framework where I have two tables. The first table has a single column that is an identity and primary key. The second table contains two columns. One is a varchar primary key and the other is a nullable foreign key to the first table.
When adding the tables to the model I get the following validation error:
Condition cannot be specified for Column member 'DetailsControlSetId' because it is marked with a 'Computed' or 'Identity' StoreGeneratedPattern.
where 'DetailsControlSetId' is the second foreign key reference in the second table.
Steps to reproduce:
1) Create a new .Net 3.5 Client Profile project with Visual Studio 2010 RC.
2) Run scripts below against test database (empty database will do).
3) Create EDMX model, targeting the database created, but opt to not import any tables.
4) Update Model from Database selecting the two tables in the database (DetailsControlSet and Application).
5) Validate the EDMX model.
Table Creation Scripts:
CREATE TABLE [dbo].[DetailsControlSet](
[DetailsControlSetId] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_DetailsControlSet] PRIMARY KEY CLUSTERED
(
[DetailsControlSetId] ASC
)
)
GO
CREATE TABLE [dbo].[Application](
[ApplicationName] [varchar](50) NOT NULL,
[DetailsControlSetId] [int] NULL,
CONSTRAINT [PK_Application] PRIMARY KEY CLUSTERED
(
[ApplicationName] ASC
)
)
GO
ALTER TABLE [dbo].[Application] WITH CHECK ADD CONSTRAINT [FK_Application_DetailsControlSet] FOREIGN KEY([DetailsControlSetId])
REFERENCES [dbo].[DetailsControlSet] ([DetailsControlSetId])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Application] CHECK CONSTRAINT [FK_Application_DetailsControlSet]
GO
Update Now that you've (finally!) posted steps to reproduce this, I can make the error happen on my machine. And diffing the EDMX of the "import everything at first" vs. the "import tables later" models makes the problem obvious. The "working" model has this line:
<Property Name="DetailsControlSetId" Type="int" />
The "error" model has this line:
<Property Name="DetailsControlSetId" Type="int" StoreGeneratedPattern="Identity" />
That's the only substantive difference between the two models.
So to fix this:
Right click EDMX in Solution Explorer.
Open with XML editor.
Delete StoreGeneratedPattern="Identity"
Note that the error immediately goes away.
Having this test case, I was able to do some research. It turns out this is a known bug in VS 2010 beta and was fixed a few days ago.
This article might help, was posted at ADO.NET official team blog
Foreign Key Relationships in the
Entity Framework

Categories