I want to write a simple query to retrieve a list of USER with a simple restriction on CUSTOMER joined table.
I'm only interested by the USER entity.
If I write it using HPQL :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getCriteriaForObsAdmin("USER");
crit.createCriteria("clients").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
StringBuilder hqlQuery = new StringBuilder().append("select u from Users as u join u.customers as c where c.idCustomer=:idCustomer");
Query q = getSessionAndManageFilter().createQuery(hqlQuery.toString());
q.setInteger("idCustomer", pCustId);
return q.list();
}
The SQL generated only Select all the fields from USER entity, as expected.
Now if I write it through Hibernate criteria API :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getSession().createCriteria(Users.class);
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
crit.createCriteria("customers").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
}
The SQL generated Select all the fields from USER entity but also from the CUSTOMER entity.
I'm using hibernate-core 3.3.1.GA.
I know I can use projection to work around the issue but my object will be transient, I also validated it works using a subquery to do my restrictions.
But I'm not happy with using workarounds and I do not understand why it would behave differently between the 2 code examples ?
It doesn't make sense to me to select fields outside of the asked entity.
Related
I'm trying to select an object using values of another object in LINQ SQL,
I currently have this,
var result1 = (from s in pdc.ScanLogs
from ec in pdc.ExhibitsContacts
where s.ExhibitID == ec.ExhibitID
select ec.Contact);
I want to assign a value of ec.Contact.Note = ec.Comment;
Is there to a way to do this in LINQ SQL without writing multiple queries?
I read this blog article: http://blog.robvolk.com/2009/05/linq-select-object-but-change-some.html but it doesn't seem to work with LINQ SQL.
Basically you can't do this. LINQ is meant to be a query language, and what you want to do is mutate existing entities with your query. This means your query would have side effects and this is not something that is supported by LINQ to SQL.
While this won't work in a single query while returning LINQ to SQL entities, what will work is when you return simple DTO structues. For instance:
var result1 =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select new ContactDto
{
Id = ec.Contact.Id,
Note = ec.Comment,
SomeOtherFields = ec.Contact.SomeOtherFields
};
As a side note: also look at how I removed the where s.ExhibitID == ec.ExhibitID join from the query, by just using the ExhibitsContacts property of the ScanLog entity (which will be generated by LINQ to SQL for you when your database schema has the proper foreign keys defined).
Update:
When you need to return those DTO from several methods, you might consider centralizing the transformation from a collection of entities to a collection of DTO objects. What I tend to do is place this method on the DTO (which makes it easy to find). The code might look like this:
public class ContactDto
{
// Many public properties here
public static IQueryable<ContactDto> ToDto(
IQueryable<Contact> contacts)
{
return
from contact in contacts
select new ContactDto
{
Id = contact.Id,
Note = contact.ExhibitsContact.Comment,
ManyOtherFields = contact.ManyOtherFields
};
}
}
The trick with this static transformation method is that it takes an IQueryable and returns an IQueryable. This allows to to simply specify the transformation and let LINQ to SQL (or any other LINQ enabled O/RM) to efficiently execute that LINQ expression later on. The original code would now look like this:
IQueryable<Contact> contacts =
from s in pdc.ScanLogs
from ec in s.ExhibitsContacts
select ec.Contact;
IQuerable<ContactDto> result1 = ContactDto.ToDto(contacts);
the problem is that LINQ to SQL does not know how to interpret your extension method. The only way, other than using stored procedures from LINQ to SQL (which kind of defeats the ponit), is to get the object, update and then commit changes.
I am new to linq to sql
I wrote this function:
public ICollection<ICustomer> GetAll()
{
DataClasses1DataContext context = new DataClasses1DataContext();
var customers = from customer in context.Customers select customer;
return customers.ToList().Cast<ICustomer>().ToList();
}
But it always return list of null values.
The database contain 3 records "filled with data" but this function return 3 nulls.
how to fix that?
It may not be able to cast the results properly, have you made your partial Customer object implement ICustomer? If not, that is the reason.
Also you don't have to bring it to a list twice, or even once for that matter since you aren't returning a list, it might be more appropriate to change your signature to List or IEnumerable depending on your usage.
You can test whether or not the cast is succeeding by doing a simple test.
DataClasses1DataContext context = new DataClasses1DataContext();
var customers = from customer in context.Customers select customer;
int numberOfCustomers = customers.Count();
var myCustomers = customers.Cast<ICustomer>(); //you could also do .OfType<ICustomer>();
int numberOfICustomers = myCustomers.Count();
If numberOfCustomers is 3 and numberOfICustomers is 0 then you know that was the issue.
Your problem is almost certainly at the .Cast() method (confirm this by stepping through your code & ensuring that customers is populated correctly).
Does the Customer object implement the ICustomer interface? It sounds like an obvious thing to check but that would be a likely problem.
I am hoping you can help. I am developing a tiered website using Linq to Sql. I created a new class(or object) in DBML designer called memberState. This object is not an actual table in the database. I have this method in my middle layer:
public override IEnumerable(memberState) GetMembersByState(string #state)
{
using (BulletinWizardDataContext context = DataContext)
{
IEnumerable(memberState) mems = (from m in context.Members
join ma in context.MemberAddresses
on m.UserId equals ma.UserId
join s in context.States
on ma.StateId equals s.StateId
where s.StateName == #state
select new memberState
{
userId = m.UserID,
firstName = m.FirstName,
middleInitial = m.MiddleInitial,
lastName = m.LastName,
createDate = m.CreateDate,
modifyDate = m.ModifyDate
}).ToArray(memberState)();
return mems;
}
}
The tables in my joins (Members, States, and MemberAddresses are actual tables in my Database). I created the object memberStates so I could use it in the query above (notice the Select New memberState. When the data is updated on the web page how do I persist the changes back to the Member Table? My Member Table consists of the following columns: UserId, FirstName, MiddleInitial, LastName, CreateDate, ModifyDate. I am not sure how save the changes back to the database.
Thanks,
If I remember correctly, you can create a view from the different tables (Members, States, and MemberAddresses) and add that to the data context. Then any modifications to data in the view object can be saved, and linq to sql will handle the commit correctly as long as all the relationships are clearly setup/defined in both the database and in the data context.
If you have a Member table, the dbml will most likely contain a Member class. To update a member in the database, you will have to create a new Member object, and the Attach it to the BulletinWizardDataContext.Members collection. Something similar to the following code should the trick (I have not tested the code):
using (BulletinWizardDataContext context = DataContext)
{
Member m = new Member() { UserId = userId };
context.Members.Attach(m);
m.FirstName = firstName;
// Set other properties
context.SubmitChanges();
}
Attach must be called before setting the properties. Also, Linq2Sql has some issues with Attach in the case where the properties of your object are set to default values (i.e. 0 for numeric values, false for booleans, null for string etc.). In this case Attach will not generate the correct SQL.
var m = myContext.Members.Single(m=> m.UserID == myMemState.userID);
m.FirstName = myMemState.firstName;
m.MiddleInitial = myMemState.middleInitial;
...
That would be the quick way. It does an additional roundtrip to the db, but will work well. If that's an issue for you, then do Attach like Jakob suggested. For that you have to have to do some extra steps, like reviewing the configuration for optimistic updates and make sure you have the original fields when doing the attach.
We have followed the approach below to get the data from multiple results using LINQ To SQL
CREATE PROCEDURE dbo.GetPostByID
(
#PostID int
)
AS
SELECT *
FROM Posts AS p
WHERE p.PostID = #PostID
SELECT c.*
FROM Categories AS c
JOIN PostCategories AS pc
ON (pc.CategoryID = c.CategoryID)
WHERE pc.PostID = #PostID
The calling method in the class the inherits from DataContext should look like:
[Database(Name = "Blog")]
public class BlogContext : DataContext
{
...
[Function(Name = "dbo.GetPostByID")]
[ResultType(typeof(Post))]
[ResultType(typeof(Category))]
public IMultipleResults GetPostByID(int postID)
{
IExecuteResult result =
this.ExecuteMethodCall(this,
((MethodInfo)(MethodInfo.GetCurrentMethod())),
postID);
return (IMultipleResults)(result.ReturnValue);
}
}
Notice that the method is decorated not only with the Function attribute that maps to the stored procedure name, but also with the ReturnType attributes with the types of the result sets that the stored procedure returns. Additionally, the method returns an untyped interface of IMultipleResults:
public interface IMultipleResults : IFunctionResult, IDisposable
{
IEnumerable<TElement> GetResult<TElement>();
}
so the program can use this interface in order to retrieve the results:
BlogContext ctx = new BlogContext(...);
IMultipleResults results = ctx.GetPostByID(...);
IEnumerable<Post> posts = results.GetResult<Post>();
IEnumerable<Category> categories = results.GetResult<Category>();
In the above stored procedures we had two select queries
1. Select query without join
2. Select query with Join
But in the above second select query the data which is displayed is from one of the table i.e. from Categories table. But we have used join and want to display the data table with the results from both the tables i.e. from Categories as well as PostCategories.
Please if anybody can let me know how to achieve this using LINQ to SQL
What is the performance trade-off if we use the above approach vis-à-vis implement the above approach with simple SQL
Scott Guthrie (the guy who runs the .Net dev teams at MS) covered how to do this on his blog some months ago much better than I ever could, link here. On that page there is a section titled "Handling Multiple Result Shapes from SPROCs". That explains how to handle multiple results from stored procs of different shapes (or the same shape).
I highly recommend subscribing to his RSS feed. He is pretty much THE authoritative source on all things .Net.
Heya dude - does this work?
IEnumerable<Post> posts;
IEnumerable<Category> categories;
using (BlogContext ctx = new BlogContext(...))
{
ctx.DeferredLoadingEnabled = false; // THIS IS IMPORTANT.
IMultipleResults results = ctx.GetPostByID(...);
posts = results.GetResult<Post>().ToList();
categories = results.GetResult<Category>().ToList();
}
// Now we need to associate each category to the post.
// ASSUMPTION: Each post has only one category (1-1 mapping).
if (posts != null)
{
foreach(var post in posts)
{
int postId = post.PostId;
post.Category = categories
.Where(p => p.PostId == postId)
.SingleOrDefault();
}
}
Ok. lets break this down.
First up, a nice connection inside a using block (so it's disposed of nicely).
Next, we make sure DEFERRED LOADING is off. Otherwise, when u try and do the set (eg. post.Category == blah) it will see that it's null, lazy-load the data (eg. do a rountrip the database) set the data and THEN override the what was just dragged down from the db, with the result of there Where(..) method. phew! Summary: make sure deferred loading is off for the scope of the query.
Last, for each post, iterate and set the category from the second list.
does that help?
EDIT
Fixed it so that it doesn't throw an enumeration error by calling the ToList() methods.
Just curious, if a Post have have one or many Categories, is it possible to instead of using the for loop, to load the Post.PostCategories with the list of Categories (one to many), all in one shot, using a JOIN?
var rslt = from p in results.GetResult<Post>()
join c in results.GetResult<Category>() on p.PostId = c.PostID
...
p.Categories.Add(c)
To simplify my current situation lets say I have 2 tables (legacy so can't touch the schema to play better with Linq)
node
Columns: key_field1, key_field2, [lots of other fields]
node_children
Columns: parent_key_field1, parent_key_field2, child_key_field1, child_key_field2
node_children is similar to the has_and_belongs_to_many join table from Rails... except that both ids refer to the same table.
Ideally I would like to have a Node Class which has a Property Children, which returns the right List?
Results should be equivalent to
select * from node
join node_children
on node.key_field1 = node_children.child_key_field1 and node.key_field2 = node_children.child_key_field2
where node_children.parent_key_field1 = #paramX and node_children.parent_key_field2 = #paramY
Whats the best way to model this in Linq to Sql?
This is what I came up with (works too!)
Open up the ORM generated Node class MyDatabase.cs
partial class Node
{
public IEnumerable<Node> Children
{
get
{
MyDatabaseDataContext dc = new MyDatabaseDataContext();
return from link in this.ChildLinks
join node in dc.Nodes on new { Site = link.child_key_field1, ID = link.child_key_field2 } equals new { Site = node.key_field1, ID = node.key_field2 }
select node;
}
}
}
The ORM designer automatically adds an association between the two tables. I renamed the property on the Node class to ChildLinks.
(This drove me up the wall for a couple of hours) For the LINQ join to work with composite keys, you need the Name as well as Order of each part to match. See How to: Join by Using Composite Keys
One of these days, I'm gonna complete the LINQ Getting Started set of articles :)