I'm starting out with nHibernate and have a simple example that I cannot get working as I'd like.
I have two model objects (Blog and Posts) and I would like to load them all in a single query for one scenario. I want lazy loading in other cases.
I naively thought that I could write something like this:
var blogs = session.Linq<Blog>().Expand("Posts");
But this will give me an instance of blog for every post rather than adding the posts to the blog.
I know I'm doing something stupid. Can someone please point out what it is? Is it that I need to relate the post and blog entities in my linq query?
Code and Mappings:
public class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public virtual long Identifier { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual Post AddPost(Post post)
{
post.Blog = this;
Posts.Add(post);
return post;
}
}
public class Post
{
public virtual long Identifier { get; set; }
public virtual string Name { get; set; }
public virtual Blog Blog { get; set; }
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="nhibEx" namespace="nhibEx">
<class name="Blog" lazy="true">
<id name="Identifier">
<generator class="native" />
</id>
<property name="Name" not-null="true" length="100"/>
<set name="Posts" inverse="true" cascade="save-update" lazy="true">
<key column="BlogIdentifier" foreign-key="fk_Post_Blog"/>
<one-to-many class="Post"/>
</set>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="nhibEx" namespace="nhibEx">
<class name="Post" lazy="true">
<id name="Identifier">
<generator class="native" />
</id>
<property name="Name" not-null="true" length="255"/>
<many-to-one name="Blog" column="BlogIdentifier" class="Blog" />
</class>
</hibernate-mapping>
After searching other forums (perhaps I should of done this properly first!) I'm using this solution:
var blogs = session.Linq<Blog>();
blogs.QueryOptions.RegisterCustomAction(
criteria => criteria.SetResultTransformer(new DistinctRootEntityResultTransformer()));
var results = blogs.Expand("Posts");
I didn't want to use Distinct as I wanted to return IQueryable
Seems to work. I just need to know the theory :)
http://nhforge.org/wikis/howtonh/get-unique-results-from-joined-queries.aspx
Distinct is what you need...
Edit:
When it doesn't work: do the distinct after the tolist.
I don't know why NHibernate loads the same number of objects as the number of database records returned and doesn't do the distinct automatically. This issue/feature is not Linq specific, but will also happen when you use criteria or hql.
session.Linq<Blog>().Expand("Posts").ToList().Distinct();
Sometimes it can be more efficient to execute 2 queries (seperate, or using multiquery/future) than executing one query with a left outer join.
We have just the same problem. It seems to me that linq is always in eager loading mode. So you don't need to do exapnd. However it is very bad. Have you tried to contact HN guys in their google group?
Related
I have the following object:
`
public class Permissions : Item
{
public int Id { get; set; }
public AppUser User { get; set; }
public Component Component { get; set; }
public string Permission { get; set; }
}
`
My MudDataGrid:
`
<MudDataGrid T="Permissions" Items="#permissions" ReadOnly="#_readOnly" EditMode="#(_isCellEditMode ? DataGridEditMode.Cell : DataGridEditMode.Form)"
StartedEditingItem="#StartedEditingItem" CancelledEditingItem="#CancelledEditingItem" CommittedItemChanges="#CommittedItemChanges"
Bordered="true" Dense="true" EditTrigger="#(_editTriggerRowClick ? DataGridEditTrigger.OnRowClick : DataGridEditTrigger.Manual)">
<Columns>
<Column T="Permissions" Field="User" Title="UserName" IsEditable="false" />
<Column T="Permissions" Field="**Component.ComponentName**" IsEditable="false" />
<Column T="Permissions" Field="Permission" />
<Column T="Permissions" Hidden="#(_isCellEditMode || _readOnly || _editTriggerRowClick)" CellClass="d-flex justify-end">
<CellTemplate>
<MudIconButton Size="#Size.Small" Icon="#Icons.Outlined.Edit" OnClick="#context.Actions.StartEditingItem" />
</CellTemplate>
</Column>
</Columns>
`
both 'AppUser' and 'Component' are foreign keys to other objects. Data is returned fine, however I am having dificulty displaying these objects on the experimental MudDataGrid.
I know that the MudDataGrid isn't 'live' currently, but I'm only using it on a test system at present. Is anyone aware of a way for me to reference the items of the foreign object in the grid? I thought it would be something like 'Field="Component.ComponentName', but I get a nullreferenceexception.
I've tried 'Field="Component.ComponentName"' ,'Field="Component[ComponentName]"' to no avail.
I'm creating some Web API's for internal use and one of the 3rd party applications that will interact with it can only use odata v3 (i.e. not odata v4).
I'm trying to query another 3rd party application from this Web API - get a contact by email but not sure how to go about getting it by a string and not id.
I have a simplified model like so:
public class Contact
{
[Key]
public int Id { get; set; }
public string Email { get; set; }
public string CompanyName { get; set; }
}
Simplified controller:
public IHttpActionResult GetContact([FromODataUri] int key)
{
var contact = _repository.GetContact(key);
// return..
}
Simplified WebApiConfig
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
var container = new UnityContainer();
container.RegisterType<IContactRepository, ContactRepository>(new ContainerControlledLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
builder.EntitySet<Contact>("Contacts");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
If I look at the metadata document it shows Id as expected:
<EntityType Name="Contact">
<Key>
<PropertyRef Name="Id"/>
</Key>
In my model if I move the [Key] annotation to email and then in WebApiConfig
change builder.EntitySet<Contact>("Contacts");
to
builder.EntitySet<Contact>("Contacts").EntityType.HasKey(c => c.Email);
I also change the key input parameter in the controller from int to string
it makes a composite key of Id and Email
<EntityType Name="Contact">
<Key>
<PropertyRef Name="Email"/>
<PropertyRef Name="Id"/>
</Key>
http://xxx:52759/odata/Contacts('some#email.com') works if I use fiddler to test but my 3rd party application reads the metadata and require me to pass in Id and Email, I don't have the Id at that time.
If I rename Id to something like cId in my model it's not part of the key in the metadata anymore but I don't think this is the right way to go about it?
<EntityType Name="Contact">
<Key>
<PropertyRef Name="Email"/>
</Key>
Am I on track or do I have to do something completely different if I want to get a contact by email (and in that way get id with the response back)?
Thanks in advance.
You should pass int Id and Email is it is a composite key, in your scenario, I think your mean AlternateKey, which is suported in V4 https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataAlternateKeySamples
I'm using ASP.NET Web API with OData. I'm trying to POST a Child entity, which has a relationship to a Parent (the parent already exists). When I post the entity (using WCF data service client and SetLink), I can see via Fiddler that it is adding a <link...href=[address of parent]> into the body of the request. This exact same request works against our WCF data service version of the service (we are migrating to Web API). However, this does not seem to translate anything into the Post method on the controller in Web API.
When posting the child to ChildController, how can I access the Parent's ID from the Post action on ChildController? I know the value is there in the request, but how can I get ahold of this value? The Child cannot be created without a Parent. Do I need to modify the controller action signature? Maybe there is some attribute I can use somewhere? From an API perspective, I would like to avoid adding ParentId directly to the Child entity if possible.
public class ChildController
{
public HttpActionResult Post([FromBody]Child child)
{
//child.Parent is null here, but all other
//properties of Child are populated.
//How can I get the Parent's ID from the POST request??
}
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Parent { get; set; }
}
public class Parent
{
public int Id { get; set; }
public IEnumerable<Children> Children { get; set; }
}
EDIT:
Here is my request. I changed some of the names to protect the innocent (replaced host name, and entity names with parent/child):
POST https://localhost/MyWebService/Child HTTP/1.1
Content-Type: application/atom+xml
DataServiceVersion: 1.0;NetFx
MaxDataServiceVersion: 3.0;NetFx
Accept: application/atom+xml,application/xml
Accept-Charset: UTF-8
User-Agent: Microsoft ADO.NET Data Services
Host: localhost
Content-Length: 1048
Expect: 100-continue
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<category term="MyWebService.Entities.Child" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Parent" type="application/atom+xml;type=entry" title="Parent" href="https://localhost/MyWebService/Parents(1L)" />
<id />
<title /><updated>2014-05-30T16:07:28Z</updated><author><name /></author>
<content type="application/xml">
<m:properties>
<d:Content>content</d:Content>
<d:CreatedDate m:type="Edm.DateTime">0001-01-01T00:00:00</d:CreatedDate>
<d:Description>desc</d:Description>
<d:Enabled m:type="Edm.Boolean">true</d:Enabled>
<d:Id m:type="Edm.Int64">0</d:Id><d:TabName>tname</d:TabName>
</m:properties>
</content>
</entry>
In order to post an entity from navigation link, you need to define your action in Parent Controller. Here is the code snippet:
public class ParentController
{
public HttpActionResult PostToChildren(int key, [FromBody]Child child)
{
var parent = parents.single(p=>p.Id == key);
if(parent != null)
{
parent.Children.Add(child);
ChildController.Children.Add(child);
return StatusCode(HttpStatusCode.NoContent);
}
else
return BadRequest();
}
}
have very simple relationship between two entities and I am trying to expose them with asp.net webapi odata controllers but it seems that something is wrong with $metadata.
When I run jaydatasvcutil.exe on the $metadata I get warning: inverseProperty other side missing.
When I use breezejs loadNavigationProperty I get similar error.
I have the problem even with official example.
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
You can observe the $metadata here http://sdrv.ms/Z5Klfw
Please help.
When we are generating navigation properties we don't reuse the relationships.
For example, lets say you have simple model,
public class Product
{
public int Id { get; set; }
public Supplier Supplier { get; set; }
}
public class Supplier
{
public int Id { get; set; }
public Product[] Products { get; set; }
}
The $metadata for the navigation properties that we generate looks like this,
<NavigationProperty Name="Supplier" Relationship="ProductsService.Models.ProductsService_Models_Product_Supplier_ProductsService_Models_Supplier_SupplierPartner" ToRole="Supplier" FromRole="SupplierPartner" />
<NavigationProperty Name="Products" Relationship="ProductsService.Models.ProductsService_Models_Supplier_Products_ProductsService_Models_Product_ProductsPartner" ToRole="Products" FromRole="ProductsPartner" />
Notice that we are generating two relationships instead of one. The reason we do that is that it is a hard problem to figure out if two navigation properties represent the same relationship. Take the instance of Product and Manufacturer.
public class Manufacturer
{
public int Id { get; set; }
public Product[] RawMaterials { get; set; }
public Product[] Produces { get; set; }
}
public class Product
{
public int Id { get; set; }
public Manufacturer[] Producers { get; set; }
public Manufacturer[] Consumers { get; set; }
}
It is not trivial to figure out that Maufacturer.RawMaterials and Product.Consumers should share the same relationship and Manufaturer.Produces and Product.Producers should share the same relationship. We chose not to do it because the clients that we know of don't make much out of this information.
All this happens because OData uses the same EDM model as the entityframework. Entityframework requires this information as it maps these relationships to association sets which would become tables in the database.
Another reason we chose not to do it is that this could be going away in OData V4. Check out the working draft here (page 23 and page 57 would be of interest). In short, navigation properties in $metadata in OData V4 would look more like this,
<NavigationProperty Name="Category" Type="Self.Category" Nullable="false" Partner="Products" />
Notice that there is no relationship and there would be no association sets.
I have this problem.
I have a class like this:
public class WfStep
{
private readonly IDictionary properties = new Hashtable();
public virtual Guid Id { get; private set; }
public virtual string Name { get; set; }
public virtual dynamic Properties { get { return new HashtableDynamicObject(properties); } }
and its mapping file like this:
<class name="ConsoleApplication4.WfStep" table="WFSteps">
<id name="Id">
<generator class="guid"/>
</id>
<property name="Name" />
<map name="Properties" table="WFSteps_Properties" access="field.lowercase">
<key column="StepId" />
<index column="PropertyName" type="System.String"/>
<composite-element class="ConsoleApplication4.ValueItem, ConsoleApplication4">
<property name="Value" type="System.String"/>
<property name="ValueType" type="System.String"/>
</composite-element>
</map>
This example is ported from:
Ayende Support dynamic fields with NHibernate and .NET 4.0
Then I save an object to the db like this:
ie.
var step = new WfStep { Name = "John" };
var property = new ValueItem() { Value = DateTime.Now, ValueType = "System.DateTime" };
step.Properties["DateProperty"] = property ;
Session.Save(step);
Now I want to return all the WFSteps that have DateProperty set to year 2010 ie.:
var Session.Query<WfStep>().Where(x=>x.Properties.DateProperty.Year == 2010);
It throws an error:
An expression tree may not contain a dynamic operation
How can I query this type of class that has Dynamic properties.?
You can index collections mapped as a map or as list. Try the following hql
from WfStep s
where s.Properties["DateProperty"] = "2010"