I've got class, let it be Foo:
public class Foo
{
...
protected MyCollection<Detail> _details
public virtual MyCollection<Detail> Details
{
get { return _details ?? new MyCollection<Details>(); }
set { _details = value; ... }
}
...
}
public class Detail {...}
When I do LINQ query:
var q = session.Query<Foo>().Select(foo => new Foo( property1 = foo.property1, ... );
...
q.ToList();
I've got NULL in _details field, and when I access to Details to get all Lazy details, of course I get new MyCollection(), but not IPersistentBag (or else, IPersistentCollection).
So How can I manually create proxy collection (I've got session / sessionFactory references)?
[ Added ] here is the mappings (on Foo):
<bag name="Details" lazy="true" collection-type="NHibernateDataService.DetailBag`1[[DataObjects.Detail, DataObjects]], NHibernateDataService" cascade="all-delete-orphan" fetch="select" batch-size="1" access="property" inverse="true">
<key column="`Master`" />
<one-to-many class="DataObjects.Detail" />
</bag>
Thank you!
MyCollection can't be mapped to IPersistentBag, unless, of course, you implement that interface on MyCollection (and probably set the mapping type explicitly??)...
A more standard approach is to set the "collection-type" property on the bag mapping to a custom type - an implementation of IUserCollectionType, which you can choose to make a base class that MyCollection derives from.
Related
I have a provider, wich index stock for product for every unit, this way:
for (Map.Entry<B2BUnitModel, Integer> unit : stockByUnit.entrySet() )
{
document.addField(indexedProperty, hasStock(unit.getValue()), unitUid(unit.getKey()));
}
so this is result after index in solr:
"localStockForUnt_001_boolean": true,
"localStockForUnt_002_boolean": true,
where localStockForUnt is SolrIndexedProperty, 001 and 002 are the units and true or false are the indexed value.
this is the impex to create it:
INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)`[unique=true];name[unique=true];type(code);sortableType(code);currency[default=false];localized[default=false];multiValue[default=false];useForSpellchecking[default=false];useForAutocomplete[default=false];fieldValueProvider;valueProviderParameter`
;$solrIndexedType; localStockForUnt ;boolean ; ; ; ; ; ; ;myResolver;
so I added it inside the 'sort' called 'relevance' in hmc, this 'sort' just have this field in hmc.
My doubt is, how can I set to it sort my result using for example localStockForUnt_002_boolean?
I did set sort in controller manually to test, I did set it to "relevance", but since the provider of field used in relevance (localStockForUnt) index two diferent informations, how can I select which one to use?
Actually what you are trying to do here was already been initiated and used in several cases by Hybris, for example:
localized properties like the name, indexed as name_en_string.
properties with currency like price is indexed as priceValue_eur_double and also used for Sort.
For :priceValue_eur_double | For : localStockForUnt_001_boolean.
priceValue is the field's name | localStockForUnt is the field's name.
euris the field qualifier | 001 is the field qualifier.
double is the field type | boolean is the field type.
So your case here is not different than these two examples, hence you need just to know how to use what's already exists.
Actually nothing magical about how these two examples works!
First of all, add new boolean attribute to SolrIndexedPropertyModel let's call it isB2bUnit :
<!-- add this to your *-items.xml -->
<itemtype code="SolrIndexedProperty" autocreate="false" generate="false">
<attributes>
<attribute qualifier="isB2bUnit" type="java.lang.boolean">
<persistence type="property" />
<!-- i would prefer to add a default value here : FALSE -->
</attribute>
</attributes>
</itemtype>
Next you have to add the same boolean attribute in the IndexedProperty dto :
<!-- add this to your *-beans.xml -->
<bean class="de.hybris.platform.solrfacetsearch.config.IndexedProperty">
<property name="isB2bUnit" type="boolean"/>
</bean>
Then you need to override DefaultIndexedPropertyPopulator it's the responsible for converting from SolrIndexedProperty to IndexedProperty:
public class MyIndexedPropertyPopulator extends DefaultIndexedPropertyPopulator {
#Override
public void populate(SolrIndexedPropertyModel source, IndexedProperty target) throws ConversionException {
super.populate(source, target);
//add this line
target.setIsB2bUnit(source.getIsB2bUnit());
}
}
Register the propulator into spring.
<!-- add this to your *-spring.xml -->
<alias name="myIndexedPropertyPopulator" alias="indexedPropertyPopulator" />
<bean id="myIndexedPropertyPopulator" class="com.foo.bar.MyIndexedPropertyPopulator" parent="defaultIndexedPropertyPopulator" />
The idea is to hook into this method DefaultFieldNameTranslator.translateFromProperty(...) and force it to add your specific fieldQualifier which is b2bUnit.code to the fieldName if the isB2bUnit of the Indexedproperty is TRUE.
The original DefaultFieldNameTranslator.translateFromProperty(...) is like this :
protected String translateFromProperty(SearchQuery searchQuery, IndexedProperty indexedProperty, FieldType fieldType) {
//...
if(qualifierProvider != null && qualifierProvider.canApply(indexedProperty)) {
Qualifier qualifier = qualifierProvider.getCurrentQualifier();
fieldQualifier = qualifier != null?qualifier.toFieldQualifier():null;
} else if(indexedProperty.isLocalized()) {
fieldQualifier = searchQuery.getLanguage();
} else if(indexedProperty.isCurrency()) {
fieldQualifier = searchQuery.getCurrency();
}
//you have to add your else if here!!!
return this.fieldNameProvider.getFieldName(indexedProperty, fieldQualifier, fieldType);
}
So create MyFieldNameTranslator that extends from DefaultFieldNameTranslator and override translateFromProperty(...).
Note: SomeB2bUnitService this service is not real but it should be able to return the current b2bUnit.
public class MyFieldNameTranslator extends DefaultFieldNameTranslator {
//To be injected!!
private SomeB2bUnitService someB2bUnitService;
#Override
protected String translateFromProperty(SearchQuery searchQuery, IndexedProperty indexedProperty, FieldType fieldType) {
//...
//...
else if(indexedProperty.getIsB2bUnit()) {
fieldQualifier = someB2bUnitService.getCurrentB2bUnit().getCode();
}
return this.fieldNameProvider.getFieldName(indexedProperty, fieldQualifier, fieldType);
}
}
Register the Translator into Spring :
<!-- add this to your *-spring.xml -->
<alias name="myfieldNameTranslator" alias="fieldNameTranslator" />
<bean id="myfieldNameTranslator" class="com.foo.bar.MyFieldNameTranslator" parent="defaultfieldNameTranslator">
<property name="someB2bUnitService" ref="someB2bUnitService" />
</bean>
Edit : now all what you have to do is to set isB2bUnit to true for localStockForUnt:
INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true] ;name[unique=true] ;type(code) ;isB2bUnit
;$solrIndexedType ;localStockForUnt ;boolean ;true
Note : that some classes and beans may have been changed between Hybris versions but the concept will remains the same.
I have a scalar-valued function in my sql database.
I receive this error when importing this function into Entity Framework model:
Error 6046: Unable to generate function import return type of the store function 'GetContentByIdAndCul'.
The store function will be ignored and the function import will not be generated. ..\EntityModels.edmx
my function tsql is:
ALTER FUNCTION [FRM].[GetContentByIdAndCul]
(#Id int,#Culture nvarchar(5))
RETURNS nvarchar(max)
AS
BEGIN
declare #Result nvarchar(max)
if #Id is not null
set #Result='This Content not defined in this Language'
select #Result=Value from CUL.Contents
WHERE ID=#Id AND (CUL.Contents.Culture = LOWER(#Culture)
OR CUL.Contents.Culture = LOWER(SUBSTRING(#Culture,1,2)))
return #Result
END
Preceding answers show the good way to solve the problem but none works in real life.
Here's a tested solution with Entity Framework 6 that works for me. So it should works for you.
Import your scalar valued function
Import your scalar valued function [FRM].[GetContentByIdAndCul] into your Entity Framework model. It automatically creates corresponding entry in the storage model of your EntityModels.edmx file :
<Function Name="GetContentByIdAndCul" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="FRM" ReturnType="nvarchar(max)">
<Parameter Name="Id" Type="int" Mode="In" />
<Parameter Name="Culture" Type="nvarchar(5)" Mode="In" />
</Function>
Add code to wrap call to your scalar valued function
Create new source file and add code to auto generated DbContext class (say her name is MyEntities) using Partial class mechanism (https://msdn.microsoft.com/en-us/library/wa80x488%28v=vs.120%29.aspx)
public partial class MyEntities
{
[DbFunction("EntityModels.Store", "GetContentByIdAndCul")]
public string GetContentByIdAndCul(int id, string culture)
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var parameters = new List<ObjectParameter>();
parameters.Add(new ObjectParameter("Id", id));
parameters.Add(new ObjectParameter("Culture", culture));
return objectContext.CreateQuery<string>("EntityModels.Store.GetContentByIdAndCul(#Id, #Culture)", parameters.ToArray())
.Execute(MergeOption.NoTracking)
.FirstOrDefault();
}
}
Use your scalar valued function
Client code :
using (var context = new MyEntities())
{
int id = 1;
string culture = "fr-FR";
string result = null;
result = context.GetContentByIdAndCul(id, culture);
}
Until today, entity framework doesn't suport generating calls for scalar functions.
But, you can solve the problem writting a custom method like this inside your DbContext class:
public partial class YouDbContext
{
[DbFunction("YouDbContext.Store", "YourScalarFunction")]
public string YourScalarFunction(string parameter)
{
var lObjectContext = ((IObjectContextAdapter)this).ObjectContext;
return lObjectContext.
CreateQuery<string >(
"YouDbContext.Store.YourScalarFunction",
new ObjectParameter("parameterName", parameter)).
Execute(MergeOption.NoTracking).
FirstOrDefault();
}
}
Create a partial class for YOURMODEL.Context.cs: public partial class YOUREntities : DbContext
[DbFunction("YOURModel.Store", "YOURSCALARFUNCTION")]
public string YOURSCALARFUNCTION(string PARAMETER)
{
List<ObjectParameter> parameters = new List<ObjectParameter>(3);
parameters.Add(new ObjectParameter("PARAMETER", PARAMETER));
var lObjectContext = ((IObjectContextAdapter)this).ObjectContext;
var output = lObjectContext.
CreateQuery<string>("YOURMODEL.Store.YOURSCALARFUNCTION(#PARAMETER)", parameters.ToArray())
.Execute(MergeOption.NoTracking)
.FirstOrDefault();
return output;
}
Be sure that you have your function added to YOURMODEL.EDMX, should be some thing like this:
<Function Name="YOURSCALARFUNCTION" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" ReturnType="nvarchar(max)">
<Parameter Name="PARAMETER" Type="nvarchar(max)" Mode="In" />
</Function>
I'm doing the below join, there are many bookingActions records, but I want there to only be one BookingAction record per booking record. I want the BookingAction record that has the highest primary key value.
How would I do this?
var bookingLocationsQuery = (
from
booking in session.Query<Booking>()
join
bookingActions in session.Query<BookingAction>() on booking.Id equals bookingActions.bookingId
where
(booking.bookingAdminID == userId)
select new { booking, bookingActions }
);
A couple of suggestions. First, you should be leveraging NHibernate's many-to-one to do the join for you instead of doing it manually. It looks like you currently have something like this...
public class BookingAction
{
// ... other properties ...
public virtual int bookingId { get; set; }
}
<class name="BookingAction">
<!-- ... other properties ... -->
<property name="bookingId" />
</class>
Don't do that. Instead, you should have:
public class BookingAction
{
// ... other properties ...
public virtual Booking Booking { get; set; }
}
<class name="BookingAction">
<!-- ... other properties ... -->
<many-to-one name="Booking" column="bookingId" />
</class>
Similar advice for Booking.bookingAdminID. It should be a many-to-one to User, not just a simple property.
Second, after you make those changes, you should be able to accomplish your goal with a query like this:
var subquery = session.Query<BookingAction>()
.Where(a => a.Booking.Admin.Id == userId)
.GroupBy(a => a.Booking.Id)
.Select(g => g.Max(a => a.Id));
var bookingActions = session.Query<BookingAction>()
.Fetch(a => a.Booking)
.Where(a => subquery.Contains(a.Id));
Sorry about switching it to the chained extension method syntax - that's easier for me to work with. It's exactly equivalent to the from ... select syntax in execution.
Try using the Max() method, for sample:
var bookingLocation = session.Query<Booking>()
.Where(booking => booking.bookingAdminID == userId)
.Max(x => booking.bookingAdminID);
I have a problem with NHibernate not using my mappings configuration for eager loading a collection when I get something using HQL or Linq (Session.Query). Session.Get and Session.QueryOver is working like expected.
I'm using NHibernate 3.2. Here's the mapping of a collection in my Product mapping.
<bag name="OrderItems" inverse="true" cascade="none" lazy="false" fetch="join">
<key column="order_id" />
<one-to-many class="OrderItem" />
</bag>
and from the other side the mapping looks like this:
<many-to-one name="Product" class="Product" column="product_id" not-null="true" />
I have 4 Tests, 2 are successfull and 2 are not. They use Session.SessionFactory.Statistics to keep track of CollectionFetchCount (was OrderItems selected in 1 joined query or in a separate). The intent is to have OrderItems selected and loaded when selecting the product as OrderItems are almost always accessed as well.
LastCreated is a simple reference to the last product inserted into the DB.
[Test] /* Success */
public void Accessing_Collection_Using_Session_Get_Results_In_1_Select()
{
// Get by Id
var product = Session.Get<Product>(LastCreated.Id);
var count = product.OrderItems.Count;
Assert.AreEqual(0,statistics.CollectionFetchCount,"Product collectionfetchcount using Get");
}
[Test] /* Success */
public void Accessing_Collection_Using_Session_QueryOver_Results_In_1_Select()
{
// Get by Id
var product = Session.QueryOver<Product>().SingleOrDefault();
var count = product.OrderItems.Count;
Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using QueryOver");
}
[Test] /* Fail */
public void Accessing_Collection_Using_Session_Query_Results_In_1_Select()
{
// Get by IQueryable and Linq
var product = Session.Query<Product>().Single(x => x.Id == LastCreated.Id);
var count = product.OrderItems.Count;
Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using Linq");
}
[Test] /* Fail */
public void Accessing_Collection_Using_HQL_Results_In_1_Select()
{
// Get by IQueryable and Linq
var product = Session.CreateQuery("from Product where Id = :id")
.SetParameter("id",LastCreated.Id)
.UniqueResult<Product>();
var count = product.OrderItems.Count;
Assert.AreEqual(0, statistics.CollectionFetchCount, "Product collectionfetchcount using HQL");
}
Is this intended behaviour or am I doing something wrong?
HQL queries will not respect a fetch="join" set in mapping. This is because they are freeform queries, making it impossible for NH to guess how to transform them to add the join.
Linq is implemented as a wrapper for HQL, QueryOver is a wrapper for Criteria; that's why you see the different behaviors.
If you need eager loads in Linq/HQL, you will have to make them explicit in the query (using join fetch and Fetch()/FetchMany()
Edited:
I'm querying some XML into objects recursively. Each object has a list of sub objects, and should refer to it's parent if it has one.
Example XML:
<object attribute1="text" attribute2="text"/>
<object attribute1="text" attribute2="text">
<object attribute1="text" attribute2="text">
<object attribute1="text" attribute2="text">
</object>
Example Linq:
private static List<MyObject> ParseMyObjects(XElement node, MyObject p)
{
List<MyObject> myobjs = (from x in node.Elements("object")
select new MyObject {
attribute1 = x.Attribute("attribute1 ").Value,
attribute2 = x.Attribute("attribute2 ").Value,
subObjects = ParseMyObjects(x, this), // the "this" key word can't refer to the MyObject being created in the query, but is there some other way of doing this?
parent= p
}).ToList();
return myobjs;
}
To accomplish this currently, I am recursively traversing the MyObjects list AFTER it has been queried and setting each parent (the "parent" line above is excluded).
I would simply prefer a more elegant solution of using the newly instantiated object within the Linq query if possible. Any ideas?
Edit:
To clarify (as BrokenGlass did in a comment), the this that the code comment is referring to is the instance of MyObject that is being created within the query
this can't work in a method marked static ever. There is no instance because the method is static.
I would simply prefer a more elegant solution of using the newly instantiated object within the Linq query if possible. Any ideas?
Just use XObject.Parent as in
parent = x.Parent
If you want the Parent member of the created MyObject instance to point to the instance itself, there are two ways to achieve this without adding code that iterates over the list after the Linq query:
1) Add a constructor that sets it up for you, e.g. the default constructor
public MyObject() {
this.Parent = this;
}
2) Add a fluent-interface style method for setting the parent, and invoke it in the query:
/* in class MyObject */
public MyObject WithSelfAsParent() {
this.Parent = this;
return this;
}
/* updated linq query */
List<MyObject> myobjs = (from x in node.Elements("object")
select new MyObject {
attribute1 = x.Attribute("attribute1 ").Value,
attribute2 = x.Attribute("attribute2 ").Value,
subObjects = ParseMyObjects(x),
}.WithSelfAsParent()).ToList();
Whether these are better than explicitly looping over the list is of course a matter of taste. I would probably just keep it simple and choose the loop, unless the assumption that the parent pointer is equal to this by default is obvious in the context of your MyObject class, which makes setting it in the default constructor the natural choice.
The solution for me was to harness the set of the subObjects property on MyObject.
class MyObject {
....
private List<MyObject> _subObjects = new List<MyObject>();
public List<MyObject> subObjects
{
get { return _subObjects ; }
set
{
_subObjects = value;
if(_subObjects != null)
{
foreach(MyObject o in _subObjects )
{
o.parent = this;
}
}
}
}
....
}
If anyone does know of a way to reference the newly created/selected object within the Linq syntax, I will mark your answer as the corrrect one.