I am using NHibernate (version 1.2.1) for the first time so I wrote a simple test application (an ASP.NET project) that uses it. In my database I have two tables: Persons and Categories. Each person gets one category, seems easy enough.
| Persons | | Categories |
|--------------| |--------------|
| Id (PK) | | Id (PK) |
| Firstname | | CategoryName |
| Lastname | | CreatedTime |
| CategoryId | | UpdatedTime |
| CreatedTime | | Deleted |
| UpdatedTime |
| Deleted |
The Id, CreatedTime, UpdatedTime and Deleted attributes are a convention I use in all my tables, so I have tried to bring this fact into an additional abstraction layer. I have a project DatabaseFramework which has three important classes:
Entity: an abstract class that defines these four properties. All 'entity objects' (in this case Person and Category) must inherit Entity.
IEntityManager: a generic interface (type parameter as Entity) that defines methods like Load, Insert, Update, etc.
NHibernateEntityManager: an implementation of this interface using NHibernate to do the loading, saving, etc.
Now, the Person and Category classes are straightforward, they just define the attributes of the tables of course (keeping in mind that four of them are in the base Entity class).
Since the Persons table is related to the Categories table via the CategoryId attribute, the Person class has a Category property that holds the related category. However, in my webpage, I will also need the name of this category (CategoryName), for databinding purposes for example. So I created an additional property CategoryName that returns the CategoryName property of the current Category property, or an empty string if the Category is null:
Namespace Database
Public Class Person
Inherits DatabaseFramework.Entity
Public Overridable Property Firstname As String
Public Overridable Property Lastname As String
Public Overridable Property Category As Category
Public Overridable ReadOnly Property CategoryName As String
Get
Return If(Me.Category Is Nothing, _
String.Empty, _
Me.Category.CategoryName)
End Get
End Property
End Class
End Namespace
I am mapping the Person class using this mapping file. The many-to-one relation was suggested by Yads in another thread:
<id name="Id" column="Id" type="int" unsaved-value="0">
<generator class="identity" />
</id>
<property name="CreatedTime" type="DateTime" not-null="true" />
<property name="UpdatedTime" type="DateTime" not-null="true" />
<property name="Deleted" type="Boolean" not-null="true" />
<property name="Firstname" type="String" />
<property name="Lastname" type="String" />
<many-to-one name="Category" column="CategoryId" class="NHibernateWebTest.Database.Category, NHibernateWebTest" />
(I can't get it to show the root node, this forum hides it, I don't know how to escape the html-like tags...)
The final important detail is the Load method of the NHibernateEntityManager implementation. (This is in C# as it's in a different project, sorry about that).
I simply open a new ISession (ISessionFactory.OpenSession) in the GetSession method and then use that to fill an EntityCollection(Of TEntity) which is just a collection inheriting System.Collections.ObjectModel.Collection(Of T).
public virtual EntityCollection< TEntity > Load()
{
using (ISession session = this.GetSession())
{
var entities = session
.CreateCriteria(typeof (TEntity))
.Add(Expression.Eq("Deleted", false))
.List< TEntity >();
return new EntityCollection< TEntity >(entities);
}
}
(Again, I can't get it to format the code correctly, it hides the generic type parameters, probably because it reads the angled symbols as a HTML tag..? If you know how to let me do that, let me know!)
Now, the idea of this Load method is that I get a fully functional collection of Persons, all their properties set to the correct values (including the Category property, and thus, the CategoryName property should return the correct name).
However, it seems that is not the case. When I try to data-bind the result of this Load method to a GridView in ASP.NET, it tells me this:
Property accessor 'CategoryName' on object 'NHibernateWebTest.Database.Person' threw the following exception:'Could not initialize proxy - the owning Session was closed.'
The exception occurs on the DataBind method call here:
public virtual void LoadGrid()
{
if (this.Grid == null) return;
this.Grid.DataSource = this.Manager.Load();
this.Grid.DataBind();
}
Well, of course the session is closed, I closed it via the using block. Isn't that the correct approach, should I keep the session open? And for how long? Can I close it after the DataBind method has been run?
In each case, I'd really like my Load method to just return a functional collection of items. It seems to me that it is now only getting the Category when it is required (eg, when the GridView wants to read the CategoryName, which wants to read the Category property), but at that time the session is closed. Is that reasoning correct?
How do I stop this behavior? Or shouldn't I? And what should I do otherwise?
Thanks!
Setting lazy loading = false in your mapping will solve the error. It would be a better practice though to tell NHibernate in your load query that you want to fetch the child collection of categories eagerly.
For a criteia query something like .SetFetchMode("Categories", FetchMode.Eager) should work.
Here is a link that gives some insight into the so called "n + 1" problem, how lazy loading relates to it, and how NHibernate is meant to be used.
HTH,
Berryl
something like this:
var entities = session
.CreateCriteria<TEntity>()
.SetFetchMode("Categories", FetchMode.Eager)
.Add(Expression.Eq("Deleted", false))
.List< TEntity >();
The problem is that when your entity is lazy loaded. The query only gets the items that it currently needs. So in your case it gets the Person object. Then when you access any linked entities, it fires off another query. It uses proxy objects for this that know about the Session that was used. You are only keeping the Session open for the Load and then you're closing it.
This is actually bad practice in the NHibernate world. You want to keep the session alive for a period known as a unit of work. This gives you a lot of benefits, like caching and lazy loading. Consequently you can disable lazy loading it should work. Although I'd recommend modifying your loader class if at all possiblem
Related
I'm still new to SpringMVC (and jstl for that matter). I'm trying to populate options in a select from a list of objects. I've found a way to do it using c:forEach, but I keep thinking there HAS to be a way to make the form:options method work.
I've browsed around, and about the closest thing I can find to official documentation on the items attribute is here >> http://static.springsource.org/spring/docs/2.0.x/reference/spring-form.tld.html#spring-form.tld.options
It says the items attribute is for
"The Collection, Map or array of objects used to generate the inner 'option' tags"
My confusion is what kind of Collection, Map, or array of objects it's looking for. What format do they need to be in? Is it looking for a Collection or array of type String specifically? Can I use
List<MyObject>
and if so, what would MyObject have to have in it in order for this to be valid (i.e. methods, variables)?
Currently, when I try to use MyObject, I get an exception that says -
ConverterNotFoundException: No converter found capable of converting from type com.example.MyObject to type java.lang.String
Do I need to make a converter? Where would that go? How would that work? I've googled that error message and haven't really turned up anything specific to what I'm trying to do... (Most are results about Roo)
the MyObject class looks like this:
public class MyObject{
private String company;
private Customer customer;
private Address customerAddress;
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Address getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(Address customerAddress) {
this.customerAddress = customerAddress;
}
}
and I'm trying to use it as such:
<form:select path="myObjectList">
<form:option value="0"/>
<form:options items="myObjectList" />
</form:select>
Does anyone know specifically what is incorrect about this method? Or, should I be using a
List<String>
to accomplish what I'm doing?
EDIT here's the stack trace >> http://pastebin.com/2c5XBCmG
The Spring Documentation says this about the items attribute of the form:options tag:
The items attribute is typically populated with a collection or array
of item objects. itemValue and itemLabel simply refer to bean
properties of those item objects, if specified; otherwise, the item
objects themselves will be stringified. Alternatively, you may specify
a Map of items, in which case the map keys are interpreted as option
values and the map values correspond to option labels. If itemValue
and/or itemLabel happen to be specified as well, the item value
property will apply to the map key and the item label property will
apply to the map value.
In a nutshell, if you need to use a List of your Custom Beans as the items attribute you need to use also the itemValue and itemLabel attributes. Personally, I'll prefer using Maps -LinkedHashMap instances speciffically- for populating my select tags, but that's a matter of taste.
Adapting an example from the Spring Documentation, your code should look like this:
<form:select path="commandAttribute">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="company" itemLabel="company"/>
</form:select>
I'm using the company attribute as both itemValue and itemLabel, but you're free to choose the attributes that fit your requirements.
Usualy I am doing it with spring tag like this :
<springform:select path="myObjectList" id="selected_company">
<springform:option value="0" label="--- Select One ---"></springform:option>
<springform:options items="${myObjectList}" itemValue="company" itemLabel="company"></springform:options>
</springform:select>
don't forget including the namespace declaration :
xmlns:springform="http://www.springframework.org/tags/form"
There is an object (ObjectA) which has another object inside (ObjectB). There is a Guava TreeBasedTable inside the Object B. This Table has a string as row-key,column-key and another object "ObjectC" as value. This table has been displayed on the jsp using the <s:iterator/> and <s:textfield/> tags and it is being displayed correctly (the "values" inside the <s:textfield/> are correct but the "names" are not).
Now, the problem arises when the <s:textfield/> is modified. How do we capture the modified values inside ObjectC in the action class?
public class ObjectA implements Serializable {
private Integer attr1;
private List<ObjectB> objB;
//...getters and setters....
public class ObjectB implements Serializable {
private Integer attr11;
private Table<String,String,ObjectC> allPFields;
// ...getters and setters....
public class ObjectC implements Serializable {
private Integer attr111;
public String attr112;
// ...getters and setters....
jsp code:
<!-- language: lang-html -->
<s:iterator value="#objB.allPlainFields.row(#rowKey)" var="fieldMap"
status="fieldStatus">
<li><label><s:property value="#fieldMap.key" /></label><span>
<s:textfield name="<NOT SURE>" value="%{#fieldMap.value.attr12}" />
</span></li>
</s:iterator>
A TreeBasedTable in Guava is similar to a map inside a map, I tried doing allPFields[#outerkey][#innerkey].attr112 but, it didn't work.
The object structure when the screen is displayed with existing values in the database
<!-- language: lang-java -->
objA
objBList ArrayList<E> (id=668)
elementData Object[10] (id=7438)
[0] objB (id=7439)
allPFields TreeBasedTable<R,C,V> (id=7443)
backingMap TreeMap<K,V> (id=8116)
cellSet null
columnComparator NaturalOrdering (id=503)
columnKeySet null
columnMap null
factory TreeBasedTable$Factory<C,V> (id=8117)
rowKeySet null
rowKeySet StandardRowSortedTable$RowKeySortedSet (id=8118)
rowMap StandardRowSortedTable$RowSortedMap (id=8119)
rowMap null
values null
And the "allPFields" looks like the following in action:
{OuterKey1=
{InnerKey1=ObjectC[attr111=31, attr112=Hi there],
InnerKey2=ObjectC[attr111=40, attr112=How are you]
}
}
The "allPFields" value above has been picked up from the IDE console.
As I told you in your other question, I've never used Guava TreeBasedTable;
However, according to the Official Guava Documentation, a
TreeBasedTable , which is essentially backed by a TreeMap<R, TreeMap<C, V>>
and the get method is
V get(Object rowKey, Object columnKey)
Returns the value corresponding to the given row and column keys, or null if no such mapping exists.
In Java it would be:
Object value = objA.getObjB().get(listIndex).getAllPlainFields.get(rowKey, columnKey).getAttr112;
value = "new value";
Then in OGNL you could try something (totally untested) like:
<s:textfield value="%{#fieldMap.value.attr12}"
name="objA.objB[#fieldStatus.index].allPlainFields.get(#rowKey, #fieldMap.Key).attr112" />
P.S: BEWARE OF TYPO... you are using allPlainFields and allPFields together... one of them is wrong, make sure that all the notations point to the right variable name.
P.P.S: I don't know your requirements, but this structure seems a little "over-designed" to me... it definitely does not respect the KISS paradigm :)
I am trying to get the private member's value (Text) from NumericUpDown.
public class NumericUpDown
{
private TextBox Text;
...
...
}
I am not understanding why it's not showing as a field. Maybe someone could clarify the difference between fields and members. If it was a field I have found that using reflection I can get it by:
typeof(NumericUpDown).GetField("Text", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) As TextBox
but it's a member so I have to get the MemberInfo by:
typeof(NumericUpDown).GetMember("Text", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(0)
this does not return the value, but a string with the type. Which would make sense, because It's the value of the memberinfo, but I want the actual value of the actual object.
Is there any way of retrieving it like FieldInfo.GetValue(object)? Any help or suggestions. Thanks in advance.
This is because you are using Silverlight.
Quote:
Silverlight reflection cannot be used to bypass access level restrictions and (for example) invoke private members.
I have been trying to use one View for updating an object and all its child collections (based on one-to-many relationships in an SQL Server database with an Entity Framework model).
It was suggested I should use AutoMapper, and I tried that and got it to work. (see Trying to use AutoMapper for model with child collections, getting null error in Asp.Net MVC 3 ).
But the solution is really hard to maintain. And when I try the simple one I had to begin with, using an entity object directly as the model (a "Consultant" object, the parent of all the child collections), I am able to get all the correct changed data back in the POST, and I can use UpdateModel to get them, including child collections. Simple. Granted, UpdateModel only worked after creating a custom model binder from a tip here at SO:
From my custom model binder:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
return base.BindModel(controllerContext, bindingContext);
}
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
propertyMetadata.Model = value;
string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);
// Try to set a value into the property unless we know it will fail (read-only
// properties and null values with non-nullable types)
if (!propertyDescriptor.IsReadOnly)
{
try
{
if (value == null)
{
propertyDescriptor.SetValue(bindingContext.Model, value);
}
else
{
Type valueType = value.GetType();
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
IListSource ls = (IListSource)propertyDescriptor.GetValue(bindingContext.Model);
IList list = ls.GetList();
foreach (var item in (IEnumerable)value)
{
list.Add(item);
}
}
else
{
propertyDescriptor.SetValue(bindingContext.Model, value);
}
}
}
catch (Exception ex)
{
// Only add if we're not already invalid
if (bindingContext.ModelState.IsValidField(modelStateKey))
{
bindingContext.ModelState.AddModelError(modelStateKey, ex);
}
}
}
}
Here's my simple Edit POST method:
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Edit(int id, FormCollection collection)
{
Consultant consultant = _repository.GetConsultant(id);
UpdateModel(consultant);
_repository.Save();
return RedirectToAction("Index");
}
But after that UpdateModel worked. The problem is, at the next stage, when trying to call SaveChanges on the context, that fails. I'm getting this error:
The operation failed: The relationship
could not be changed because one or
more of the foreign-key properties is
non-nullable. When a change is made to
a relationship, the related
foreign-key property is set to a null
value. If the foreign-key does not
support null values, a new
relationship must be defined, the
foreign-key property must be assigned
another non-null value, or the
unrelated object must be deleted.
I don't understand what is wrong. I'm seeing all the correct values in the Consultant object posted, I just can't save it to database. The route of AutoMapper in this case (although an interesting tool) is not working well, it's complicating my code immensely and making the application, which should be rather simple, a nightmare to maintain.
Can anyone offer any insight into why I'm getting this error and how to overcome it?
UPDATE:
Reading some posts here, I found one that seemed slightly related: How to update model in the database, from asp.net MVC2, using Entity Framework? . I don't know if it relates to this, but when I inspected the Consultant object after POST it seems this object itself has entitykey, but the individual items in a collection do not (EntityKeySet = null). Each item however does have the correct id. I don't pretend to understand any of this with the EntityKey, so please explain if it has any bearings on my issue, and if so, how to resolve it...
UPDATE 2:
I thought of something that might have something to do with my problems: The View is using a technique described by Steven Sanderson (see http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ ), and when debugging it seems to me as if UpdateModel has trouble matching the items in a collection in the View with the ones in the actual Consultant object. I'm wondering if this has to do with the indexing in this technique. Here's the helper from that code (I can't follow it very well myself, but it uses a Guid to create indexes, which might be the problem):
public static class HtmlPrefixScopeExtensions
{
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null)
{
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
But then again, I wouldn't have thought this should be the problem since the hidden input contains the id in the value attribute, and I thought UpdateModel just looked at the name of the field to get Programs (the collection) and Name (the property), and then the value to the the id...? And then again there's seems to be some mismatch during update. Anyway, here's the generated html from FireBug also:
<td>
<input type="hidden" value="1" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Id" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true">
<input type="text" value="Visual Studio" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Name">
<span data-valmsg-replace="true" data-valmsg-for="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" class="field-validation-valid"></span>
</td>
Anyone know if this is the problem? And if so, how can I work around it to be able to easily update the collections with UpdateModel? (While still being able to add or remove items in the View before POST, which was the purpose of this technique to begin with).
It looks like there is a Parent entity that has a one to many relationship with your Consultant entity. When you change an attribute of the Consultant entity that is used as the ForeignKey for that relationship, Entity Framework sets the relevant field in the Parent entity to null to decouple the relationship. When that field is not nullable you'll get this error. Actually that error definition is surprisingly good, I've seen this problem with far more cryptic errors.
So, I recommend that you check the parent entity in the database, and proceed to a remedy from there (if you can change it to nullable all is well, if it is part of a different constraint -pk or suchlike- you'll have to fiddle with your object models). I'd ask you to post your entity models, but the chunk of text is intimidating as it is.
I think the error you are getting is related to: EF 4: Removing child object from collection does not delete it - why? You have created an orphan somewhere.
Yes it is related to HtmlPrefixScopeExtensions, but only because you are using Mvc Futures model binders.
In global.asax.cs comment out the line
Microsoft.Web.Mvc.ModelBinding.ModelBinderConfig.Initialize();
and retry: it will work ok !
The problem happens because the MVC futures model binder does not handle correctly this case. It converts ok the form data into your model when you submit the form, but it has a problem when filling the ModelState object when you use HtmlPrefixScopeExtensions to generate non incremental ids.
The model itself is correctly created from the form data. The problem lies inside ModelState which contains only the last value of the collection instead of all elements of the collection.
The strongly typed helper method - which renders the list - only select items which are in your Model property list AND in the matching ModelState entry which is converted into a list. So because there is only one item in the matching ModelState entry other list items get deselected.
This method called by the strongly typed helper code:
htmlHelper.GetModelStateValue(fullName, typeof(string[]))
returns only the last element of the list, because ModelState["Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].List"].Value contains only the last element of the list.
This is a bug (or non supported scenario) in MVC3 Futures extensible model binders.
i've started learning Silverlight 4 RIA services. i've gone over alot of samples of how to bind data to a grid. but always there object being bound is a simple one with no child tables.
in my DB there's a table for employees and a table for city names (with id field as pk). in the employee table theres a FK to the CityId field.
the first thing i've tried to do was to show a list of employees and their city name.
this i've done in the normal way shown in all the tutorials (create edmx, create domain service and using the datasource window to create the datagrid)
the problems started when i tried binding the name of the city throw the FK between employee (parent entity) and citytypes (child entity)
this line works fine:
<sdk:DataGridTextColumn x:Name="cityCodeColumn" Binding="{Binding Path=CityCode}"
Header="CityCode" Width="SizeToHeader" />
this line does not:
<sdk:DataGridTextColumn x:Name="cityNameColumn" Binding="{Binding Path=CityType.Name}" Header="CityName" Width="SizeToHeader" />
after reading some more i've realized that the the domain service does not pass only the data of the entity selected by the LINQ command, and does not pass child entities info.
unless using the include attribute.
so my question is , is there a pattern of building a silverlight application with out signing all the associations between entities as included?
thanks,
Oren
To have the City information available when binding your Employee record you need to ensure you are marking the City reference with an [Include] attribute in your RIA domain service metadata.
[MetadataTypeAttribute(typeof(MyTestObject.MyTestObject_Metadata))]
public partial class MyTestObject
{
internal sealed class MyTestObject_Metadata
{
// Metadata classes are not meant to be instantiated.
private MyTestObject_Metadata()
{ }
[Include]
public AnotherObject Foo { get; set; }
}
}
You also need to include the references in your query.
var results = this.ObjectContext.MyTestObject.Include(Foo);
Hope this helps.