There is no issue; I'm just curious as to how FluentNHibernate/NHibernate knows which rows to access.
In the following pseudocode, I have these entities:
public class User
{
public virtual int Id { get; protected set; }
public virtual IList<Friend> Friends { get; set; }
}
public class Friend
{
public virtual int Id { get; protected set; }
public virtual User User { get; set; }
public virtual String FunnyProperty { get; set; }
}
with this one-to-many relationship:
public class UserMap : ClassMap<User>
{
Id(x => x.Id);
// One user has many friends
HasMany(x => x.Friends).KeyColumn("UserId")...
}
public class FriendMap : ClassMap<Friend>
{
Id(x => x.Id);
References(x => x.User, "UserId")...
}
If I run the following code to establish the two-way relationship:
var user = new User().Friends.AddRange( /* 5 friends */ );
/* 5 friends */.ForEach(friend => friend.User = user);
and then set:
user[2].FunnyProperty = "a magic string";
then, in a visual database explorer tool, I see the table Users as:
Id
--------------------------------------
63
--------------------------------------
and the table Friends as:
Id | UserId | FunnyProperty
--------------------------------------
24 | 63 | (null)
--------------------------------------
25 | 63 | (null)
--------------------------------------
26 | 63 | a magic string
--------------------------------------
27 | 63 | (null)
--------------------------------------
28 | 63 | (null)
--------------------------------------
How does FluentNHibernate/NHibernate "remember" id 26 as the 3rd Friend object in user.Friends? Say you commit the transaction, begin another transaction elsewhere, and retrieve the same user.Friends[2]. How does FluentNHibernate/NHibernate know to access the row with id 26?
Especially when when you have a Friends table with many different Users floating about, like below, how does FluentNHibernate/NHibernate "remember" which row to access ?:
Id | UserId | FunnyProperty
--------------------------------------
24 | 24 | (null)
--------------------------------------
25 | 89 | (null)
--------------------------------------
26 | 66 | a magic string
--------------------------------------
27 | 12 | (null)
--------------------------------------
28 | 66 | (null)
--------------------------------------
28 | 89 | (null)
--------------------------------------
Your Friends table contains the property UserId which is the referenence to friends. Fluent NHibernatie reads you mapping where you have defined this.
There is also an index column in the Friends table.
Related
I'm having trouble with table view.
I want my table to look like this one
Name | Test1 | Test2 | Test3
================================
Anna | 70 | 51 | 90
================================
Jack | 56 | 77 | 82
================================
Now I have
Name | Grade | Test |
================================
Anna | 70 | Test1 |
================================
Anna | 51 | Test2 |
================================
Anna | 90 | Test3 |
================================
Jack | 56 | Test1 |
================================
Jack | 77 | Test2 |
================================
Jack | 82 | Test3 |
================================
The number of tests is dynamic.
Is there any way to do this? Couldn't find anything.
Should I create another model? Or it could be solved by editing View?
Model
public class StudentsWork
{
[Key]
public int IdStudentsWork { get; set; }
public int? Grade { get; set; }
public DateTime? Date { get; set; }
public int Student_id { get; set; }
public int Course_id { get; set; }
public int Test_id { get; set; }
[ForeignKey("Student_id")]
public Student StudentId { get; set; }
[ForeignKey("Course_id")]
public Course CourseId { get; set; }
[ForeignKey("Test_id")]
public Test TestId { get; set; }
}
Controller
public async Task<IActionResult> Group(int? id,int id_group)
{
var group = await _context.StudentsWork
.Include(p => p.StudentId)
.ThenInclude(p => p.GroupId)
.Include(p => p.CourseId)
.Include(p => p.TestId)
.Where(c => c.Course_id == id && c.StudentId.Group_id == id_group)
.ToListAsync();
return View(group);
}
Any help would be greatly appreciated.
You can group by StudentId and select a dictionary for each group to get test name and grade :
var query = db.StudentsWork.Include(p => p.StudentId)
.Include(p => p.CourseId)
.Include(p => p.TestId)
.GroupBy(c => c.StudentId)
.Select(g => new
{
StudentName=g.Key.Name,
TestGrade = g.ToDictionary(t => t.TestId.Name, t => t.Grade)
});
var result = query.ToList();
I have two table: Customers and Products
public class Customers
{
public virtual int Id { get; set; }
public virtual string CName { get; set; }
public virtual int Age { get; set; }
public virtual string Address { get; set; }
public virtual int Salary { get; set; }
}
public class Product
{
public virtual int Id { get; set; }
public virtual string PName { get; set; }
public virtual Customers CustomerID { get; set; }
public virtual int Amount { get; set; }
}
whit this Values in DB:
------------Customer Table----------------
| id | CName | Age | Address | Salary |
------------------------------------------
| 1 | Ben | 18 | a | 1000 |
| 2 | Mark | 20 | b | 2000 |
| 3 | Ben | 18 | a | 3000 |
| 4 | Ben | 19 | c | 4000 |
| 5 | Mark | 20 | b | 5000 |
| 6 | Jane | 21 | d | 6000 |
------------Customer Table----------------
| id | PName | CustomerID_id | Amount |
------------------------------------------
| 1 | A | 1 | 5 |
| 2 | B | 2 | 10 |
| 3 | C | 1 | 15 |
| 4 | D | 2 | 20 |
| 5 | E | 2 | 25 |
| 6 | F | 6 | 30 |
| 7 | G | 6 | 40 |
When I run this query in SQL Server Management:
SELECT CName , Amount
FROM [TestNhibernate].[dbo].[Product]
Inner Join [TestNhibernate].[dbo].[Customers]
on [TestNhibernate].[dbo].[Product].[Customerid_id]
= [TestNhibernate].[dbo].[Customers].[id]
SQL result is:
-------------------
| CName | Amount |
-------------------
| Ben | 5 |
| Mark | 10 |
| Ben | 15 |
| Mark | 20 |
| Mark | 25 |
| Jane | 30 |
| Jane | 40 |
And when I run this query
SELECT CName , Sum(salary) as SumSalary, sum(amount) as SumAmount
FROM [TestNhibernate].[dbo].[Product]
Inner Join [TestNhibernate].[dbo].[Customers]
on [TestNhibernate].[dbo].[Product].[Customerid_id]
= [TestNhibernate].[dbo].[Customers].[id]
Group By Cname
results is :
----------------------------------
| CName | SumSalary | SumAmount |
----------------------------------
| Ben | 2000 | 20 |
| Jane | 12000 | 70 |
| Mark | 6000 | 55 |
----------------------------------
How can I express that in NHiberante query?
UPDATE: some attempts
I try this code
session
.QueryOver<Product>()
.JoinQueryOver<Customers>(p => p.CustomerID)
.SelectList(w => w
.Select(x => x.Amount)
.Select(z => z.CustomerID))
.List<object[]>()
this is done but when i write this code
session
.QueryOver<Product>()
.JoinQueryOver<Customers>(p => p.CustomerID)
.SelectList(w => w
.Select(x=>x.Amount)
.Select(z=>z.CustomerID.CName))
.List<object[]>()
doesn't work!
Based on the information in the question, there is some draft of the QueryOver syntax, which should help to understand. Firstly we should create some DTO, representing the result:
public class ProductDTO
{
public virtual string ClientName { get; set; }
public virtual decimal SumSalary { get; set; }
public virtual decimal SumAmount { get; set; }
}
Now, we should have business model with a bit more standard naming:
public class Product
{
...
// instead of this
//public virtual Customers CustomerID { get; set; }
// we should use
public virtual Customers Customers { get; set; }
}
Personally I would rather see Customer than Customers... but still better than CustomersID
Because our mapping here must be <many-to-one representing reference relation, not just <property - representing the value type / integer.
Now the query:
// to have access to client name
Customers client = null;
// that would be result - used for proper columns aliasing
ProductDTO dto = null;
var result = session.QueryOver<Occupation>()
.JoinQueryOver<Customers>(p => p.Customers , () => client)
.SelectList(w => w
// SUM
.SelectSum(x => x.Amount)
.WithAlias(() => dto.SumAmount)
.SelectSum(x => x.Salary)
.WithAlias(() => dto.SumSalary)
// GROUP BY
.Select(x => client.CName)
.WithAlias(() => dto.ClientName)
)
// we do have enough info to ask NHibernate for
// fully typed result - Product DTO
.TransformUsing(Transformers.AliasToBean<ProductDTO>())
.List<ProductDTO>();
That should give some idea how to do querying with NHibernate. Also, I would suggest to extend Customer with IList<Products>
it's hard for me to explain, so I'll start with an example.
I'm beggining to implement an object changing history in my project.
I want to track date of the change, the changer, the object changed, etc. and persist that info in my DB.
I designed a table similar to this (fits my needs the most):
Table CHANGES
change_id (pk) | change_date | changer_id (fk) | object_id | table_name | column_name
with an exemplary rows
120 | 2013-11-20 | 55 | 88 | "invoices" | "number"
121 | 2013-11-25 | 53 | 99 | "employees" | "name"
Now, i would like to have this kind of entity objects:
class Employee
{
public virtual string Name {get; set;}
public virtual IList<Changes> ChangesList {get; set;}
}
class Invoice
{
public virtual string Number {get; set;}
public virtual IList<Changes> ChangesList {get; set;}
}
The ChangesList from both Invoice and Employee should come from the same table in DB of course. Is it possible and how to achieve this by mapping? Are there any alternatives/minor changes to make this possible?
Before you invent your own take a look at NHibernate.Envers which handles auditing.
If it does not fit your needs it is possible to map it like
public ChangeMap
{
Id(x => x.Id);
Map(x => x.Date, "change_date");
References(x => x.Changer, "changer_id");
...
Map(x => x.TableName, "table_name");
}
// in EmployeeMap
HasMany(x => x.ChangesList).Where("table_name = 'employees'");
// in InvoiceMap
HasMany(x => x.ChangesList).Where("table_name = 'invoices'");
I have a domain data source in Silverlight 4. I'm setting it up in the code behind, and I'm also using a RadDataPager and RadGridView.
DomainDataSource dds = new DomainDataSource();
dds.QueryName = "MyQueryName";
dds.DomainContext = ctx;
dds.LoadSize = 10;
dds.PageSize = 10;
dds.AutoLoad = true;
radDataPager1.PageSize = 10;
radDataPager1.Source = dds.Data;
radGridView1.ItemsSource = dds.Data;
dds.Load();
The first time this loads, the dds.Data.Count is 8, and only 8 items show up in the grid view, even though the dds.Data.PageSize is 10. After I page to the next page, all 10 objects are loaded like they should be.
I'm new to Silverlight, and have no idea what is going on here. Am I loading the data wrong? Any ideas?
This behavior can be caused by wrong set of Key attribute in your model class. If in the result query, rows with Key field are duplicated, then DataGrid will take only first.
Example (book store info ):
Your result class (BookPriceInfo.cs) looks like this:
public class BookPriceInfo {
[DataMember]
[Key]
public int BookId { get; set; }
[DataMember]
public string Name {get; set; }
[DataMember]
public string StoreName {get; set;}
[DataMember]
public decimal Price {get; set;}
}
And if you have query result returned from database :
BookId | Name | StoreName | Price
1 | book1 | store1 | 70.00
1 | book1 | store2 | 69.99
2 | book2 | store1 | 40.00
2 | book2 | store2 | 39.99
Then DataGrig will show only this :
BookId | Name | StoreName | Price
1 | book1 | store1 | 70.00
2 | book2 | store1 | 40.00
this happens because of DataGrid will 'distinct' the results by field marked as Key(get only first row from all rows with same BookId), because it should be uniq for all rows.
Solution
Remove Key attribute from field which will have duplicated values (BookId) and set it to field which will have uniqe values for all rows (you can add some column like BookPriceId, which will be uniqe).
public class BookPriceInfo {
[DataMember]
public int BookId { get; set; }
[DataMember]
public string Name {get; set; }
[DataMember]
public string StoreName {get; set;}
[DataMember]
public decimal Price {get; set;}
[DataMember]
[Key]
public int BookPriceId {get; set;}
}
Query result:
BookPriceId | BookId | Name | StoreName | Price
1 | 1 | book1 | store1 | 70.00
2 | 1 | book1 | store2 | 69.99
3 | 2 | book2 | store1 | 40.00
4 | 2 | book2 | store2 | 39.99
After that you should see all rows returned by query.
Hope this helps :)
I'm trying to leverage NH to map to a data model that is a loose interpretation of the EAV/CR data model.
I have most of it working but am struggling with mapping the Entity.Attributes collection.
Here are the tables in question:
--------------------
| Entities |
--------------------
| EntityId PK |-|
| EntityType | |
-------------------- |
-------------
|
V
--------------------
| EntityAttributes | ------------------ ---------------------------
-------------------- | Attributes | | StringAttributes |
| EntityId PK,FK | ------------------ ---------------------------
| AttributeId FK | -> | AttributeId PK | -> | StringAttributeId PK,FK |
| AttributeValue | | AttributeType | | AttributeName |
-------------------- ------------------ ---------------------------
The AttributeValue column is implemented as an sql_variant column and I've implemented an NHibernate.UserTypes.IUserType for it.
I can create an EntityAttribute entity and persist it directly so that part of the hierarchy is working.
I'm just not sure how to map the EntityAttributes collection to the Entity entity.
Note the EntityAttributes table could (and does) contain multiple rows for a given EntityId/AttributeId combination:
EntityId AttributeId AttributeValue
-------- ----------- --------------
1 1 Blue
1 1 Green
StringAttributes row looks like this for this example:
StringAttributeId AttributeName
----------------- --------------
1 FavoriteColor
How can I effectively map this data model to my Entity domain such that Entity.Attributes("FavoriteColors") returns a collection of favorite colors? Typed as System.String?
here goes
class Entity
{
public virtual int Id { get; set; }
internal protected virtual ICollection<EntityAttribute> AttributesInternal { get; set; }
public IEnumerable<T> Attributes<T>(string attributeName)
{
return AttributesInternal
.Where(x => x.Attribute.Name == attributeName)
.Select(x => x.Value)
.Cast<T>();
}
}
class EntityAttribute
{
public virtual Attribute Attribute { get; set; }
public virtual object Value { get; set; }
}
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
HasMany(e => e.AttributesInternal)
.Table("EntityAttributes")
.KeyColumn("EntityId")
// EntityAttribute cant be an Entity because there is no real Primary Key
// (EntityId, [AttributeId] is not unique)
.Component(c =>
{
c.References(ea => ea.Attribute, "AttributeId").Not.LazyLoad();
c.Map(ea => ea.Value, "AttributeValue").CustomType<VariantUserType>();
});
}
}