I have a collection of manufacturers, each of them makes several products. Manufacturers can have string tags, and I would like to search the products by their manufacturer tag (such that tags are inherited from the manufacturer by the product).
One way of doing that is to create an index which, in the TransformResults part, adds the manufacturer's tags to each product.
from product in results
select new {
...,
Tags = Database.Load("manufacturers/"+ product.ManufacturerId).Tags
}
But I cannot seem to be able to query it:
this.Query<T>("TaggedProducts").Where(s => s.Tags.Any(tag => tag=="reliable"));
because Tags is not indexed. I am using RavenDB Studio.
This is the map:
from product in docs.Products
select new {
product.Id,
product.ManufacturerId
}
It is unclear to me whether I need to define a Field, I couldn't find any documentation explaining that.
These are the classes:
class Manufacturer
{
public int Id {get; set;}
public List<string> Tags {get; set;}
}
class Product
{
public int ManufacturerId {get; set;}
public int Id {get; set;}
}
Note that I am trying to avoid adding a Tags field to the Product class, if that's possible, because that would give the false impression that tags can be set in stories.
What you put in the Map is what is being indexed and what you put in TransformResults is what you get back from a successful query. Therefore you can never query the tags unless you add them in the map.
What you could do is to use LoadDocument in the map. It is a fairly new feature that was made available in 2.0. The docs are here: http://ravendb.net/docs/2.0/client-api/querying/static-indexes/indexing-related-documents
This is what the map could look like in your case perhaps:
from product in docs.Products
select new {
product.Id,
product.ManufacturerId,
Tags = LoadDocument("manufacturers/"+ product.ManufacturerId).Tags
}
The TransformResults you are using could remain the same.
A tip is to use the management studio and look what is indexed. You can do so in Indexes -> [your index] -> Query and then check the Index entries checkbox from the Query Options drop down.
Related
In LINQ I have written a simple query where I am searching for an animal using the ID property. However, I am also including the Farm the animal belongs using the Include property.
I want to write the same LINQ query in SQL where I can include Farm. How can I include Farm using SQL. I have an incomplete SQL syntax below. Can anyone help me out.
LINQ
await _dbContext.Animals.Where(x => x.id == 1)
.Include(x => x.Farm)
.ToListAsync();
SQL
select * from Animals where id = 1;
Apparently your database has a table with Animals and a table with Farms. There seems to be a one-to-many relation between Animals and Farms: on every Farm live zero or more Animals; every Animal lives on exactly one Farm, namely the Farm that the foreign key refers to.
I think you will have classes similar to the following:
class Farm
{
public int Id {get; set;}
public string Name {get; set;}
... // etc
// Every Farm has zero or more Animals (one-to-many)
public virtual ICollection<Animal> {get; set;}
}
class Animal
{
public int Id {get; set;}
public string Name {get; set;}
... // etc
// Every Animal lives on exactly one Farm, using foreign key
public int FarmId {get; set;}
public virtual Farm Farm {get; set;}
}
I want to write the same LINQ query in SQL where I can include Farm.
A small trick: if you want to know the SQL code generated by Entity Framework, use property DbContext.Database.Log.
using (var dbContext = new DbContext())
{
// Log generated SQL to debug window:
dbContext.Database.Log = System.Diagnostics.Debug.Write;
// execute your LINQ:
var fetchedAnimals = _dbContext.Animals.Where(x => x.id == 1)
.Include(x => x.Farm)
.ToList();
}
Write your own SQL
You'll have to join Animals with Farms, and keep only the Animal with ID = 1:
See SQL Join
// Select only the properties of Animals and Farms that you actually plan to use
SELECT Animals.Id, Animals.Name, ...,
Farms.Id, Farms.Name, ...
FROM Animals INNER JOIN Farms
ON Animals.FarmId = Farm.Id
WHERE Animals.Id = 1
You should not use "" to fetch everything. If Farm [10] Has 5000 Chickens, then every Chicken will have a foreign key with a value 10. If you use "" you will transfer this value 10 more than 5000 times, while you already know the value of the foreign key.
There's room for improvement
When using entity framework to fetch data, always use Select, and select only the properties that you plan to use, even if you Select all properties. Only omit Select and / or use Include if you plan to change / update the fetched data.
The reason is, that fetching data without using Select is not very efficient.
If you fetch data without using Select, entity framework will put the fetched item in the DbContext.ChangeTracker, together with a copy of the fetched item. You get a reference to the copy. Whenever you change properties of the fetched item, you change the copy in the ChangeTracker. When you call DbContext.SaveChanges, the original is compared with the copy, property per property to see which properties are changed, and thus need to be updated in the database.
So if you don't plan to change the fetched data, it would be a waste of processing power to put this data AND a copy in the ChangeTracker. Hence: always use Select, unless you plan to update the fetched data.
I have two tables:
dbo.Dashboards
Id (int PK) Title(nvarchar) WidgetIds(nvarchar)
1 Test [1,2]
dbo.Widgets
Id (int PK) Details(nvarchar)
1 {'text': 'some data'}
2 {'text': 'test'}
Expected output:
Dashboard.Id Dashboard.Title Widget.Id Widget.Details
1 Test 1 {'text': 'some data'}
1 Test 2 {'text': 'test'}
I would like to get dashboards with assigned widgets by using Entity Framework.
My first solution is to get dbo.Dashboards and then dbo.Widgets. After that I can merge it in a backend, but it is not the best practice.
Is there any option to get Dashboards with assigned Widget list?
Function Include() is not working because there isn't FK relationship between tables.
It seems to me that you have a many-to-many relationship between Dashboards and Widgets: Every Dashboard has zero or more Widgets and every Widget is used by zero or more Dashboards.
In a proper database you would have a separate junction table. Apparently you chose not to use this pattern, but create a string that contains a textual representation of the widgets that a 'Dashboard` has.
If you plan to create a serious application I strongly advise you to
use the standard pattern in many-to-many relationships
If you don't, all your queries will be more difficult. Imagine the problems you'll experience if you want to delete a Widget. You'd have to check the textual representation of every Dashboard to check if the widget that you want to remove is used somewhere and change it.
If you want to configure your many-to-many relations ship according to the Entity Framework Code-First Conventions, you will have something like this:
class Dashboard
{
public int Id {get; set;}
public string Title {get; set;}
// every Dashboard has zero or more Widgets
public virtual ICollection<Widget> Widgets {get; set;}
... // other properties
}
class Widget
{
public int Id {get; set;}
// every Widget is used in zero or more Dashboards
public virtual ICollection<Dashboard> Dashboards{get; set;}
... // other widget properties
}
class MyDbContext : DbContext
{
public DbSet<Dashboard> Dashboards {get; set;}
public DbSet<Widget> Widgets {get; set;}
}
Because you stuck to the conventions, this is all that entity framework needs to know to understand that you want to configure a many-to-many relationship between Dashboards and Widgets. Entity Framework will create the junction table for you. It will automatically update this table whenever you add a Widget to a Dashboard. It will also create the proper joins whenever you want to fetch Dashboards with their Widgets, or Widgets with the Dasheboards that use them.
Your query will be fairly simple:
var DashBoardsWithTheirWidgets = myDbcontext.Dashboards
// I only want to see the super dashboards
.Where(dashboard => dashboard.Type = DashboardType.Super)
.Select(dashboard => new
{
// Select only the properties you plan to use:
Id = dashboard.Id,
Title = dashboard.Title,
// select only the Widgets you plan to use:
Widgets = dashboard.Widgets
.Where(widget => widget.Price > 100.00)
.Select(widget => new
{
// again select only the properties you plan to use
Name = widget.Name,
Price = widget.Price,
})
.ToList();
});
See how easy it is if you stick to the conventions?
If you really want your obscure method of using foreign keys, you need a function to remove the square brackets and the commas from the widgetIds, split the string into sub-strings, Parse them to numbers, and do a join.
But before you plan to continue on this path, experiment on how to add a Widget and a Dashboard. How to add a Widget to a Dashboard, how to remove a Widget. I think the time needed to reform your database into proper format is much less than the time you'll need to implement those functions
Solution 1:
You need to restructure the dbo.dashboards table. Change the column layout of dbo.dashboards to
Auto_Generated_ID, Unique_Identifier(PK), Title, WidgetIds
I know the above column restructuring is done in a bad way. But still this will work in your case.
After redesigning it you can use join between dbo.dashboards and dbo.widgets to retrieve it in an efficient way.
Solution 2:
The below-normalized tables will work in your case
dbo.dashboard
id, title (columns)
dbo.dashboard_widget
id, dashboard_id, widget_id (columns)
dbo.widgets
id, details (columns)
Query:
select d.id, d.title, dw.widget_ids, w. details from dbo.dashboard d INNER JOIN dbo.dashboard_widget dw ON d.id = dw.dashboard_id INNER JOIN dbo.widgets w ON dw.widget_id = w.id where d.id = << id number >>
I understand that you can use filters in NHibernate to only load parts of child collections. I would like to be able to do this using the Criteria API, instead of HQL. Is this possible?
Some specifics from my application:
I'm trying to load the invoices for a given vendor that occur after a given date, and have positive line items.
The entities look something like this:
public partial class Vendor
{
public virtual string Name {get; set;}
//A bunch of other properties
public virtual ICollection<Invoice> Invoices {get; protected set;}
}
public partial class Invoice
{
public virtual DateTime? Date {get; set;}
public virtual ICollection<LineItem> LineItems {get; protected set;}
}
public partial class LineItem
{
public virtual decimal Amount {get; set;}
}
Originally, I was just pulling line items, but now they want the user to be able to input a bunch of criteria to select the vendor. I already have the UI/code to allow the user to specify vendors using the Criteria API, so I'd like to apply a filter to only pull the LineItems that are positive for Invoices that occur after a given date. Is this possible using the Criteria API? If not, can I at least get away without adding the filter-defs to my mapping file?
Updated the answer, didn't read it well the first time... I don't know if this will help but I ran into the same issue where I only wanted actives being pulled of a child collection. This uses queryover but it's a wrapper on top of criteria so it can be done...
Here it is...
I started using NHibernate today, but I cannot figure out how I setup a simple relation between two tables. I don't really know what it's called, it could be one-to-many or foreign key relation (I'm not that into database design and the terms used), but here's a very simple example.
I have a table Product with attributes Id (PK), ProductName and CategoryId. Then I have a table Categories with attributes Id (PK) and CategoryName.
I created these classes:
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductName { get; set; }
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual string CategoryName
{
get { return this.Category == null ? String.Empty : this.Category.CategoryName; }
}
}
public class Category
{
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
}
In other words, I simply want the Product to store to which category it belongs (via the CategoryId attribute which points to an Id in the Categories table). I don't need the Category class to hold a list of related Products, if that makes it any simpler.
To make it even more clear what I'm after, this is the SQL that I'm expecting:
SELECT Products.*, Categories.*
FROM Products INNER JOIN Categories ON Products.CategoryId = Categories.Id
at least that's what I think it should be (again, I'm not that good at database design or queries).
I can't figure out which kind of mapping I need for this. I suppose I need to map it in the Product.hbm.xml file. But do I map the CategoryId as well? And how do I map the Category property?
It seems like I would need a 'one-to-many' relation since I have ONE category per product (or is this reasoning backward?) but it seems like there is no one-to-many mapping...
Thanks for any help!
Addition:
I tried to add the many-to-one relation in the Person mapping, but I keep getting an exception saying "Creating proxy failed", and in the inner exception "Ambiguous match found".
I should maybe mention I am using an old version of NHibernate (1.2 I think) because that is the only one I got running with MS Access due to it not finding the JetDriver in newer versions.
I've put the mapping files, classes, and code where the error occurs in screenshots because I can't figure out how to post XML code here... It keeps reading it as html tags and skipping half of it. Anyway.
The mappings:
http://www.nickthissen.nl/Images/tmp7B5A.png
The classes:
http://www.nickthissen.nl/Images/tmpF809.png
The loading code where the error occurs:
http://www.nickthissen.nl/Images/tmp46B6.png
(As I said, the inner exception says "Ambiguous match found".
(Product in my example has been replaced by Person)
The Person and Category classes inherit Entity which is an abstract base class and defines the Id, Deleted, CreatedTime and UpdatedTime properties.
The code where the error occurs is in a generic 'manager' class (type parameter TEntity which must inherit Entity). It is simply supposed to load all entities with the Deleted attribute false. In this case, TEntity is 'Person'.
It works fine if I leave out the many-to-one Category mapping in the Person mapping, but then obviously the Category property is always null.
Oh yeah, sorry about the mix between C# and VB, the C# code is in a generic framework I use for multiple projects while the VB part is the actual implementation of that framework on my website and I just happened to use VB for that.
Help? Thanks!
In your Product class only needs to contain a Category object, you don't need a CategoryId property. Then in your Product mapping you need to have this entry
<many-to-one name="Category" column="CategoryId" />
UPDATE:
Your mappings appear to be missing the fully qualified name of the mapped class in the tag. See http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-class
UPDATE 2:
See if this helps you NHibernate 1.2 in a .NET 4.0 solution
The 'Ambiguous match found' exception was caused by the project targeting .NET Framework 4, which does not seem to be compatible with NHibernate 1.2.1. I switched to 3.5 and that seems to solve that particular issue.
Now on to the next. As you can see, the Person class has a CategoryName property that should return the name of the current Category object, or an empty string if the category happens to be null. This is so I can databind a collection of Person objects to a grid, specifying 'CategoryName' as a property to bind a column to.
Apparently this does not work with NHibernate. Whenever I try to databind my collection of persons, I get this exception:
"Property accessor 'CategoryName' on object 'NHibernateWebTest.Database.Person' threw the following exception:'Could not initialize proxy - the owning Session was closed.'"
This occurs on the 'DataBind' method call in this code:
public virtual void LoadGrid()
{
if (this.Grid == null) return;
this.Grid.DataSource = this.Manager.Load();
this.Grid.DataBind();
}
(This is an ASP.NET project and 'Grid' is a GridView)
'this.Manager' returns an existing instance of NHibernateEntityManager, and I've already shown its Load method before, it contains this:
public virtual EntityCollection Load()
{
using (ISession session = this.GetSession())
{
var entities = session
.CreateCriteria(typeof (TEntity))
.Add(Expression.Eq("Deleted", false))
.List();
return new EntityCollection(entities);
}
}
(THere's some generic type parameters in there but this website seems to hide them (due to the html like tags I guess)... Sorry about that).
This may have something to do with NHibernate itself, as I said I'm completely new to this. When I call my Load method I would expect it to return an EntityCollection(Of Person) with all its properties already set. It seems I have to keep the ISession open while I am databinding for some reason..? That seems a little strange...
Can I get around this? Can I make my Load method simply return a collection of persons already fully loaded, so that I can access CategoryName whenever I want?
Wait... Is this lazy loading perhaps?
I am using fluent nhibernate.
example:
i have 3 tables i.e.
CUSTOMER
CustomerId pk
CustomerName
PRODUCT
ProductId pk
ProductName
Cust_Product
cust_prodId pk
ProductId fk
CustomerId fk
Now, I want to show customername, productnae
so, how do i write mapping class for the same.
i want to use
session.CreateCriteria(typeof("className")).List()
like this. how do i do this..?
If you're looking for a full tutorial on how to do this, I recommend the FNH wiki or one of the many blog postings which can be found through Google.
However, you're trying to implement a many-to-many relationship here, and that seems to throw a lot of people off. Here's a rough guide:
On your Customer class, you'll need a collection like:
IList<Product> Products { get; private set; }
And similarly, on your Product class:
IList<Customers> Customers { get; private set; }
You start off a many-to-many map with the HasManyToMany function:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
// other mappings
HasManyToMany<Product>(x => x.Products)
.WithTableName("Cust_Product") // Specifies the join table name
.WithParentKeyColumn("CustomerId") // Specifies the key joining back to this table (defaults to [class]_id, Customer_id in this case)
.WithChildKeyColumn("ProductId")
.FetchType.Join(); // Instructs NHibernate to use a join instead of sequential select
}
}
Then repeat the process for the other side of the relationship (the Customers property on the Product class).