I have inherited an ASP.NET website built on NHibernate, with which I have no experience. I need to add a calculated field based on a column in a related table to an existing query. In SQL, this would be done easily enough using a correlated subquery:
select
field1,
field2,
(select count(field3) from table2 where table2.table1ID = table1.ID) calc_field
from
table1
where
[criteria...]
Unfortunately, of course, I can't use SQL for this. So in reality, I have three related questions:
What is the best way to trace through the web of interfaces, base classes, etc used by NHibernate in order to pinpoint the object where I need to add the field?
Having located that object, what, if anything, has to be done besides adding a public property to the object corresponding to the new field?
Are there any NHibernate-specific considerations with regard to referencing a related object in a query?
Here is the existing code that performs the search:
public INHibernateQueryable<C> Search(ISearchQuery query, string sortField)
{
_session = GetSession();
var c = _session.Linq<C>();
c.Expand("IP");
c.Expand("LL");
c.Expand("LL.Address");
c.Expand("LL.Address.City");
c.Expand("LL.Address.City.State");
c.Expand("LL.Address.City.County");
c.Expand("CE");
c.Expand("IC");
c.Expand("AR");
c.Expand("ER");
c.Expand("Status");
var res = _SearchFilters
.Where(x => x.ShouldApply(query))
.Aggregate(c, (candidates, filter) => (INHibernateQueryable<C>) filter.Filter(candidates, query));
res = SortSearch(res, sortField);
return res;
}
I appreciate any advice from experienced Hibernators.
Thanks,
Mike
If you are only interested in returning a query containing a computed value, you can still call a stored procedure in NHibernate and map the results to a POCO in the same way as you map a table for CRUD operations; obviously read-only instead of updatable.
Have a look at the ISession.CreateSQLQuery method; I can post an example from one of my projects if you need one.
Related
I am learning asp.net core mvc and API. I can simply work on it for CRUD operation. But, I get confused for accessing data from multiple tables like listing all categories with showing number of items each categories contains. What I need to learn for example Lina, entity framework code first, ado.net? I am currently using entity framework code first.
Thanks
Learn Dapper a simple object mapper for .NET.
What I Understood from your problem is that you have multiple tables and you want to query them and map the query result in your c# or vb code.Here I will show simple mapping of query result to c# objects.
Suppose you have three tables category_1,category_2 and category_3.
Lets say each table have two columns named itemName and ItemValue.
Lets create c# class corresponding to each table.
class category_1{
string ItemName {get;set};
int ItemValue {get;set;};
}
class category_2{
string ItemName {get;set};
int ItemValue {get;set;};
}
class category_3{
string ItemName {get;set};
int ItemValue {get;set;};
}
Suppose your querying three tables and map the result of query to respective object in c#.
let our sql query be as follows:
string sql = #"select * from category_1;select * from category_2;select * from category_3;"
Here we have three select statements and each select statement will give you result of respective table.Lets query the above sql to database using dapper and map them to c# object as follows.
List<category_1> lstCotegory1 = new List<category_1>();
List<category_2> lstCotegory2 = new List<category_2>();
List<category_3> lstCotegory3 = new List<category_3>();
using (var multi = connection.QueryMultiple(sql))
{
lstCotegory1 = multi.Read<category_1>().ToList(); // map first select statement
lstCotegory2 = multi.Read<category_2>().ToList(); // map second select statement
lstCotegory3 = multi.Read<category_3>().ToList(); // map third select statement.
}
This is how you can return results of multiple queries and map them to appropriate object. I know you can do better than this but to understand we have to go with simple example.Hope this will help.
I'm newbie in SQLite.
I would like to query my SQLite database to get multiple rows.
When I add a new item in my local database I call this method Add:
public bool Add<T>(string key, T value)
{
return this.Insert(new SQliteCacheTable(key, this.GetBytes(value))) == 1;
}
_simpleCache.Add("favorite_1", data1);
_simpleCache.Add("favorite_2", data2);
_simpleCache.Add("favorite_3", data2);
Then,
I would like to retrieve from local database all entries where key starts with "favorite_"
to returns all objects in the database which are "favorite" objects.
I'm experienced in Linq, and I would like to do something like this:
IEnumerable<Element> = repository.Find((element) => element.Key.StartWith("favorite_"))
In the SQLiteConnection class there is a method like this:
SQLite.Net.SQLiteConnection.Find<T>(System.Linq.Expressions.Expression<System.Func<T,bool>>)
But I would like the same with in returns a collection IEnumerable<T>.
Can you help me please?
Thank you.
Jool
You have to build your query on the table itself, not the connection:
Assuming:
SQLiteConnection repository;
Then the code would look like:
var favorites = repository.Table<SQliteCacheTable>().Where(item => item.StartsWith("favorite_"));
The favorites variable is of type TableQuery<SQliteCacheTable> though, so it does not yet contain your data. The execution of the actual SQL query will be deferred until you try to access the results (by enumerating with foreach or converting to a list with ToList, for example).
To actually observe what's going on on the database, you can turn on tracing in sqlite-net, by setting repository.Trace = true on your SQLiteConnection object.
Finally, it's worth mentioning that you can also use the C# query syntax on TableQuery<T> objects, if your comfortable with it. So your query could become:
var favorites = from item in repository.Table<SQliteCacheTable>()
where item.StartsWith("favorite_")
select item;
This question is regarding the ASP.NET webservice that i am creating using the DAL-BLL architecture for my final school project.
I have a stored procedure, which is a select query with an inner join for 2 tables. Hence the stored procedure returns multi-table value. One of my DAL tableAdapter methods accesses this stored procedure. How do i retrieve the return value in the BLL? Do i have to create a class structure similar to the one supposed to be returned by the stored proc? or is there a direct way to achieve the same? Help greatly appreciated. Please let me know if someone needs code applet to get a better understanding. Thanks
Here is some more information:
I am using the SQL dataset (.xsd) in DAL. So i have a datatable called "Insurance", which has a tableAdapter. One of the queries in the adapter references to a stored procedure, which has an inner join. So my SP looks like:
ALTER PROCEDURE dbo.GetInsurancesPaged
(
#startRowIndex int,
#maximumRows int,
#patientID int
)
AS
select * from
(
SELECT Insurance.insuranceID, Insurance.memberID, Insurance.groupID, Insurance.accountType, Insurance.comments, Insurance.patient, Insurance.company, InsuranceCompany.companyID, InsuranceCompany.companyName, InsuranceCompany.address, InsuranceCompany.phone, InsuranceCompany.fax, ROW_NUMBER() over (order by Insurance.dateModified DESC) as ROWRANK
FROM Insurance INNER JOIN InsuranceCompany ON Insurance.company = InsuranceCompany.companyID
WHERE Insurance.patient = #patientID
)
AS DataWithRowNumbers
WHERE ROWRANK > #startRowIndex AND ROWRANK <= (#startRowIndex + #maximumRows)
So this SP returns a datatable which will be a combination of the 2 tables in the inner join. Please correct me if i am wrong.
Now in my BLL, i have:
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)]
public mySys.InsuranceDataTable GetInsurancesPaged(int startRowIndex, int maximumRows, int patientID)
{
return insAdapter.GetInsurancesPaged(startRowIndex, maximumRows, patientID);
}
where insAdapter is an instance of insuranceTableAdapter
This gives an error on execution. I can execute the SP successfully, so i think the problem is only bcz i am trying to return a wrong datatable from the BLL.
Please help me solve this.
If using ADO .Net dataset. The wizard will definetly create a table for the same. now from the dataaccess layer, do the following steps
1. Create a object of dataset. (DLL)
Private YourCustomeDataSetDatatable DataAccess()
{
YourCustomDataSet ds = new YourCustomDataSet(); // also called strongly typed dataset
YourCustomeDataSetDatatable dt = ds.YourCustomeDataSetDatatable ()
YourCustomeDataSetTableAdapter ta = new ds.YourCustomeDataSetTableAdapter (); // table adapter that will be invoked
ta.Fill(dt); // or if you have set to return only you can also use GetData()
}
2. Now in business layer
Private YourCustomeDataSetDatatable DataAccess()
{
// create a object of DLL.
MyDAL myDal = new MyDAL ();
return myDal.DataAccess();
}
Catch this on your UI page by following the creating object of BLL and call the method. Here in BLL you can also do various operations to lowered the codes in you ui and keeping it clean from various manipulations.
Found a solution :)
Finally got it working.
I created a new table adapter using the Dataset designer, and called the SP as one of the queries there. The datatable thus created, has all the fields (from Insurance and InsuranceCompany) included. Now, ASP.NET can detect that the return type is the newly created datatable.
Works like a charm.
If there is a better way to solve this, please comment.
Thank you all for your time.
I am using an objectdatasource with a gridview to get data from my orm class, but I cannot get it to order by properly.
I am using the code below but it does not come up in descending order like I have specified below. What am I missing? Using subsonic 2.1
<DataObjectMethod(DataObjectMethodType.Select, True)> Public Function FetchByPatID(ByVal PatientID As Object) As VisitCollection
Dim coll As VisitCollection = New VisitCollection().Where("PatientID", PatientID).Load()
**OrderBy.Desc(Visit.DosColumn)**
Return coll
End Function
The order by is executed against the database as part of your query. It needs to be added before the .Load() method is called.
Dim coll As VisitCollection = New VisitCollection().Where("PatientID", PatientID).OrderByDesc(Visit.Columns.DosColumn).Load()
Ranomore is correct. You must specify the OrderBy before you actually execute the command as the ordering is done on the database.
If you are using SubSonic 2.1 I much prefer the new syntax that has been added which makes the Query much more readable
SubSonic.Select.AllColumnsFrom(Of Visit)().Where(Visit.PatientIDColumn).isEqualTo(PatientID).OrderByDesc(Visit.DosColumn.ColumnName).ExecuteAsCollection(Of VisitCollection)()
Part of the web application I'm working on is an area displaying messages from management to 1...n users. I have a DataAccess project that contains the LINQ to SQL classes, and a website project that is the UI. My database looks like this:
User -> MessageDetail <- Message <- MessageCategory
MessageDetail is a join table that also contains an IsRead flag.
The list of messages is grouped by category. I have two nested ListView controls on the page -- One outputs the group name, while a second one nested inside that is bound to MessageDetails and outputs the messages themselves. In the code-behind for the page listing the messages I have the following code:
protected void MessageListDataSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
var db = new DataContext();
// parse the input strings from the web form
int categoryIDFilter;
DateTime dateFilter;
string catFilterString = MessagesCategoryFilter.SelectedValue;
string dateFilterString = MessagesDateFilter.SelectedValue;
// TryParse will return default values if parsing is unsuccessful (i.e. if "all" is selected"):
// DateTime.MinValue for dates, 0 for int
DateTime.TryParse(dateFilterString, out dateFilter);
Int32.TryParse(catFilterString, out categoryIDFilter);
bool showRead = MessagesReadFilter.Checked;
var messages =
from detail in db.MessageDetails
where detail.UserID == (int)Session["UserID"]
where detail.Message.IsPublished
where detail.Message.MessageCategoryID == categoryIDFilter || (categoryIDFilter == 0)
where dateFilter == detail.Message.PublishDate.Value.Date || (dateFilter == DateTime.MinValue)
// is unread, showRead filter is on, or message was marked read today
where detail.IsRead == false || showRead || detail.ReadDate.Value.Date == DateTime.Today
orderby detail.Message.PublishDate descending
group detail by detail.Message.MessageCategory into categories
orderby categories.Key.Name
select new
{
MessageCategory = categories.Key,
MessageDetails = categories.Select(d => d)
};
e.Result = messages;
}
This code works, but sticking a huge LINQ statement like this in the code-behind for a LinqDataSource control just doesn't sit right with me.
It seems like I'm still coding queries into the user interface, only now it's LINQ instead of SQL. However, I feel that building another layer between the L2S classes and the UI would cut back on some of the flexibility of LINQ. Isn't the whole point to reduce the amount of code you write to fetch data?
Is there some possible middle ground I'm not seeing, or am I just misunderstanding the way LINQ to SQL is supposed to be used? Advice would be greatly appreciated.
All your LINQ querys should be in a business logic class, no change from older methodologies like ADO.
If you are a purist you should always return List(of T) from your methods in the business class, in fact, the datacontext should only be visible to the business classes.
Then you can manipulate the list in the user interface.
If you are a pragmatist, you can return a IQueryable object and make some manipulations in the user interface.
Regardless of LINQ, I think that mixing presentation code with database-relaed code is not a good idea. I would create a simple DB abstraction layer on top of LINQ queries. In my opinion LINQ is just a convenient tool, that doesn't have a serious impact on traditional application design.