I'm writing a homework for my RDBMS class, I need to perform CRUD operations on quite simple domain, which is cyber sport championship.
Students are required to use ADO.NET. My question is how can I solve bidirectional relationship, for example 1:m (every championship has many matches, but every match belongs to only one exact championship)? It seems to me that there must be some technique for that.
And the most interesting part for me is - how does ORM like EF or NHibernate solve this situation?
In NHibernate, it is quite simple and straight-forward. Here's how the domain classes would look, followed by fluent mappings. This assumes you would use NHibernate to generate your schema. If you are mapping a legacy database, it is simple to set the column names and table names used.
public class Championship {
public virtual int Id { get; set; }
public virtual IList<Match> Matches { get; set; }
}
public class Match {
public virtual int Id { get; set; }
public virtual Championship Champioship { get; set; }
}
public class ChampionshipMap : ClassMap<Championship> {
public ChampionshipMap() {
Id(x => x.Id);
HasMany(x => x.Matches);
}
}
public class MatchMap : ClassMap<Match> {
public MatchMap () {
Id(x => x.Id);
References(x => x.Championship);
}
}
Have a look at Davy Brions Blog about building your own Data Access Layer. He talks about all those sort of challenges.
For something like many-to-many with Hibernate, you define the relationship. Here's an example (reference is here:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>
From the database side itself, for many-to-many relationship you will usually have a link table.
So we'd have:
PERSON
ADDRESS
PERSON_ADDRESS
The PERSON_ADDRESS table would contain person_id and address_id to link the two entities together. So one person could have many addresses, and a given address could potentially belong to more than one person or company.
For a 1:m relationship, it's good enough to have this:
PERSON
ADDRESS
In address, you would have the person_id column, but there could be many address records for a given person_id, giving you the 1:m capability.
For example in DataObjects.Net you can write following to get automatically associated Championship.Matches entity set and Match.Championship persistent field.
[HierarchyRoot]
public class Championship : Entity
{
[Field, Key]
public int Id { get; set; }
[Field, Association(PairTo="Championship")]
public EntitySet<Match> Matches { get; private set; }
}
[HierarchyRoot]
public class Match : Entity
{
[Field, Key]
public int Id { get; set; }
[Field]
public Championship Championship { get; set; }
}
Related
I have the following scenario: I have a component of an entity, but instead of store it in the same table I need to store in a separate table. The relationship between this two tables is one to one at most (1-0:1).
The id of the component table is given by the main table, as value object it doesn't have an identity.
Now I wonder how can I map the component to be stored in his own table without add an Id to it in the domain model.
There are three main ways to map a one-to-one relationship: inheritance, one-to-one, and join. I'm pretty sure that all three of these can be configured to share primary keys instead of having to add an additional primary key column. In this case, join sounds like the best fit, since you wouldn't have to create a separate entity. Ayende's article is the best resource for understanding join.
Example (from Ayende's article, but adding a component to the mix):
<class name="Person"
table="People">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
<join table="Addresses">
<key column="PersonId"/>
<component name="Address"
class="Address">
<property name="Line1"/>
<property name="Line2"/>
<property name="City"/>
<property name="Country"/>
<property name="ZipCode"/>
</component>
</join>
</class>
The classes for this would look like:
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
}
Note that Address does not have an Id property, and it's properties are not virtual. This is because Address is not an entity, it is a component. But join allows it to live in a separate table from Person's other properties.
If the table with the value object is only referenced by one table you can use the primary key of that table as primary key but don't make any methods to access the field in the value object.
If the table is referenced by more other tables then you have to create an own primary key.
You need a primary key to at least join the tables.
I use HBM mapping.
I have tables :
I) person with columns :
1. ID
2. TYPE
3.CREATE_DATE
4.UPDATE_DATE
II) Attribute with columns:
1.ID
2.TYPE(in this example person may be all type)
3.NAME
4.CREATE_DATE
5.UPDATE_DATE
III) Attribute_VALUE with columns:
1.ID
2.VALUE
4.OBJECT_ID
5.ATTRIBUTE_ID
6.CREATE_DATE
7.UPDATE_DATE
There is relationship one-to-many between person(ID) and Attribute_VALUE(OBJECT_ID).
There is relationship one-to-many between Attribute(ID) and Attribute_VALUE(ATTRIBUTE_ID)
I need build object PERSON that contain all columns of person and dictionary with name attribute.
The dictionary contain key - name of attribute value- collection of values .
Can I build appropriate HBM ??
the short answer no.
the long answer:
consider how should nhibernate match attributes when you Attributes.Add("foo", "value")? it has to search the db for an attribute foo (which is not a simple mapping, its logic) or it would create a new Attribute, everytime you add one.
So given the above schema you either a) have some kind of custom onsave code (which i think is a lot of effort) or b) you change the Person to
class Person
{
public virtual int Id { get; set; }
public virtual ICollection<AttributeValue> Attributes { get; set; }
public virtual IEnumerable<string> GetValues(string attributeName)
{
return Attributes
.Where(attr => attr.Attribute.Name == attributeName)
.Select(attr => attr.Value);
}
public virtual void AddValue(Attribute attribute, string value)
{
Attributes.Add(new AttributeValue
{
Attribute = attribute,
Value = value
});
}
public virtual IEnumerable<string> GetAttributeNames()
{
return Attributes
.Select(attr => attr.Attribute.Name);
}
}
class Attribute
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
// and more Properties like created and updated
}
class AttributeValue
{
public virtual int Id { get; set; }
public virtual Attribute Attribute { get; set; }
public virtual string Value { get; set; }
// and more Properties like created and updated
}
and then use
<class name="Person" table="Persons" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<bag name="Attributes">
<key column="OBJECT_ID"/>
<one-to-many class="AttributeValue"/>
</bag>
</class>
<class name="Attribute" table="Attributes" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<property name="Name" column="Name"/>
<!--additional properties-->
</class>
<class name="AttributeValue" table="AttributeValues" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<many-to-one class="Attribute" column="ATTRIBUTE_ID"/>
<property name="Value" column="Value"/>
<!--additional properties-->
</class>
http://www.codinginstinct.com/2010/03/nhibernate-tip-use-set-for-many-to-many.html
I want to do the way the author suggested for fluent nhibernate many-to-many but use automapping instead of HBM file.
Here are my two entities
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Iesi.Collections.Generic.Set<City> Cities { get; set; }
}
public class City{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Iesi.Collections.Generic.Set<User> Users { get; set; }
}
Tried with HashSet, IList, and Set. But when I looked at the HBM files generated by calling automapping output method:
var autoMappings = new AutoPersistenceModel().AddEntityAssembly(entityAssembly).Where(x => x.Namespace.EndsWith("Domain"));
autoMappings.WriteMappingsTo((#"C:\TEMP");
It's still bag type
<bag inverse="true" name="Users" table="MNUserCity" mutable="true">
<key>
<column name="CityId" />
</key>
<many-to-many class="MyApp.Entity.Domain.User, MyApp.Entity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<column name="UserId" />
</many-to-many>
</bag>
Is there any conventions/override I can use in Fluent NHibernate to alter the collection type for all the ManyToMany in the app domain? I looked at IHasManyToMany convention but no clue.
Anyone can help? Thanks.
BTW, I'm using latest build in http://github.com/jagregory/fluent-nhibernate
Changing the type of Users and Cities from Set to ISet should solve your issue.
As stated by James in this thread, "The automapper is very opinionated and inflexible, and it expects collections to be exposed as IList or ISet."
EDIT: I simplified the problem to leave only what really bothers me.
Hello all,
I am trying to make the following mapping.
In my database, I have a table called "ReportRowValue" containg the following columns:
RowNumber
ColumnNumber
StringValue
LongValue
DateValue
Value
In my code I want to get a more usable structure by creating several two classes from this one table. I guess this should be done using components and inheritance but I did not managed to create a working mapping file. What I want in code should look like this:
ReportRow
RowNumber
Values (collection of ReportValue below)
ReportValue (being an abstract class)
ColumnNumber
Value
ReportValueString / ReportValueLong / ReportValueDate (each one inheriting from ReportValue)
Value (each one having a Value property of its one type)
And that's about all!
Does anyone can point me how to create an nhibernate mapping file/files for doing that?
Thanks,
Meigetsu
There is couple of tools that maps and builds class for you one of them is
mygeneration
is the software http://sourceforge.net/projects/mygeneration/
In this page you find the templates that you need to run with the
softwarehttp://www.mygenerationsoftware.com/TemplateLibrary/Archives/?query=nhibernate
After you have this in the mygeneration tool you only connect to your DB and it will generated for you
Unfortunately, you can't have a polymorphic structure in a component. But I'm acutally not sure if you need it.
The following code is straight from my head, so it certainly has errors or missing things and wouldn't compile. But it should show the direction:
public class ReportRow
{
public int Id { get; private set; }
public IList<IReportValue> Values { get; private set; }
}
public interface IReportValue
{
public int Id{ get; set; }
public object UntypedValue { get; }
}
public abstract class ReportValue<T> : IReportValue
{
public int Id{ get; set; }
public T Value { get; set; }
public object UntypedValue { get { return Value; } }
}
public class ReportLongValue : ReportValue<long> {}
public class ReportStringValue : ReportValue<string> {}
public class ReportDateValue : ReportValue<DateTime>{}
Mapping:
<class ReportRow>
<id ...>
<bag name="Values" >
<key column="RowNumber"/>
<one-to-many class="IReportValue"/>
</bag>
</class>
<class name="IReportValue" abstract="true">
<id ...>
<subclass name="ReportLongValue">
<property name="Value" column="LongValue"/>
</subclass>
<subclass name="ReportStringValue">
<property name="Value" column="StringValue"/>
</subclass>
<subclass name="ReportDateValue">
<property name="Value" column="DateValue"/>
</subclass>
</class>
I have a table structure something like this
table Employees
EmployeeID
EmployeeLogin
EmployeeCustID
table Customers
CustomerID
CustomerName
What i would like is to map the structure above to one single class named:
Class Employee
EmployeeID
EmployeeLogin
EmployeeName
How do i do that with fluent nhibernate ?
I don't know if it is possible with fluent, but in xml you use the join element:
simplified:
<class name="Employee" table="Customers" >
<id name="CustomerID" .../>
<property name="CustomerName"/>
<join table="Employees">
<key column="EmployeeCustID" />
<property name="EmployeeLogin" />
</join>
</class>
See this post by Ayende
I agree with Frans above but if you're stuck with someone else's code and have to use the existing structure, you can can use WithTable.
public class EmployeesMap : ClassMap<Employees>
{
public EmployeesMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
WithTable("Customers", join =>
{
join.Map(m => m.EmployeeName, "CustomerName");
join.WithKeyColumn("EmployeeCustID");
});
}
}
[DataContract(IsReference = true)]
public class Employees
{
[DataMember]
public virtual int EmployeeId { get; set; }
[DataMember]
public virtual string EmployeeLogin { get; set; }
[DataMember]
public virtual string EmployeeName { get; set; }
}
I have not tried this since Fluent NHibernate went to 1.0 so my syntax may be incorrect. I'm pretty sure this will only work if Customer.CustomerId is a foreign key to Employee.
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
Table("Customer", m =>
{
m.Map(x => x.EmployeeName, "CustomerName");
});
}
}
Is EmployeeCustID unique? If not, this is never going to work, as you then try to cram two different entity types into 1. Also, with your structure, how do you want to save an instance? -> the CustomerID isn't known, so you can't save such an entity.
IMHO it's better to simply keep Customer as a related entity to Employee, as (I assume) the EmployeeCustID is used to link a Customer entity to an Employee entity if the employee is also a customer, which means 'customer' is just a role for employee and therefore optional and changeable and thus should be a separate entity.