I am tying to rewrite a module which interacts with NHibernate. The buisness logic becomes more sofisticated and we decided to change our linq queries to Nhibernate's Criteria. Here is the old code:
nhSession.Query<User>().Where(
u => u.Roles.Contains(query.Role)
)
And the new code:
var criteria = nhSession.CreateCriteria<User>("user");
criteria.Add(/* contains? */);
And mapping:
<class name="User" table="users">
<id name="Id" column="id">
<generator class="hilo">
<param name="table">hilo</param>
<param name="column">user_id</param>
<param name="max_lo">10</param>
</generator>
</id>
<property name="Password" column="password" />
<bag name="Roles" table="user_roles">
<key column="user_id" />
<element column="role" />
</bag>
</class>
Where Roles is an enum.
How to make the query with Criteria behave like the Linq query?
var criteria = nhSession.CreateCriteria<User>("user");
var roleCriteria = criteria.CreateCriteria("Roles","roles");
roleCriteria.Add(Expression.Eq("role",Role.YourRole);
Assuming the user_roles table is mapped to a UserRole Class, and from this question, you may try something like :
DetachedCriteria dCriteria = DetachedCriteria.For<UserRole>("ur")
.SetProjection(Projections.Property("ur.UserId"))
.Add(Restrictions.EqProperty("ur.UserId", "user.Id"))
.Add(Restrictions.Eq("ur.Role", query.Role));
var criteria = nhSession.CreateCriteria<User>("user")
.Add(Subqueries.Exists(dCriteria)).List<User>();
Related
I have a MySQL database linked with NHibernate.
I am getting a StaleObjectStateException:
Message : Test method DALTest.UnitTest1.AddingResultAndGetAll threw
exception: NHibernate.StaleObjectStateException: Row was updated or
deleted by another transaction (or unsaved-value mapping was
incorrect): [Domain.Bean.Result#3]
...here on the rr.Save call :
DateTime date = new DateTime();
rr = new ResultRepository();
FootRace foot = new FootRace(2,"une courseeee", "vraiment une chouette course", date, 5, false);
Participant part = new Participant(2,"Boveeé", "Joseeé", 123, "M", date, 5);
rr.Save(new Result(3,foot,part , new TimeSpan(2500),5));
Where my Interface implementation is :
public void Save(Result result) {
var e =Session.GetSessionImplementation().PersistenceContext.EntityEntries;
Session.SaveOrUpdate(result);
Session.Flush();
}
After doing some research it looks like it's a thread problem but I can't find a solution to this. By the way the database is resetted at the moment i call this test, so there is absolutely nothing inside.
Here is the XML mapping file:
<class name="Result" table="result">
<id name="Id" column="idResult" type="int">
<generator class="native"></generator>
</id>
<many-to-one name="FootRace" class="FootRace" column="idFootRace" cascade="save-update"/>
<many-to-one name="Participant" class ="Participant" column ="idParticipant" cascade="save-update"/>
<property name="RaceTime" column="raceTime" not-null="false"/>
<property name="Rank" column="rank"/>
</class>
I'm building an OData application and I'm struggling on how to retrieve results and only include certain (child properties).
First, let me show you the registration in my builder:
builder.EntitySet<AggregatedArticlesSearchModel>("Search").EntityType.HasKey(x => x.Name);
Now, on to the model that I'm returning from my Query:
<EntityType Name="AggregatedArticlesSearchModel">
<Key>
<PropertyRef Name="Name"/>
</Key>
<Property Name="Name" Nullable="false" Type="Edm.String"/>
<Property Name="Values" Type="Collection(Zevij_Necomij.Mobile.App.Api.Models.OccurenceViewModel)"/>
</EntityType>
<ComplexType Name="OccurenceViewModel">
<Property Name="Value" Type="Edm.String"/>
<Property Name="Count" Nullable="false" Type="Edm.Double"/>
<Property Name="Articles" Type="Collection(Zevij_Necomij.Mobile.App.Api.Models.AggregatedArticleDescriptionViewModel)"/>
</ComplexType>
<ComplexType Name="AggregatedArticleDescriptionViewModel">
<Property Name="Name" Type="Edm.String"/>
<Property Name="Specification" Type="Edm.String"/>
<Property Name="Brand" Type="Edm.String"/>
</ComplexType>
When I'm executing a request to get the data, I'm not doing anything fancy but just returning the results from the database:
public async Task<IHttpActionResult> Get()
{
// Create all the managers for the platform context that are required by the application.
var classificationManager = Context.CreateManager(typeof(AggregatedArticleManager<>)) as AggregatedArticleManager<IAggregatedArticleStore<AggregatedArticle>>;
var classifications = await classificationManager.GetAllAsync();
var returnList = classifications.OrderBy(x => x.Name).Select(AggregatedArticlesSearchModel.MapFromDbModel).ToList();
return Ok(returnList.AsQueryable());
}
Since I'm working with child objects, the list can get quite huge:
{
"#odata.context": "http://api.mobileapp.appserver.dev.dsoft.be/OData/$metadata#Search",
"value": [
{
"Name": "(Veiligheids)slipkoppeling",
"Values": [
{
"Value": "ja",
"Count": 118,
"Articles": [
{
"Name": "230 V Sleuvenzaag",
"Specification": "Compacte machine",
"Brand": "Makita"
},
{
"Name": "230V Cirkelzaag SJS",
"Specification": "Softstart voor
},
}
}
I can have a thousand articles in a single set, thus, way to much to return over the Web Api.
Since I don't need all those properties in a single request, I was thinking to let the cliënt only retrieve child properties by using the ?$select parameter, thus the cliënts can say for example:
OData/Search?$select=Values
The problem here is that I for example, only want to return the Count, thus, I tought that a request like this was possible:
OData/Search?$select=Values/Count
However, this leads to an OData error: "The query specified in the URI is not valid. Found a path with multiple navigation properties or a bad complex property path in a select clause. Please reword your query such that each level of select or expand only contains either TypeSegments or Properties."
Anyone who has an idea on how to solve this one?
#Complexity
As I know, to select a sub property in a property is not supported.
However, if you build the OccurenceViewModel as entity type, then you can use the nested $select in $expand to meet your requirement.
For example:
1) build the entity type
builder.EntitySet<OccurenceViewModel>("ViewModels").EntityType.HasKey(x => x.Value);
then, Values in AggregatedArticlesSearchModel should be a navigation property.
2) Now, you can issue a GET request as follows to only return the Count property:
GET ~/odata/Search?$select=Values&$expand=Values($select=Count)
Then, the payload should look like the below:
{
"#odata.context":"http://localhost/odata/$metadata#Search(Values,Values(Count))","value":[
{
"Values":[
{
"Count":101.0
},{
"Count":102.0
}
]
},{
"Values":[
{
"Count":101.0
},{
"Count":102.0
}
]
}
]
}
Hope it can help you. Thanks.
I was asked today to look at a new project - reading in some XML and doing some analysis. I know a little C#. I have gotten this far with this code that so far works. I get the 4 node lists successfully. I have a couple problems. First I am not sure how to access what is in the tag on any of the nodes in any of the lists. Second, I'd prefer to be able to use LINQ queries but XmlNodeList doesn't seem to support that syntax. In the sample XML below, I'd like to be able to get all the vdisks that belong to a particular IO Group or mdisk as determined by io_group_name or mdisk_grp_name property. Most of what I looked at gave examples for accessing the [Attribute] list and searches all used properties/atttributes interchanged.
What I tried is also below, it gave a null value exception. The Attributes list only has one attribute. I can't find any examples to do what I want and it isn't clear from inspecting the node in the debugger what I need to access to do what I want.
//this works
XmlTextReader reader = new XmlTextReader(_InputFile);
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList clusterlist = doc.SelectNodes("//object[#type='cluster']");
XmlNodeList controllerlist = doc.SelectNodes("//object[#type='controller']");
XmlNodeList mdisklist = doc.SelectNodes("//object[#type='mdisk']");
XmlNodeList vdisklist = doc.SelectNodes("//object[#type='vdisk']");
// this did not work - got null value exception
foreach (XmlNode vdisknode in vdisklist)
{
string str = vdisknode.Attributes["mdisk_grp_name"].Value;
}
A sample of the XML:
<object type="vdisk">
<property name="id" value="0" />
<property name="name" value="nim01_vd06_gmt" />
<property name="IO_group_id" value="0" />
<property name="IO_group_name" value="ossvc06_iogrp0" />
<property name="status" value="online" />
<property name="mdisk_grp_id" value="0" />
<property name="mdisk_grp_name" value="T1_OSIBM06_MDG1" />
<property name="capacity" value="644245094400" />
<property name="type" value="striped" />
</object>
object node has only one attribute: type
string type = vdiskNode.Attributes["type"].Value;
property node has two attributes: name and value:
string name = propertyNode.Attributes["name"].Value;
string value = propertyNode.Attributes["value"].Value;
What you need I deem is to extend the XPath query:
"//object[#type='vdisk']/property[#name='mdisk_grp_name']/#value"
Or use LINQ to XML:
from obj in doc.Load(xml).Root.Elements("object")
where (string)obj.Attribute("type") == "vdisk"
from prop in obj.Elements("property")
//where (string)prop.Attribute("name") == name
select prop.Value
I'm trying to use NHibernate's Criteria API to write the equivalent of this:
select foo_id from foo_history
group by foo_id
having sum(bar_in) > 0 or sum(baz_in) > 0;
with this mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MVC"
namespace="MVC.Model.Things">
<class name="MVC.Model.Things.FooHistory, MVC"
table="foo_history">
<id name="ID" column="foo_hist_id" type="guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<!-- Properties -->
<property name="BarIn" column="bar_in" type="decimal"
precision="19" scale="4" not-null="true" />
<property name="BazIn" column="baz_in" type="decimal"
precision="19" scale="4" not-null="false" />
<!-- Foreign Keys -->
<many-to-one name="Foo" column="foo_id"
class="MVC.Model.Things.Foo, MVC.Model.Things"
not-null="true" />
</class>
</hibernate-mapping>
and this Criteria code (Detached because it will be a subquery):
var results = DetachedCriteria.For<FooHistory>("fh")
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty(Projections.Id()))
.Add(Projections.Sum("fh.BarIn"))
.Add(Projections.Sum("fh.BazIn")))
.Add(Restrictions.Gt(Projections.Sum("fh.BarIn"), 0) ||
Restrictions.Gt(Projections.Sum("fh.BazIn"), 0))
.GetExecutableCriteria(session).List();
The problem is that adding a conditional restriction after the SetProjection() results in NHibernate generating invalid MySQL:
SELECT this_.foo_hist_id as y0_,
sum(this_.bar_in) as y1_,
sum(this_.baz_in) as y2_
FROM foo_history this_
WHERE (sum(this_.bar_in) > ?p0
or sum(this_.baz_in) > ?p1)
GROUP BY this_.foo_hist_id
...using a WHERE instead of a HAVING. Using a single restriction works fine and everything is correct. I assume that since HN-1280 ("Adds HAVING support to CreateCriteria queries, Fixes parameter order bugs") this is possible but I'm not using the correct "OR" language (e.g., Restrictions.Disjunction() just always creates WHERE).
Is this possible?
I faced with this problem, however i don't think my workaround was good cause it doesn't work for example when you need ToRowCountQuery() for pagination. But anyway... You can use this class instead of built-in OrExpression
class OrHaving : OrExpression
{
public OrHaving(ICriterion lhs, ICriterion rhs) : base(lhs, rhs)
{
}
public override IProjection[] GetProjections()
{
return LeftHandSide.GetProjections().Concat(RightHandSide.GetProjections()).ToArray();
}
}
like
.Add(new OrHaving (Restrictions.Gt(Projections.Sum("fh.BarIn"), 0) ,
Restrictions.Gt(Projections.Sum("fh.BazIn"), 0)))
Need help in parsing the following XML. I am a newbie to Linq to XML.
I want to parse all picture data in a single objects array, and I dont seem to find a way,
Here is a sample xml,
<Object type="System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="Form1" children="Controls">
<Object type="System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="PictureBox1" children="Controls">
<Property name="TabIndex">0</Property>
<Property name="Size">206, 152</Property>
<Property name="ImageLocation">C:\Documents and Settings\Administrator\Desktop\logo2w.png</Property>
<Property name="Location">41, 68</Property>
<Property name="TabStop">False</Property>
<Property name="Name">PictureBox1</Property>
<Property name="DataBindings">
<Property name="DefaultDataSourceUpdateMode">OnValidation</Property>
</Property>
</Object>
<Object type="System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="PictureBox2" children="Controls">
<Property name="TabIndex">0</Property>
<Property name="Size">206, 152</Property>
<Property name="ImageLocation">C:\Documents and Settings\Administrator\Desktop\logo2w.png</Property>
<Property name="Location">42, 68</Property>
<Property name="TabStop">False</Property>
<Property name="Name">PictureBox2</Property>
<Property name="DataBindings">
<Property name="DefaultDataSourceUpdateMode">OnValidation</Property>
</Property>
</Object>
</Object>
I want to access the value as PictureObjects[0].Location = 41, 68, PictureObjects[1].Location = 42, 68 etc, Can I do it?
I saw several samples where I can create such objects based on the node name, and not based on the nodes attribute value? C# LINQ with XML, cannot extract multiple fields with same name into object
Can someone guide or let me know if its feasible?
You can start with this, code below just select TabIndex and Size properties, obviously adding other would not be a tricky:
XDocument xdoc = XDocument.Load(#"path to a file or use text reader");
var tree = xdoc.Descendants("Object").Skip(1).Select(d =>
new
{
Type = d.Attribute("type").Value,
Properties = d.Descendants("Property")
}).ToList();
var props = tree.Select(e =>
new
{
Type = e.Type,
TabIndex = e.Properties
.FirstOrDefault(p => p.Attribute("name").Value == "TabIndex")
.Value,
Size = e.Properties
.FirstOrDefault(p => p.Attribute("name").Value == "Size")
.Value
});