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.
Related
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 am trying to perform an aggregate count() just like a SQL query would against my database, but instead of SQL I want to use LINQ.
I am trying to use LINQ to query my Entity Framework Data Model and perform an aggregate sum(). Specifically I want to do a count of the column(TimeWorked) in the TimeEntry table grouping by Project Name and Phase between 2 specific dates and then do a natural join on the Project table. I am then going to take that query result and load it into an observable collection and display it in a ListView.
My desired result is [ProjectName],[Phase],[Count(TimeWorked)],[Date]
I want to filter the counting to only Projects with a TimeEntry that is between two dates that for the sake of this example will just be called Date1 and Date2.
I am not familiar with LINQ 100% yet, I am still learning so please excuse my lack of LINQ terminology.
Here is a picture of my Relationship and tables.
DB Schema
Here are the data types
Project Table data types,
TimeEntry table
I have been looking everywhere and can't seem to find any good resources or examples. Could someone please point me in the right direction.
So you have projects and timeentries. There is a one-to-many relation between a Project and a TimeEntry: every Project has zero or more TimeEntries, every TimeEntry belongs to exactly one Project.
If you'd followed the entity framework code first conventions, you would have created classes like this:
class Project
{
public int Id {get; set;}
// every Project has zero or more TimeEntries:
public virtual ICollection<TimeEntry> TimeEntries {get; set;}
... // other properties
}
class TimeEntry
{
public int Id {get; set;}
// every TimeEntry belongs to exactly one Project using foreign key:
public int ProjectId {get; set;}
public virtual Project Project {get; set;}
... // other properties
}
class MyDbContext : DbContext
{
public DbSet<Project> Projects {get; set;}
public DbSet<TimeEntry> TimeEntries {get; set;}
}
Because you followed the conventions, this would be enough to inform entity framework that you planned a one-to-many. Entity Framework would be able to detect the primary and foreign keys and the relation between Projects and TimeEntries (possible problem: pluralization of time entry).
If you want different table names or column names, you'll need attributes or fluent API. But the structure remains similar.
So now you have your Project and TimeEntries. You want for every Project the Number of TimeEntries where TimeWorked is in a given time interval (are you sure? you want the Count, not the sum of time worked?)
I'd go for this:
var projectWithCountTimeWorked = dbContext.Projects
.Select(project => new
{
ProjectName = project.ProjectName,
...
// the Count of TimeEntries of this project in this period:
CountTimeWorked = project.TimeEntries
.Where(timeEntry => minDate <= timeEntry.TimeWorked
&& timeEntry.TimeWorked <= maxDate)
.Count(),
});
Because I used the ICollections, entity framework will internally do the proper joins to calculate the result.
If you want to specify the joins yourself I'd go for this:
var result = dbContext.Project // GroupJoin Projects
.GroupJoin(dbContext.TimeEntries // and TimeEntries
project => project.Id, // from every Project take the Id
timeEntry => timeEntry.ProjectId, // from every timeEntry take the ProjectId
(project, timeEntries) => new // for every Project and his matching
{ // timeEntries make a new object
Name = project.Name,
...
CountTimeWorked = timeEntries // count all timeEntries during the period
.Where(timeEntry => minDate <= timeEntry.TimeWorked
&& timeEntry.TimeWorked <= maxDate)
.Count(),
If you are unfamiliar with entity-framework code first basics. This web site helped me a lot to get me on track
This article was a good summary for me to have a look at most used linq methods
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.
I am using NHibernate in a web application I'm building. The user can subscript to zero or more mailing-lists (there are a total of 8). This is represented on the screen with a checkbox for each mailing-list.
I would like to use NHibernate to update these in one go. A very simple sql query would be:
update mail_subscriptions set subscribed = true where mailing_list_id in (21,14,15,19) and user_id = 'me'
What is the cleanest way to perform this update via NHibernate so that I can make a single round trip to the database?
Thanks in advance
JP
NHibernate might not be able to update the mail_subscriptions in the way you have shown above but it can do it in a single round trip to the DB using batched queries.
This example considers Subscriptions mapped as a HasMany using Component although roughly the same technique can be used if the mapping was just a plain HasMany. I am also assuming that each user already has rows in the mail_subscriptions table for each mailing list set to false for subscribed.
public class User{
public virtual string Id {get; set;}
public virtual IList<MailSubscription> Subscriptions {get; set;}
}
public class MailSubscription{
public virtual int ListId {get; set;}
public virtual bool Subscribed {get; set;}
}
public void UpdateSubscriptions(string userid, int[] mailingListIds){
var user = session.Get<User>(userid);
foreach(var sub in
user.Subscriptions.Where(x=> mailingListIds.Contains(x.ListId))){
sub.Subscribed=true;
}
session.Update(user);
}
Now when the unit of work completes you should see SQL like this produced sent as a single round trip to the DB.
update mail_subscriptions set subscribed=true where user_id='me' and listid=21
update mail_subscriptions set subscribed=true where user_id='me' and listid=14
update mail_subscriptions set subscribed=true where user_id='me' and listid=15
update mail_subscriptions set subscribed=true where user_id='me' and listid=19
I think the NHibernate feature you seek is known as Executable DML.
Ayende has a blog post giving an example at http://ayende.com/blog/4037/nhibernate-executable-dml .
Depending on your names of your entities and their properties, and assuming you have an ISession instance variable called session, you would need to execute an HQL query something like:
session.CreateQuery("update MailSubscriptions set Subscribed = true where MailingList.Id in (21,14,15,19) and User.Id = 'me'")
.ExecuteUpdate();
Now, having said that, I think in the use case you describe (updating a handful of entries within a collection on a single aggregate root), there is no need to use Executable DML. Mark Perry has the right idea - you should simply modify the booleans on the appropriate entities and flush the session in the usual way. If ADO.NET batching is configured appropriately, then the child entries will cause multiple update statements to be sent to the RDBMS in a single database call.
I am working on an NHibernate project and have a question regarding updating transient entities.
Basically the workflow is as follows:
Create a DTO (projection) and send over the wire to client. This has a small subset of properties from the entity.
Client sends back the changed DTO
Map the DTO properties back onto the appropriate enitity so an UPDATE statement can be generated and executed by NH.
Save the entity
Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.
What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?
Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.
Essentially I need a way to specify which entity properties are dirty so only these are included in the update.
== EDIT ==
For example...
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Nickname { get; set; }
public virtual string Surname { get; set; }
public virtual DateTime BirthDate { get; set; }
}
And the test case.
// Create the transient entity
Person p = new Person()
p.id = 1;
using (ISession session = factory.OpenSession())
{
session.Update(p);
// Update the entity – now attached to session
p.Firstname = “Bob”;
session.Flush();
}
I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?
== /EDIT ==
Thanks in advance,
John
Dynamic-update is what you're looking for. In your mapping file (hbm.xml):
<class name="Foo" dynamic-update="true">
<!-- remainder of your class map -->
Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.
UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var p = new Person {PersonId = 1};
session.Lock(p, LockMode.None); // <-- This is the secret sauce!
p.Firstname = "Bob";
// No need to call session.Update(p) since p is already associated with the session.
tx.Commit();
}
(N.B. dynamic-update="true" is specified in my mapping.)
This results in the following SQL:
UPDATE Person
SET Firstname = 'Bob' /* #p0_0 */
WHERE PersonId = 1 /* #p1_0 */