I'm working on a module in orchardCMS and I am trying to save object in database but when I invoke the method create it throws exception saying
"NHibernate Property Value exception"
not-null property references a null or transient value LinksHandling.Models.Links_DM.ContentItemRecord
In my db table all columns are nullable but ID column is not nullable and Id Column is Identity so in my object I have values in all attributes and id is set to 0.
When I go to save this object as below it donot work. However I have made such a function earlier and it was working as well.
public bool createLink(Links_DM linkModel)
{
var link = _linksRepository.Table.FirstOrDefault(p => p.LinkUrl == linkModel.LinkUrl);
if (link == null)
{
try {
_linksRepository.Create(linkModel);
return true;
}
catch (Exception)
{
return false;
}
}
return false;
}
I'm banging my head to figure the issue but could not reach to a solution yet.
here is my Migration.cs
SchemaBuilder.CreateTable(typeof(Links_DM).Name,
table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("LinkUrl")
.Column<string>("LinkName")
.Column<int>("PageId")
.Column<bool>("IsActive")
);
Please help me figure out The issue.
Thanks,
Sohaib
Related
I am writing an API that need to access the value of an entity and check if it has changed (userChangedPairOrSingle).
public ActionResult<ProdutoFabricante> Put([FromBody] ProdutoFabricanteViewModel produtoFabricanteViewModel)
{
if (produtoFabricanteViewModel.Invalid)
{
return StatusCode(400, produtoFabricanteViewModel);
}
try
{
var actualProdutoFabricante = produtoFabricanteRepository.GetById(produtoFabricanteViewModel.Id);
if (produtoFabricanteService.userChangedPairOrSingle(produtoFabricanteViewModel, actualProdutoFabricante.Par))
{
if (produtoFabricanteService.produtoFabricanteHasItems(produtoFabricanteViewModel.Id))
{
return StatusCode(400, new { Message = "Não é possível alterar " });
}
}
actualProdutoFabricante = mapper.Map<ProdutoFabricante>(produtoFabricanteViewModel);
produtoFabricanteRepository.Update(actualProdutoFabricante);
return Ok(actualProdutoFabricante);
}
catch (Exception ex)
{
return StatusCode(500, (ex.Message, InnerException: ex.InnerException?.Message));
}
}
But when I access the same entity that I am going to Update it gives me the following error:
The instance of entity type 'ProdutoFabricante' cannot be tracked because another instance with the key value '{Id: 13}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
How can I avoid this error?
When you're mapping, you're creating a new instance (which isn't tracked) instead of updating the existing instance (which is tracked). Instead, you need to do:
mapper.Map(produtoFabricanteViewModel, actualProdutoFabricante);
That will keep the existing instance, and simply update the property values on it. Then, EF will be fine, because this instance is the same one that's tracked.
I am changing my project to post multiple objects to my db table. The issue I am having is, after I changed the Post method I do not know how to return the created route with the Id's of the objects. I have not done this before so I believe I a correct on everything else up until that point.
public async Task<IHttpActionResult> PostNewPurchaseOrderDetail([FromBody]IEnumerable<PurchaseOrderDetail> newPurchaseOrderDetail)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var context = new ApplicationDbContext())
{
context.PurchaseOrderDetails.AddRange(newPurchaseOrderDetail);
await context.SaveChangesAsync();
return CreatedAtRoute("PurchaseOrderDetailApi", new { newPurchaseOrderDetail.PurchaseOrderDetailId }, newPurchaseOrderDetail);
}
}
catch (Exception ex)
{
return this.BadRequest(ex.Message);
}
}
Error Message
Error 2 'System.Collections.Generic.IEnumerable' does not contain a definition for 'PurchaseOrderDetailId' and no extension method 'PurchaseOrderDetailId' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?) C:\Development\TexasExterior\TexasExterior\apiControllers\apiPurchaseOrderDetailController.cs 58 98 TexasExterior
The error tells you exactly what the issue is. You variable, newPurchaseOrderDetail is an enumerable of PurchaseOrderDetail, and you're trying to reference a property of PurchaseOrderDetail directly off of it. You need to get a single item from the list before you can call the property. For example:
return CreatedAtRoute("PurchaseOrderDetailApi", new { newPurchaseOrderDetail.First().PurchaseOrderDetailId }, newPurchaseOrderDetail);
Notice the .First(). However, that would only give you the id of the first item in the collection, which is probably not what you want. I'm not sure what your CreatedAtRoute method does or how it works, but you could change the second parameter to expect a collection of ids and then pass something like:
newPurchaseOrderDetail.Select(m => m.PurchaseOrderDetailId)
Which would give you a list of ids.
I am learning MVC2 and I am trying to create a data request management system. Somewhat like a ticketing system. A quick question, in my mvc controller class I have a post-create
[HttpPost]
public ActionResult Create(Request request)
{
if (ModelState.IsValid)
{
try
{
// TODO: Add insert logic here
var db = new DB();
db.Requests.InsertOnSubmit(request);
db.SubmitChanges();
return RedirectToAction("Index");
}
catch
{
return View(request);
}
}
else
{
return View(request);
}
}
Ok, this is extremely simple enough, well I add my view and once I create a row I get the 0 first in my Primary Key row. Then it will not increment anymore, I goto add another row and the catch returns me to the same view I am on. It seems that the primary key int id is not incrementing.
How do you auto increment the id (type int) here? I am a bit confused why MVC isn't handling this since it is the primary key type int. It will only make the first row with the id = 0 and that's all.
Your ID column needs to be set as an Identity column in the table in SQL server.
Also you should create your DB data context in a using:
using(var db = new DB())
{
db.Requests.InsertOnSubmit(request);
db.SubmitChanges();
return RedirectToAction("Index");
}
Otherwise you're spilling connections all over the place; and creating more memory leaks than an early build of windows (well, depending on your traffic ;) )
The ExamVersion class has an int? property named SourceSafeVersionNum
When I execute the following code:
var query = from examVersion in db.ExamVersions
where examVersion.ExamVersionID == ExamVersionID
select examVersion;
foreach (ExamVersion examVer in query.ToList())
{
yield return examVer;
}
examVer.SourceSafeVersionNum is set to 1 even though it is NULL in the database.
When I run the SQL code generated by LINQ in SQL Server, the SourceSafeVersionNum column value is NULL (as I'd expect) but in the foreach loop the examVer.SourceSafeVersionNum is 1.
I can't find anywhere in the code where a default value is assigned or any similar logic.
Any ideas why/where this value is being set to 1?
Here is the property declaration (generated by a .dbml file)
[Column(Storage="_SourceSafeVersionNum", DbType="Int", UpdateCheck=UpdateCheck.Never)]
public System.Nullable<int> SourceSafeVersionNum
{
get
{
return this._SourceSafeVersionNum;
}
set
{
if ((this._SourceSafeVersionNum != value))
{
this.OnSourceSafeVersionNumChanging(value);
this.SendPropertyChanging();
this._SourceSafeVersionNum = value;
this.SendPropertyChanged("SourceSafeVersionNum");
this.OnSourceSafeVersionNumChanged();
}
}
}
Have you tried setting a breakpoint in the set{} method of the property to see what else might be populating its value? You might catch the culprit in the act, then look at the Call Stack to see who it is.
As a follow up to this, here is what happened:
The code that retrieved the value from the database was being called twice but through two different code paths. The code path that was assigning the value of 1 was being stepped over by the debugger so I didn't see it.
Ayende has an article about how to implement a simple audit trail for NHibernate (here) using event handlers.
Unfortunately, as can be seen in the comments, his implementation causes the following exception to be thrown: collection xxx was not processed by flush()
The problem appears to be the implicit call to ToString on the dirty properties, which can cause trouble if the dirty property is also a mapped entity.
I have tried my hardest to build a working implementation but with no luck.
Does anyone know of a working solution?
I was able to solve the same problem using following workaround: set the processed flag to true on all collections in the current persistence context within the listener
public void OnPostUpdate(PostUpdateEvent postEvent)
{
if (IsAuditable(postEvent.Entity))
{
//skip application specific code
foreach (var collection in postEvent.Session.PersistenceContext.CollectionEntries.Values)
{
var collectionEntry = collection as CollectionEntry;
collectionEntry.IsProcessed = true;
}
//var session = postEvent.Session.GetSession(EntityMode.Poco);
//session.Save(auditTrailEntry);
//session.Flush();
}
}
Hope this helps.
The fix should be the following. Create a new event listener class and derive it from NHibernate.Event.Default.DefaultFlushEventListener:
[Serializable]
public class FixedDefaultFlushEventListener: DefaultFlushEventListener
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void PerformExecutions(IEventSource session)
{
if (log.IsDebugEnabled)
{
log.Debug("executing flush");
}
try
{
session.ConnectionManager.FlushBeginning();
session.PersistenceContext.Flushing = true;
session.ActionQueue.PrepareActions();
session.ActionQueue.ExecuteActions();
}
catch (HibernateException exception)
{
if (log.IsErrorEnabled)
{
log.Error("Could not synchronize database state with session", exception);
}
throw;
}
finally
{
session.PersistenceContext.Flushing = false;
session.ConnectionManager.FlushEnding();
}
}
}
Register it during NHibernate configuraiton:
cfg.EventListeners.FlushEventListeners = new IFlushEventListener[] { new FixedDefaultFlushEventListener() };
You can read more about this bug in Hibernate JIRA:
https://hibernate.onjira.com/browse/HHH-2763
The next release of NHibernate should include that fix either.
This is not easy at all. I wrote something like this, but it is very specific to our needs and not trivial.
Some additional hints:
You can test if references are loaded using
NHibernateUtil.IsInitialized(entity)
or
NHibernateUtil.IsPropertyInitialized(entity, propertyName)
You can cast collections to the IPersistentCollection. I implemented an IInterceptor where I get the NHibernate Type of each property, I don't know where you can get this when using events:
if (nhtype.IsCollectionType)
{
var collection = previousValue as NHibernate.Collection.IPersistentCollection;
if (collection != null)
{
// just skip uninitialized collections
if (!collection.WasInitialized)
{
// skip
}
else
{
// read collections previous values
previousValue = collection.StoredSnapshot;
}
}
}
When you get the update event from NHibernate, the instance is initialized. You can safely access properties of primitive types. When you want to use ToString, make sure that your ToString implementation doesn't access any referenced entities nor any collections.
You may use NHibernate meta-data to find out if a type is mapped as an entity or not. This could be useful to navigate in your object model. When you reference another entity, you will get additional update events on this when it changed.
I was able to determine that this error is thrown when application code loads a Lazy Propery where the Entity has a collection.
My first attempt involed watching for new CollectionEntries (which I've never want to process as there shouldn't actually be any changes). Then mark them as IsProcessed = true so they wouldn't cause problems.
var collections = args.Session.PersistenceContext.CollectionEntries;
var collectionKeys = args.Session.PersistenceContext.CollectionEntries.Keys;
var roundCollectionKeys = collectionKeys.Cast<object>().ToList();
var collectionValuesClount = collectionKeys.Count;
// Application code that that loads a Lazy propery where the Entity has a collection
var postCollectionKeys = collectionKeys.Cast<object>().ToList();
var newLength = postCollectionKeys.Count;
if (newLength != collectionValuesClount) {
foreach (var newKey in postCollectionKeys.Except(roundCollectionKeys)) {
var collectionEntry = (CollectionEntry)collections[newKey];
collectionEntry.IsProcessed = true;
}
}
However this didn't entirly solve the issue. In some cases I'd still get the exception.
When OnPostUpdate is called the values in the CollectionEntries dictionary should all already be set to IsProcessed = true. So I decided to do an extra check to see if the collections not processed matched what I expected.
var valuesNotProcessed = collections.Values.Cast<CollectionEntry>().Where(x => !x.IsProcessed).ToList();
if (valuesNotProcessed.Any()) {
// Assert: valuesNotProcessed.Count() == (newLength - collectionValuesClount)
}
In the cases that my first attempt fixed these numbers would match exactly. However in the cases where it didn't work there were extra items alreay in the dictionary. In my I could be sure these extra items also wouldn't result in updates so I could just set IsProcessed = true for all the valuesNotProcessed.