I have been experimenting with Entity Framework 4.4, NHibernate 3.3.1.4000 and SQL Server and I have noticed a difference when it comes to fixing up the relationships when you commit your changes, and I was wondering what is the best practice or if I'm doing something wrong.
Here's what I tested. I have a classic Parent linked to n Children. I have 2 parents in the database with 20 children each. I load both parents, and I take the first child of the first parent and assign that child the second parent. I then commit the changes.
In EF, after I have saved, I can see that the count for the Children collection of both parents has been altered, so it fixed up the relationships.
However, when I do the same thing in NHibernate, the counts remain the same.
Here's my code setup to reproduce the issue.
POCOs:
public class Parent
{
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Child> Children { get; set; }
public Parent()
{
Children = new List<Child>();
}
}
public class Child
{
public virtual int ChildId { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual Parent Parent { get; set; }
}
NHibernate Parent mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ConsoleApplication1"
namespace="ConsoleApplication1">
<class name="Parent" table="Parents">
<id name="ParentId" column="ParentId" />
<property name="Name" column="Name" />
<bag name="Children" cascade="all">
<key column="ParentId"/>
<one-to-many class="Child"/>
</bag>
</class>
</hibernate-mapping>
NHibernate Child mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ConsoleApplication1"
namespace="ConsoleApplication1">
<class name="Child" table="Children">
<id name="ChildId" column="ChildId" />
<property name="Name" column="Name" />
<many-to-one name="Parent" class="Parent" column="ParentId" not-null="true" />
</class>
</hibernate-mapping>
EF DbContext:
public class Entities : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
}
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate" />
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">
NHibernate.Dialect.MsSql2005Dialect
</property>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Data Source=localhost;Initial Catalog=MaintainRelationshipsNH;Integrated Security=True;
</property>
</session-factory>
</hibernate-configuration>
<connectionStrings>
<add
name="Entities"
providerName="System.Data.SqlClient"
connectionString="Server=localhost;Database=MaintainRelationshipsNH;Trusted_Connection=true;"/>
</connectionStrings>
</configuration>
Script to create the tables and data:
create table Parents (
ParentId INT not null,
Name NVARCHAR(255) null,
primary key (ParentId)
)
create table Children (
ChildId INT not null,
Name NVARCHAR(255) null,
ParentId INT not null,
primary key (ChildId)
)
alter table Children
add constraint FK_Children_Parents
foreign key (ParentId)
references Parents
declare #idChild int
insert into Parents (ParentId, Name) values (0, 'John');
set #idChild = 0
while #idChild < 20
begin
insert into Children (ChildId, Name, ParentId) values (#idChild, 'Child ' + convert(nvarchar(2), #idChild), 0);
set #idChild = #idChild + 1
end
insert into Parents (ParentId, Name) values (1, 'Julie');
while #idChild < 40
begin
insert into Children (ChildId, Name, ParentId) values (#idChild, 'Child ' + convert(nvarchar(2), #idChild), 1);
set #idChild = #idChild + 1
end
NHibernate test code:
System.Diagnostics.Debug.WriteLine("Test NHibernate:");
Configuration configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof(Parent).Assembly);
ISessionFactory sessionFactory = configuration.BuildSessionFactory();
Parent parent0, parent1;
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
parent0 = session.Load<Parent>(0);
parent1 = session.Load<Parent>(1);
System.Diagnostics.Debug.WriteLine("Before modifications and commit");
System.Diagnostics.Debug.WriteLine("Parent0 number of children: " + parent0.Children.Count);
System.Diagnostics.Debug.WriteLine("Parent1 number of children: " + parent1.Children.Count);
parent0.Children[0].Parent = parent1;
transaction.Commit();
}
}
System.Diagnostics.Debug.WriteLine("After modifications and commit");
System.Diagnostics.Debug.WriteLine("Parent0 number of children: " + parent0.Children.Count);
System.Diagnostics.Debug.WriteLine("Parent1 number of children: " + parent1.Children.Count);
Entity framework test code:
System.Diagnostics.Debug.WriteLine("Test Entity Framework:");
Parent parent0, parent1;
using (Entities entities = new Entities())
{
parent0 = entities.Parents.Find(0);
parent1 = entities.Parents.Find(1);
System.Diagnostics.Debug.WriteLine("Before modifications and commit");
System.Diagnostics.Debug.WriteLine("Parent0 number of children: " + parent0.Children.Count);
System.Diagnostics.Debug.WriteLine("Parent1 number of children: " + parent1.Children.Count);
parent0.Children[0].Parent = parent1;
entities.SaveChanges();
}
System.Diagnostics.Debug.WriteLine("After modifications and commit");
System.Diagnostics.Debug.WriteLine("Parent0 number of children: " + parent0.Children.Count);
System.Diagnostics.Debug.WriteLine("Parent1 number of children: " + parent1.Children.Count);
So basically with this test I can see the counts changing with EF but not with NHibernate. Am I doing something wrong with NH or do I have to manually manage every part of the relationships affected by my changes ? Thanks!
In NHibernate you would need to manually move them from one parent collection to another. I usually use Add or Remove methods in the parent classes to do this. Here is an example of these add or remove methods:
public virtual void AddLine(OrderLine orderLine)
{
orderLine.Order = this;
this.orderLines.Add(orderLine);
}
public virtual void RemoveLine(OrderLine orderLine)
{
this.orderLines.Remove(orderLine);
}
To reparent a child I would then do something like this:
originalParent.RemoveLine(child);
newParent.AddLine(child);
Related
I'm trying to get a list of persons using JPA. Every time I run the code, I get "java.lang.IllegalArgumentException: NamedQuery of name: Persoon.getAllePersonen not found."
I tried changing the table name, replaced Persoon.getAllePersonen by getAllePersonen,.... I just can't seem to figure out what's causing the error
Persoon
#Entity
#Table(name = "Persoon")
#NamedQueries({
#NamedQuery(name = "Persoon.getAllePersonen",
query = "SELECT p FROM Persoon p"),
#NamedQuery(name = "Persoon.findByName",
query = "SELECT p FROM Persoon p WHERE p.achternaam = :persoonNaam OR p.voornaam = :persoonNaam")
})
public class Persoon implements Serializable {
PersoonDao
public List<Persoon> getAlleLeden(){
TypedQuery<Persoon> queryP = em.createNamedQuery("Persoon.getAllePersonen", Persoon.class);
try{ return queryP.getResultList();
} catch (NoResultException e){
throw new EntityNotFoundException("Cannot find leden");
}
}
EDIT:
Generic Superclass DAO
public class GenericDaoJpa<T>{
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("TaijitanPU");
protected static final EntityManager em = emf.createEntityManager();
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="TaijitanPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>domein.Persoon</class>
<class>domein.Graad</class>
<class>domein.Locatie</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:sqlserver://localhost\sqlexpress:1433;databaseName=Taijitan;integratedSecurity=true;"/>
<property name="javax.persistence.jdbc.user" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
You have to do an abstract class Generic class and override the entityManager of the parent class for each child. Check below. I used EJB Stateless for the childs.
-> PARENT DAO
public abstract class AbstractDAO<T> {
...
protected abstract EntityManager getEntityManager();
-> CHILD DAO
#PersistenceContext(unitName = "yourPersistenceUnitName")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
Problem with tables creation by code first model with npgsql
Database creation should work from version 2.2.0-beta1:
«David Karlaš added support for EFMigration and Database creation in EF6+
Now it is possible to start Code First projects without needing to create a database upfront. EntityFramework and Npgsql will take care of it.
Emil Lenngren added support for a lot of missing features of EntityFramework.»
https://www.nuget.org/packages/Npgsql/2.2.0-beta1
But, when I tried to do this I faced with problems.
Firstly, I did the simple project, that works with SqlServer:
I created Asp.Net MVC project (VS2013) and add some code:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
}
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
public class MovieDBInitializer : DropCreateDatabaseIfModelChanges<MovieDBContext>
{
}
public class HomeController : Controller
{
private MovieDBContext db = new MovieDBContext();
public ActionResult Index()
{
List<Movie> objs = db.Movies.ToList();
return View(objs);
}
WebConfig:
<connectionStrings>
<add name="MovieDBContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MovieCreateDbInSql;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MovieDB.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>
<entityFramework>
<contexts>
<context type="MovieCreateDbInSql.Models.MovieDBContext, MovieCreateDbInSql">
<databaseInitializer type="MovieCreateDbInSql.Models.MovieDBInitializer, MovieCreateDbInSql" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
When starting the project the database MovieDB is created.
All is works.
We can see this database in App_Data folder in the project.
Good.
Then I tried to do the same with npgsql.
Add libs:
EntityFramework6.Npgsql.dll (version 3.1.0.0)
Npgsql.dll (version 3.1.2.0)
2.
Than change WebConfig:
<connectionStrings><add name="MovieDBContext" connectionString="Server=127.0.0.1;Port=5432;Database=postgres;User Id=postgres;Password=postgres000;" providerName="Npgsql" /></connectionStrings>
<entityFramework>
<contexts>
<context type="MovieCreateDbInSql.Models.MovieDBContext, MovieCreateDbInSql">
<databaseInitializer type="MovieCreateDbInSql.Models.MovieDBInitializer, MovieCreateDbInSql" />
</context>
</contexts>
<defaultConnectionFactory type="Npgsql.NpgsqlFactory, Npgsql" />
<providers>
<provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, EntityFramework6.Npgsql"></provider>
</providers>
</entityFramework>
<system.data>
<DbProviderFactories>
<remove invariant="Npgsql" />
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql"
type="Npgsql.NpgsqlFactory, Npgsql" />
</DbProviderFactories>
</system.data>
Start. Error:
System.NotSupportedException was unhandled by user code
HResult=-2146233067
Message=Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations.
Source=EntityFramework
But this configuration was enough for SqlServer!
Ok. Try this:
EF5 Getting this error message: Model compatibility cannot be checked because the database does not contain model metadata
1.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
Don't really help. The error is the same
public class MovieDBInitializer : DropCreateDatabaseAlways
{
}
New error:
Npgsql.PostgresException was unhandled by user code
HResult=-2147467259
Message=55006: база данных "postgres" занята другими пользователями
Source=Npgsql
ErrorCode=-2147467259
BaseMessage=база данных "postgres" занята другими пользователями
Code=55006
Detail=Эта база данных используется ещё в 1 сеансе.
(error 55006 database is being accessed by other user)
This error is not good too.
As far as I understand this error is because we have serious database posgresql unlike primitive localdb.sql.
And dropping db operation in postgresql is not so easy as in localdb.sql.
I found several links on that error:
https://groups.google.com/forum/#!topic/npgsql-help/1f5niOiHpGg
Drop a database being accessed by another users?
npgsql and Entity Framework code first setup problems
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<MovieDBContext>(null);
}
The same error again:
System.NotSupportedException was unhandled by user code
HResult=-2146233067
Message=Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations.
Source=EntityFramework
What should I do to have the opportunity to create tables by the code first model?
Of course I can generate the database in SqlServer and than convert scripts to postgresql, but I want do this with npgsql.
Libraries that I use:
EntityFramework.6.1.3
EntityFramework6.Npgsql.3.1.0
Npgsql.3.1.3
My Database context:
public class CustomContext : DbContext
{
public CustomContext()
: base("name=CustomContext")
{
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<CustomContext, Configuration>());
}
public DbSet<Movie> Movies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Change schema name from "dbo" => "public" for all entities (except MigrationHistory).
// MigrationHistory schema name is modified in custom HistoryContext
modelBuilder.Types().Configure(c => c.ToTable(c.ClrType.Name, "public"));
}
}
Migration history table has a default schema set to "dbo". If you want to have it in a different schema (ex. public) you have to create a custom HistoryContext.Currently it's impossible to set a default schema with modelBuilder.HasDefaultSchema("public") while using automatic migrations (AFAIK).
public class CustomHistoryContext : HistoryContext
{
public CustomHistoryContext(DbConnection existingConnection, string defaultSchema)
: base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Change schema from "dbo" to "public"
modelBuilder.Entity<HistoryRow>()
.ToTable(tableName: "__MigrationHistory",
schemaName: "public");
}
}
Finally, my migration configuration class looks like this:
internal sealed class Configuration : DbMigrationsConfiguration<CustomContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
// Setting custom historyContext factory.
// Used for modify __MigrationHistory table schema name from "dbo" to "public"
this.SetHistoryContextFactory("Npgsql",
(connection, defaultSchema) =>
new CustomHistoryContext(connection, defaultSchema));
}
protected override void Seed(CustomContext context)
{
// This method will be called after migrating to the latest version.
}
}
After these steps automatic code first migrations work correctly.
However, here is a bit more details.
I have classes:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime IssueDay { get; set; }
public decimal EarnedMoney { get; set; }
public virtual ICollection<Actor> Actors { get; set; }
}
public class Actor
{
public int ID { get; set; }
public int MovieID { get; set; }
public DateTime BirthDay { get; set; }
public string Name { get; set; }
}
And I got scripts:
CREATE TABLE "Movie"
(
"ID" serial NOT NULL,
"Title" text,
"IssueDay" timestamp without time zone NOT NULL DEFAULT '0001-01-01 00:00:00'::timestamp without time zone,
"EarnedMoney" numeric(18,2) NOT NULL DEFAULT 0,
CONSTRAINT "PK_public.Movie" PRIMARY KEY ("ID")
)
WITH (
OIDS=FALSE
);
ALTER TABLE "Movie"
OWNER TO postgres;
CREATE TABLE "Actor"
(
"ID" serial NOT NULL,
"MovieID" integer NOT NULL DEFAULT 0,
"Name" text,
"BirthDay" timestamp without time zone NOT NULL DEFAULT '0001-01-01 00:00:00'::timestamp without time zone,
CONSTRAINT "PK_public.Actor" PRIMARY KEY ("ID"),
CONSTRAINT "FK_public.Actor_public.Movie_MovieID" FOREIGN KEY ("MovieID")
REFERENCES "Movie" ("ID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
OIDS=FALSE
);
ALTER TABLE "Actor"
OWNER TO postgres;
CREATE INDEX "Actor_IX_MovieID"
ON "Actor"
USING btree
("MovieID");
And these scripts require additional processing of course.
That is why in general there is no difference generate scripts in postgres directly or generate in sqlServer and then convert to postgres by means of Navicat for example…
Here is some entity:
#Entity
public class Forest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
public Forest() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
I want to insert some element in table forest:
public class Main {
private static EntityManagerFactory emf =
Persistence.createEntityManagerFactory("server");
public static void main(String[] args) {
EntityManager em = emf.createEntityManager();
EntityTransaction trx = em.getTransaction();
Forest forest = new Forest();
trx.begin();
em.persist(forest);
trx.commit();
}
}
Thrown exception:
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Table 'server.forest' doesn't exist
Caused by: org.hibernate.exception.SQLGrammarException: Table 'server.forest' doesn't exist
My persistence.xml file with settings:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="server">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/server"/>
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="root" />
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
When I removed #GeneratedValue(strategy = GenerationType.AUTO) and set id for forest:
forest.setID(1), there was no exception and table has been generated. So, auto-generating of id is not working and I don't know why.
According configuration there is org.hibernate.dialect.HSQLDialect used with MySQL database. Using MySQL dialect instead of one of HSQL likely helps. Likely InnoDB is used - if so, then MySQL5InnoDBDialect is way to go.
I am trying to get a value from a postgres database using a plpgsql function editor's note: OP had plsql but I am not able to retrieve the data.
I get the following exception:
Exception in thread "main" java.lang.UnsupportedOperationException:
org.hibernate.dialect.PostgreSQLDialect does not support resultsets
via stored procedures
My hibernate configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://192.168.1.100:54321/localDB</property>
<!-- <property name="hibernate.connection.url">jdbc:postgresql://192.168.1.100:54321/scprj</property>-->
<property name="hibernate.connection.username">postgres</property>
<property name="hibernate.connection.password">dbserver</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="hibernate.hbm.xml"/>
</session-factory>
</hibernate-configuration>
My hibernate mapping file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="callFunctionPack.DateBean" table="userlogin">
<id column="userid" name="userid">
<generator class="increment"/>
</id>
<property column="username" name="username" type="string"/>
<property column="password" name="password" type="string"/>
</class>
<sql-query name="getlogin" callable="true" comment="Call the getlogindata procedure">>
<return class="callFunctionPack.DateBean">
<return-property name="username" column="username"/>
<return-property name="password" column="password"/>
</return>
{ call getlogin(:userid) }
</sql-query>
</hibernate-mapping>
My main class function to the procedure:
public class Call {
public static void main(String... args) {
//select();
show();
}
public static void show() {
Query nQuery = getSession().getNamedQuery("getlogin").setParameter("userid", 1);
List results = nQuery.list();
for (Iterator it = results.iterator(); it.hasNext();) {
DateBean dateBean = (DateBean)it.next();
System.out.println(dateBean.getUsername());
System.out.println(dateBean.getPassword());
}
}
public static Session getSession() {
Session session = new Configuration().configure().buildSessionFactory().openSession();
return session;
}
}
My bean class:
public class DateBean {
private String date;
private int userid;
private String username;
private String password;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
My plpgsql function:
CREATE OR REPLACE FUNCTION getlogindata(numeric)
RETURNS refcursor AS
$BODY$
DECLARE
p_userid ALIAS FOR $1;
sql_stmt VARCHAR;
p_condition VARCHAR;
v_boolean BOOLEAN := false;
email_tracking_cursor REFCURSOR;
BEGIN
sql_stmt := 'SELECT USERNAME, PASSWORD FROM USERLOGIN WHERE USERID =' || p_userid;
raise notice '%',sql_stmt;
OPEN email_tracking_cursor FOR EXECUTE sql_stmt;
RETURN email_tracking_cursor;
END; $BODY$
LANGUAGE plpgsql VOLATILE;
ALTER FUNCTION getlogindata(numeric) OWNER TO postgres;
Your plpgsql function does not return a "value", but a cursor. This function does what you seem to want:
CREATE OR REPLACE FUNCTION getlogindata(numeric, OUT username text, OUT password text)
RETURNS record AS
$BODY$
SELECT u.username, u.password FROM userlogin u WHERE u.userid = $1;
$BODY$
LANGUAGE sql STABLE;
You could also use plain SQL for this simple query:
SELECT username, password FROM userlogin WHERE userid = $my_userid;
I don't know Hibernate very well, but you can change the function to be defined as "RETURNS TABLE" and then you can do a select * from getlogindata(42);.
Maybe Hibernate can work with that solution.
I have an entity class Customer which as the property Address that is an object of the Address class and that has few properties. It looks as follows:
public partial class Customer
{
public virtual int ID { get; set; }
public virtual string Symbol { get; set; }
public virtual string Name { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string NIP { get; set; }
public virtual Address Address { get; set; }
}
public partial class Address
{
public virtual int ID { get; set; }
public virtual string Descriptive { get; set; }
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string PostCode { get; set; }
public virtual string Country { get; set; }
}
There's no relation between them in the sanse of database or entities. The Address is just an object contained in the Customer class and they are produced by an NHibernate framework. In fact the <component /> mapping element is being used for that:
<class name="Customer" table="`CRM_CUSTOMER`">
<id name="ID">
<generator class="native" />
</id>
<property name="Symbol" unique="true" />
<property name="Name" />
<property name="FirstName" />
<property name="LastName" />
<property name="NIP" />
<component name="Address" class="Address">
<property name="Descriptive" />
<property name="Street" />
<property name="City" />
<property name="PostCode" />
<property name="Country" />
</component>
</class>
However, the client generated code does not see an Address class/property at all. Nothing helps. Have read tons of articles and nothing. So if anyone could help, than it would be really really appreciated :)
I am using VS 2010 Proffesional.
TIA
Roland
Try and decorate the Customer class address property with the [Include] attribute. This should prompt RIA to return the Address property.
If this doesn't work I'd see if the server-side domain service method has the address property populated correctly and if the problem is in passing the data to your Silverlight client.
You could then try and treat Address as a RIA domain entity and build a relationship between customer and address. The include approach should then produce results.
Hope this helps.
[Sorry for the delay... I'm back here]
The [Include] attribute does not help, unfortunatelly. It is required to add an [association] accompanying attribute. But this implies what I wanted to avoid - enforced additional fields and implementations to the domain objects that were intented to be simple and without such unnecessarities and which were intented for ORM mapping (NHibernate).
The solution is quite invasive and introduced all the stuff required for an associations with respect to the entities - IDs. This also has had its impact on the mapping as I needed to put the <parent /> element inside the <component />. Just to get an ID from the parent - have had no better idea on how to get it...
Finally, the domain classes became as follows:
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
[DataContract]
internal class CustomerMetadata
{
[Key]
[DataMember]
public int ID { get; set; }
[DataMember]
public string Symbol { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string NIP { get; set; }
[Include]
[Association("Customer_Address", "ID", "CustomerID", IsForeignKey = true)]
[DataMember]
public Address Address { get; set; }
}
}
...and
public partial class Customer
{
public virtual int ID { get; set; }
public virtual string Symbol { get; set; }
public virtual string Name { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string NIP { get; set; }
public virtual Address Address { get; set; }
}
To make it work, the mapping has been changed to the following:
<class name="Customer" table="`CRM_CUSTOMER`">
<id name="ID">
<generator class="native" />
</id>
<property name="Symbol" unique="true" />
<property name="Name" />
<property name="FirstName" />
<property name="LastName" />
<property name="NIP" />
<component name="Address" class="Address">
<parent name="Customer" />
<property name="Descriptive" />
<property name="Street" />
<property name="City" />
<property name="PostCode" />
<property name="Country" />
</component>
</class>
The <parent /> element is new there. I am not only sure whether an optional attribute to make the association a foreign key association was correct...
I was also surprising to me that I don't neeed any domain service for an address itself as the generated code contains it and all other things which I expected to occure at the very beginning of the story with the RIA services...
The solution became really invasive and as such not the perfect for me but for now at least satisfying. For now it works. I only wonder wat I shall do with many-to-many association in NH?
Hope this helps someone. Cheers.
Roland