NHibernate Join Table with Non Primary Key - c#

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! =)

Related

Join table with another table in a seperate database using NHibernate Query Language HQL

I'm trying to join a table from another database using NHibernate. I basically prefix the table name with the DB name which I have done in the mapping files before and it has worked.
Please see my code below:
query.CreateAlias("OtherDatabase.SiteProduct","OtherDatabase.SiteProduct", JoinType.InnerJoin);
However, I get the following error message:
NHibernate.QueryException: could not resolve property: OtherDatabase of: XXX.XXXXX.Core.Domain.Catalog.Product
Is this possible to do in NHibernate? I'm assuming since there is no foreign key that ties the two tables together that maybe I need to specify what column the two tables need to be joined on?
Working with other DB/Table (accessible with current connection) is supported. The thing here is.. the path to such table must come from mapping.
We can use either standard mapped Class, or we can introduce just an dynamic dictinoary mapping. E.g.
<class entity-name="OtherDBTable" table="[otherDB].[schema].[Table]" ... >
<id name="ID" column="id" type="Int32" />
<property name="PropertyName" column="ColName" type="String" />
...
</class>
JOIN without mapped relation
Despite of the title .. about HQL .. the broken statement
query.CreateAlias("...
seems to belong to CreateriaQuery... which we will use to JOIN this table
So, let's create standard CriteriaQuery
var query = session.CreateCriteria<MyInnerEntity>("rootAlias");
And we can join it to the dynamically mapped other DB/Table like this
query.CreateEntityAlias("otherAlias",
Restrictions.EqProperty("rootAlias.OtherId", "otherAlias.ID"),
JoinType.InnerJoin,
"OtherDBTable"
);
and we can get the results
query.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("rootAlias.ID"))
.Add(Projections.Property("rootAlias.OtherId"))
.Add(Projections.Property("otherAlias.ID"))
.Add(Projections.Property("otherAlias.PropertyName"))
);
and finally we can read it as a list of array (or transform it ...)
var list = query.List<object[]>();
JOIN with mapped relation
in case, that our mapping to otherDB/Table would contain a relation (e.g. many-to-one) .. we could even use standard CreateAlias .. but still ... the fact that we crossed boundaries of current DB.. must come from mapping

NHibernate dictionary mapping without primary key

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.

Can't access Entity Framework table with no primary key

I am creating an application to transfer data from one database into another for a new application and I am running into an issue access some of the data in the old database. The old database was created using Ruby on Rails and they way it was designed and created through Rails, the database in SQL Server has no primary keys and all the columns are nullable. The new database was designed and built in SQL Server with proper keys and nullable columns. Since both databases are in SQL Server I wanted to use Entity Framework Database First to make the data transition easier.
In the EF Desginer I was able to assign entity keys to all of the tables except for one (Response), which is keeping me from correctly accessing the data in the table. The table definition is as follows:
assess_id [int] NULL
person_id [int] NULL
question_id [int] NULL
question_version [int] NULL
answer_id [int] NULL
answer_version [int] NULL
text [nvarchar(4000)] NULL
created_at [datetime] NOT NULL
updated_at [datetime] NOT NULL
Because of the allowed records the primary key should consist of
assess_id
person_id
question_id
question_version
answer_id
answer_version
but there may be multiple answer_id and answer_version records to the same question_id and question_version or the answer_id and answer_version are null so I cannot use that. Any subset of this key would not allow me to properly retrieve all the data. Also I cannot use the created_at or updated_at columns as there are multiple instances of rows being written with the same time stamp.
I only need to read the data, not write, since it is being transformed into the new database and there is no way for me to change the existing database. Is there any way around the key issue?
Before I answer I would like to point out that this solution only works for read-only situations and if you need to write to the table it will not help. In that case I point you to this answer where you will find some help.
I managed to find a work around for this scenario and while easy enough to use, I would not consider it optimal.
In order to get around the key issue, I picked a primary key that would at least not fail a null check, in this case just the assess_id and person_id columns. This let me build without any errors but I still could not retrieve all the data correctly.
The workaround is to use a SqlQuery on the database.
var responses = context.Database.SqlQuery<Response>("SELECT * FROM dbo.responses");
This executed the query on the whole database and casted the result set into a list of responses with the correct data. Make sure you execute the query on the database and not the table, otherwise the incorrect key specified in the designer will be used and you won't get the correct data back.
Put your SQL in a stored procedure and call that from your application.
If you cannot modify the source database, you can put the stored procedure in the destination.

Best way to access data in related tables

I have two tables that are conceptually related but the original coder did not create any FK relationship between the tables.
Table1:
TABLE [Name](
ID [varchar](10) NOT NULL (this is the PK),
MAIL_ADDRESS_NUM [int] NOT NULL,
BILL_ADDRESS_NUM [int] NOT NULL,
Shipping_Address_Num [int] Not Null
)
Table2:
TABLE [Address](
ID [varchar](10) NOT NULL,
ADDRESS_NUM [int] NOT NULL (this is the PK),
PURPOSE [varchar](20) NOT NULL
)
If I had the option I would put FKs on the MailingAddress to AddressNumber, BillingAddress to AddressNumber, and ShippingAddress to AddressNum. But that is not an option.
Currently in my LINQ I do something like:
from n in Names.Where(predicate)
join ba in db.Addresses on n.BillingAddressNumber equals ba.AddressNumber
join sa in db.Addresses on n.ShippingAddressNumber equals sa.AddressNumber
join ma in db.Addresses on n.MailingAddressNumber equals ma.AddressNumber
If I understand EF and linq correctly I should be able to make that association in/on the Edmx design surface and simplify my query. Correct?
If so what steps need to be taken to add the correct associations to the Edmx files? I have found some references but they seem to involve a fair amount of manipulating the underlying XML files. Is that necessary? Also these tables are strictly read-only. Using VS2012 and EF5.

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