How can I make NHibernate to update any field ONLY on first saving and not to update it on session.Update(obj)?
EDIT: For example, I have an entity A, that has reference to entity B, like:
public class A
{
// ... some properties
public virtual B PropB {get; set;}
}
After retrieving the instance of class A I save all its properties instead of PropB into fields on web page (including id and version). After user modified some fields and click 'Save' (herewith I am sure, that he can not edit PropB), I can just restore this object from the web page and save it to the database but I can not restore the linked PropB. So, when I save A instance, it looses link. So, because of PropB can not be modified by any way after first saving, I need a solution to restrict its updating.
There's a mapping attribute that effectively makes a property insert-only: update="false".
However, there are two issues with your question:
session.Update does not update an entity, flushing the session does. You only need to call session.Update to attach entities that were not loaded by the session.
Why are you modifying a property you don't intend to update in the DB?
I would us DTO's in this case and update the entities within the transaction.
Pseudo-code:
public StoreB(ADto dto)
{
using (transaction)
{
A entity = session.Get<A>(dto.Id);
entity.PropB = dto.PropB;
transaction.Commit();
}
}
public StoreC(ADto dto)
{
using (transaction)
{
A entity = session.Get<A>(dto.Id);
entity.PropC = dto.PropC;
transaction.Commit();
}
}
Related
ASP.NET Core 2 Web application using a REST API. Currently using sqlite3 for development database. (Also tried migrating to SQL Server and got same results as below).
I'm sending an entity to web client, the client makes changes to the entity that involve adding a new related entity and then that updated principle entity gets sent back as json in body of PUT a request.
I was hoping the new related entity would get created automatically, but this is not happening. The simple properties on the principle entity are updated properly, but not reference properties. I'm not getting any exceptions or anything - it just seems to be ignoring the reference properties.
Simplified Classes (I removed other properties that shouldn't affect the relationship):
public partial class DashboardItem {
public int Id { get; set; }
public int? DataObjectId { get; set; }
public DataObject DataObject { get; set; }
}
public partial class DataObject {
public int Id { get; set; }
}
Portion of DbContext Fluent API for associated property:
modelBuilder.Entity<DashboardItem>(entity => {
entity.HasOne(p => p.DataObject)
.WithMany()
.HasForeignKey(p => p.DataObjectId);
});
Controller Method for PUT:
[HttpPut("{id}")]
public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != entity.Id)
{
return BadRequest();
}
_context.Entry(entity).State = EntityState.Modified;
try{
await _context.SaveChangesAsync();
}catch (DbUpdateConcurrencyException)
{
if (!DashboardItemExists(id)){
return NotFound();
}else {
throw;
}
}
return NoContent();
}
The simplified json (without all the other properties) would look like this (I've tried different variations of have the foreign key "DataObjectId" removed from the json, set to null, or set to zero in case that might be interferring.):
{
Id:1,
DataObjectId:null,
DataObject:{
Id: 0
}
}
When debugging in the controller action method, the existing "DashboardItem" principle entity created from the request body has the reference property "DataObject" populated before getting added to the DbContext, but the new DataObject never gets created in the database. There is only a SQL UPDATE statement issued for DashboardItem and no INSERT for DataObject.
I've also tried making the controller method synchronous instead of async, using DbContext.SaveChanges() instead of .SaveChangesAsync(), since there used to be a problem with that in earlier versions of EF Core related to creating related entities, even though I'm using 2.0 which already has a fix for that. Same result.
This EFCore Doc sounds like it should just work out of the box.
This has worked for me in a prior project. What am I missing here?
Basically, my mistake was in assuming the process of updating data was much simpler than it actually is when sending the updated data from a client in a web application.
After digging a lot more, it seems that the following line in my controller method for handling the PUT request is the problem:
_context.Entry(entity).State = EntityState.Modified;
Setting the entity entry state to Modified in this way results in Entity Framework Core ignoring the reference properties for the related objects - the SQL UPDATE generated will only address the columns in the entity table.
This simple summary eventually got me started down the right path.
Summarizing what I've now learned:
This controller method is dealing with a 'detached' entity that was edited and sent back from the client. The DbContext is not yet tracking this entity since I get a new instance of the context with each http request (hence the entity is considered 'detached'). Because it is not being tracked yet, when it is added to the DbContext, the context needs to be told whether this entity has been changed and how to treat it.
There are several ways to tell the DbContext how to handle the detached entity. Among those:
(1) setting the entity state to EntityState.Modified will result in ALL properties being included in the SQL update (whether they've actually changed or not), EXCEPT for the reference properties for related entities:
_context.Entry(entity).State = EntityState.Modified;
(2) adding the entity with a call to DbContext.Update will do the same as above, but will include the reference properties, also include ALL properties on those entities in the update, whether they've changed or not:
_context.Update(entity)
Approach #2 got things working for me, where I was just trying to get the new related child entity to be created in the Update to its parent.
Beyond that, DbContext.Attach() and DbContext.TrackGraph sound like thy provide more find-grained control over specifying what specific properties or related entities to include in the update.
This seems like a super obvious question, but I haven't been able to find a clear answer.
I'm using FluentNHibernate automapping with the DefaultCascade.All() convention.
Entities are saving, but in one-to-many relationships I'm having to provide the one side on my many side even though i'm saving by adding to a collection.
An example will probably explain this better:
Lets say I've got these two classes:
public class Owner
{
public virtual IList<PetDog> Dogs { get; set; }
}
public class PetDog
{
public virtual Owner Owner { get; set; }
}
In order to add a new PetDog to the Dogs collection on an owner, I feel like I should be able to call
Owner.Dogs.Add(new PetDog());
and dispose my ISession. However, I'm just getting the Owner saving and thats it.
If I explicitly set
Owner.Dogs.Add(new PetDog { Owner = Owner })
It works.
Is there a way to avoid explicitly providing that value?
This can be done by marking the Owner class as the owner of the relationship by setting inverse = false in the Owner mapping, i.e.
HasMany(x => x.Dogs)
.Not.Inverse()
.Cascade.AllDeleteOrphan();
Then the owner_id foreign key in the PetDog table will be populated on commit, i.e.
using (var transaction = session.BeginTransaction())
{
var order = new Owner() {Dogs = new List<PetDog>()};
order.Dogs.Add(new PetDog() );
order.Dogs.Add(new PetDog() );
session.Save(order);
transaction.Commit();
}
Alternatively, instead of using transaction you can call session.Flush() instead which will cause the new data to be inserted into the DB, i.e.
var order = new Owner() {Dogs = new List<PetDog>()};
order.Dogs.Add(new PetDog() );
order.Dogs.Add(new PetDog() );
session.Save(order);
session.Flush(); // data persisted to DBMS here.
Please note that the use of session.Flush() is not recommended best practice. It is recommended that explicit transactions are used. Please see this blog post by Ayende Rahien for further details.
I have some old code which is performing a query where a model can be transient. That is, a model with some fields populated from user input, which are then used as part of the query.
It worked under NH 2.1.x, but is failing under the latest version.
The exception raised is "object references an unsaved transient instance - save the transient instance before flushing". This happens when NH attempts to perform a query using a non-persisted object as part of the query.
A simplified version to illustrate the problem.
abstract class BaseModel
public virtual long Id { get; set; }
class Car : BaseModel
public virtual Engine Engine { get;set; }
class Engine : BaseModel
public virtual string Kind { get; set; }
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", eng));
return c.List<Car>(); // <--- Error occurs here
}
And calling code is equivalent to this:
Engine obj = new Engine { Id = 42 }; // Transient instance
var x = GetByEngine(obj);
What I expected to happen (Which appears to be the behaviour of the old NHibernate version), is that the Engine passed is used only for getting the Id. That is, generating SQl like
select .... from Cars where Engine = 42
But with the new version NHibernate seems to check that the engine used in the Expression is actually persisted.
Is there a way to avoid having to load a persisted Engine before performing the query ?
yes using Session.Load() which returns the object if already in the session or a lazyLoadingProxy if not present.
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", Session.Load<Engine>(eng.Id)));
return c.List<Car>();
}
You can use the Session.Load method, which exist for this kind of scenarios.
The Load method will return a Proxy to the Entity and won't hit the Data Base untill you access one of it's properties, (except the Primary key property which won't hit the DB at all).
Usage:
Engine obj = session.Load<Engine>(42);
var x = GetByEngine(obj);
check this article about Session.Get and Session.Load
I think you could do something like this:
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>().CreateCriteria("Engine");
c.Add(Expression.Eq("Id", eng.Id));
return c.List<Car>();
}
Anyway... how it's possible that a car with that engine exists if you haven't saved it yet?
Folks, I know I didn't phrase that title very well, but here's the scenario.
I have a WinForm UI tier, and a WCF middle tier, serving up my EF4 entity objects, which are (of course) mapped to my database tables. Everything works fine.
One of my objects is the Client - and in the Client db table are three varbinary(max) fields for PDF documents. So my entity object has three Byte() properties, one for each document.
But when I load up an initial grid listing the Clients, it's going to drag ALL that PDF data from the MT - making a much bigger payload than I generally need.
With DataSets, I'd write my SQL to not include the PDF binary - but I'd include a Boolean flag field for each to indicate whether there IS one to download if the user wants it. Then I'd load the PDFs via a separate call as needed.
With EF4 - what's the best pattern for this?
First, I'm thinking to put the documents into a child-table/child-objects, so I don't pull it across the tier with the Client. One problem solved.
Second, I suppose I could use partial classes to extend my Client entity object to have the three Boolean properties I want.
Am I on the right track?
I think you have three options:
1) Create a custom class which you project the properties you want into:
public class MySpecialSelection
{
public int ID { get; set; }
public string Name { get; set; }
// more
public bool HasPDFDoc1 { get; set; }
public bool HasPDFDoc2 { get; set; }
public bool HasPDFDoc3 { get; set; }
}
using (var context = new MyContext())
{
var mySpecialSelectionList = context.MyEntities.Where(...some predicate...)
.Select(e => new MySpecialSelection
{
ID = e.ID,
Name = e.Name,
// ...
HasPdfDoc1 = (e.PdfDoc1 != null),
HasPdfDoc2 = (e.PdfDoc2 != null),
HasPdfDoc3 = (e.PdfDoc3 != null),
}).ToList();
// ...
}
Instead of a "named" object you can also project into anonymous types.
Note: This doesn't attach any full model entity to the context, so you won't have any change tracking of entities.
2) Table splitting: It means that you split your single entity into two separate classes which are related by a navigation property. You can map then both entities to a single table in the database. It allows you to load the navigation properties (for instance the binary fields) on request (by lazy, eager or explicite loading). Details about this for EF4.0 are here and for EF4.1 here.
3) Your own proposal: Create separate tables and separate entities which are linked by navigation properties and FK constraints.
Say I have a common pattern with a Customer object and a SalesOrder object. I have corresponding SalesOrderContract and CustomerContract objects that are similar, flatter objects used to serialize through a web service
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public Address ShippingAddress { get; set; }
//more fields...
}
public class Order
{
public int OrderId { get; set; }
public Customer Customer { get; set;
// etc
}
And my sales order contract looks like this
public class OrderContract
{
public int OrderId { get; set; }
public int CustomerId { get; set; }
}
public class OrderTranslator
{
public static Order ToOrder(OrderContract contract)
{
return new Order { OrderId = contract.OrderId };
// just translate customer id or populate entire Customer object
}
}
I have a layer inbetween the service layer and business object layer that translates between the two. My question is this...do I populate the Order.Customer object on the other end since the Order table just needs the customer id. I don't carry the entire customer object in the OrderContract because it's not necessary and too heavy. But, as part of saving it, I have to validate that it's indeed a valid customer. I can do a few things
Populate the Order.Customer object completely based on the CustomerId when I translate between contract and entity.. This would require calling the CustomerRepository in a helper class that translates between entities and contracts. Doesn't feel right to me. Translator should really just be data mapping.
Create a domain service for each group of operations that performs the validation needed without populating the Order.Customer. This service would pull the Customer object based on Order.CustomerId and check to see if it's valid. Not sure on this because a sales order should be able to validate itself, but it's also not explicitly dealing with Orders as it also deals with Customers so maybe a domain service?
Create a seperate property Order.CustomerId and lazy load the customer object based on this.
Populate Order.Customer in from a factory class. Right now my factory classes are just for loading from database. I'm not really loading from datacontracts, but maybe it makes sense?
So the question is two part...if you have association properties in your enties that will be required to tell if something is completely valid before saving, do you just populate them? If you do, where you do actually do that because the contract/entity translator feels wrong?
The bottom line is that I need to be able to do something like
if (order.Customer == null || !order.Customer.IsActive)
{
//do something
}
The question is where does it make sense to do this? In reality my Order object has a lot of child entities required for validation and I don't want things to become bloated. This is why I'm considering making domain services to encapsulate validation since it's such a huge operation in my particular case (several hundred weird rules). But I also don't want to remove all logic making my objects just properties. Finding the balance is tough.
Hope that makes sense. If more background is required, let me know.
You have a couple of things going on here. I think part of the issue is mainly how you appear to have arranged your Translator class. Remember, for an entity, the whole concept is based on instance identity. So a Translator for an entity should not return a new object, it should return the correct instance of the object. That typically means you have to supply it with that instance in the first place.
It is perhaps useful to think in terms of updates vs creating a new object.
For an update the way I would structure this operation is as follows: I would have the web service that the application calls to get and return the contract objects. This web service calls both repositories and Translators to do it's work. The validation stays on the domain object.
In code an update would look something like the following.
Web Service:
[WebService]
public class OrderService
{
[WebMethod]
public void UpdateOrder(OrderContract orderContract)
{
OrderRepository orderRepository = new OrderRepository(_session);
// The key point here is we get the actual order itself
// and so Customer and all other objects are already either populated
// or available for lazy loading.
Order order = orderRepository.GetOrderByOrderContract(orderContract);
// The translator uses the OrderContract to update attribute fields on
// the actual Order instance we need.
OrderTranslator.OrderContractToOrder(ref order, orderContract);
// We now have the specific order instance with any properties updated
// so we can validate and then persist.
if (order.Validate())
{
orderRepository.Update(order);
}
else
{
// Whatever
}
}
}
Translator:
public static class OrderTranslator
{
public static void OrderContractToOrder(ref Order order, OrderContract orderContract)
{
// Here we update properties on the actual order instance passed in
// instead of creating a new Order instance.
order.SetSomeProperty(orderContract.SomeProperty);
// ... etc.
}
}
The key concept here is because we have an entity, we are getting the actual Order, the instance of the entity, and then using the translator to update attributes instead of creating a new Order instance. Because we are getting the original Order, not creating a new instance, presumably we can have all the associations either populated or populated by lazy load. We do not have to recreate any associations from an OrderContract so the issue goes away.
I think the other part of the issue may be your understanding of how a factory is designed. It is true that for entities a Factory may not set all the possible attributes - the method could become hopelessly complex if it did.
But what a factory is supposed to do is create all the associations for a new object so that the new object returned is in a valid state in terms of being a full and valid aggregate. Then the caller can set all the other various and sundry "simple" attributes.
Anytime you have a Factory you have to make decisions about what parameters to pass in. Maybe in this case the web service gets the actual Customer and passes it to the factory as a parameter. Or Maybe the web service passes in an Id and the factory is responsible for getting the actual Customer instance. It will vary by specific situation but in any case, however it gets the other objects required, a factory should return at minimum a fully populated object in terms of it's graph, i.e all relationships should be present and traversible.
In code a possible example of new Order creation might be:
[WebService]
public class OrderService
{
[WebMethod]
public void SaveNewOrder(OrderContract orderContract)
{
// Lets assume in this case our Factory has a list of all Customers
// so given an Id it can create the association.
Order order = OrderFactory.CreateNewOrder(orderContract.CustomerId);
// Once again we get the actual order itself, albeit it is new,
// and so Customer and all other objects are already either populated
// by the factory create method and/or are available for lazy loading.
// We can now use the same translator to update all simple attribute fields on
// the new Order instance.
OrderTranslator.OrderContractToOrder(ref order, orderContract);
// We now have the new order instance with all properties populated
// so we can validate and then persist.
if (order.Validate())
{
//Maybe you use a Repository - I use a unit of work but the concept is the same.
orderRepository.Save(order);
}
else
{
//Whatever
}
}
}
So, hope that helps?