nHibernate set referenced property by id - nhibernate

I have the following method which is called from Ajax:
[Authorize]
[ValidateInput(false)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public JsonNetResult CreateOrUpdateTimeRecord(TimeRecord tr)
{
TimeRecord trLocal;
if (tr.Id == -1 || tr.Id == 0)
{
trLocal = new TimeRecord
{
Description = tr.Description,
StartTime = tr.StartTime,
EndTime = tr.EndTime,
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},// _userRepo.Get(tr.User.Id),
Hdt = new Hdt {Id = tr.Hdt.Id}//_hdtRepo.Get(tr.Hdt.Id)
};
_timeRepo.Insert(trLocal);
}
else
{
trLocal = _timeRepo.Get(tr.Id);
trLocal.Description = tr.Description;
trLocal.StartTime = tr.StartTime;
trLocal.EndTime = tr.EndTime;
_timeRepo.Update(trLocal);
}
...
}
As you can see my TimeRecord has a reference to User and Hdt. Now I started to work with NHibernate Profiler which complains when I resolve my properties by loading them from their coresponding repositories. Which is clear to me since I actually don't need to query the database for that since I have the ID's for this objects.
User = _userRepo.Get(tr.User.Id),
Hdt = _hdtRepo.Get(tr.Hdt.Id)
But I'm not 100% sure if I can use this instead:
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},,
Hdt = new Hdt {Id = tr.Hdt.Id}
I guess NHibernate lazy proxies work the same way since they only contain just the ID of the related object and load the rest when it is needed. Do I have to attach this "new" oject anyway to my session?
Can someone tell me what is the correct way to do this?
Cheers,
Stefan

There are a few ways how to achieve that. One of them could be using the Load() method. Check Ayendes post: NHibernate – The difference between Get, Load and querying by id, an extract:
Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
Other words, we can do something like this
User = _userRepo.Load(tr.User.Id),
Hdt = _hdtRepo.Load(tr.Hdt.Id)
Where the Load would be encapsulating the session.Load()

Related

Update context in SQL Server from ASP.NET Core 2.2

_context.Update(v) ;
_context.SaveChanges();
When I use this code then SQL Server adds a new record instead of updating the
current context
[HttpPost]
public IActionResult PageVote(List<string> Sar)
{
string name_voter = ViewBag.getValue = TempData["Namevalue"];
int count = 0;
foreach (var item in Sar)
{
count = count + 1;
}
if (count == 6)
{
Vote v = new Vote()
{
VoteSarparast1 = Sar[0],
VoteSarparast2 = Sar[1],
VoteSarparast3 = Sar[2],
VoteSarparast4 = Sar[3],
VoteSarparast5 = Sar[4],
VoteSarparast6 = Sar[5],
};
var voter = _context.Votes.FirstOrDefault(u => u.Voter == name_voter && u.IsVoted == true);
if (voter == null)
{
v.IsVoted = true;
v.Voter = name_voter;
_context.Add(v);
_context.SaveChanges();
ViewBag.Greeting = "رای شما با موفقیت ثبت شد";
return RedirectToAction(nameof(end));
}
v.IsVoted = true;
v.Voter = name_voter;
_context.Update(v);
_context.SaveChanges();
return RedirectToAction(nameof(end));
}
else
{
return View(_context.Applicants.ToList());
}
}
You need to tell the DbContext about your entity. If you do var vote = new Vote() vote has no Id. The DbContext see this and thinks you want to Add a new entity, so it simply does that. The DbContext tracks all the entities that you load from it, but since this is just a new instance, it has no idea about it.
To actually perform an update, you have two options:
1 - Load the Vote from the database in some way; If you get an Id, use that to find it.
// Loads the current vote by its id (or whatever other field..)
var existingVote = context.Votes.Single(p => p.Id == id_from_param);
// Perform the changes you want..
existingVote.SomeField = "NewValue";
// Then call save normally.
context.SaveChanges();
2 - Or if you don't want to load it from Db, you have to manually tell the DbContext what to do:
// create a new "vote"...
var vote = new Vote
{
// Since it's an update, you must have the Id somehow.. so you must set it manually
Id = id_from_param,
// do the changes you want. Be careful, because this can cause data loss!
SomeField = "NewValue"
};
// This is you telling the DbContext: Hey, I control this entity.
// I know it exists in the DB and it's modified
context.Entry(vote).State = EntityState.Modified;
// Then call save normally.
context.SaveChanges();
Either of those two approaches should fix your issue, but I suggest you read a little bit more about how Entity Framework works. This is crucial for the success (and performance) of your apps. Especially option 2 above can cause many many issues. There's a reason why the DbContext keep track of entities, so you don't have to. It's very complicated and things can go south fast.
Some links for you:
ChangeTracker in Entity Framework Core
Working with Disconnected Entity Graph in Entity Framework Core

Dapper.Net and the DataReader

I have a very strange error with dapper:
there is already an open DataReader associated with this Command
which must be closed first
But I don't use DataReader! I just call select query on my server application and take first result:
//How I run query:
public static T SelectVersion(IDbTransaction transaction = null)
{
return DbHelper.DataBase.Connection.Query<T>("SELECT * FROM [VersionLog] WHERE [Version] = (SELECT MAX([Version]) FROM [VersionLog])", null, transaction, commandTimeout: DbHelper.CommandTimeout).FirstOrDefault();
}
//And how I call this method:
public Response Upload(CommitRequest message) //It is calling on server from client
{
//Prepearing data from CommitRequest
using (var tr = DbHelper.DataBase.Connection.BeginTransaction(IsolationLevel.Serializable))
{
int v = SelectQueries<VersionLog>.SelectVersion(tr) != null ? SelectQueries<VersionLog>.SelectVersion(tr).Version : 0; //Call my query here
int newVersion = v + 1; //update version
//Saving changes from CommitRequest to db
//Updated version saving to base too, maybe it is problem?
return new Response
{
Message = String.Empty,
ServerBaseVersion = versionLog.Version,
};
}
}
}
And most sadly that this exception appearing in random time, I think what problem in concurrent access to server from two clients.
Please help.
This some times happens if the model and database schema are not matching and an exception is being raised inside Dapper.
If you really want to get into this, best way is to include dapper source in your project and debug.

How can I rotate back?

I am developing a wcf service for Windows 8 APP. But I'm choked up at one point.
The following method, it is coming data in the database using entity. But this the data returns back to a class type. My question , if result is null what can I sent person who will this method
public AnketorDTO AnketorBul(string tc, string pass)
{
_entity = new AnketDBEntities();
var result = (from i in _entity.Anketors
where i.TC == tc
where i.Sifre == pass
select i).ToList();
if (!result.Any())
-->>> return new AnketorDTO();
Anketor anketor = result.First();
return Converter.ConvertAnketorToAnketorDTO(anketor);
}
with this methot I SENT it by creating a new class type but part which use this methot does not work because the values become null. how can we prevent it.
Client :
AnketorDTO anketor = await client.AnketorBulAsync(txtKullanici.Text, txtSifre.Password);
**if (anketor != null)
lblError.Text = anketor.Adi;**
else
lblError.Text = "Hata";
Can you try this method to see, if it works?
_entity = new AnketDBEntities();
var result = _entity.Anketors.FirstOrDefault(yourexpressions);

Entity Framework: Update entity or add if it doesn't exist

I have a scenario where I have to update an entity if it exists or add a new one if it doesn't.
I would like to execute a single method for this (it would be great if it were a single trip to the server).
Is there something like that in EF?
Right now my code looks like this:
var entity = db.Entities.FirstOrDefault(e => e.Id == myId);
if (entity == null)
{
entity = db.Entities.CreateObject();
entity.Id = myId;
}
entity.Value = "my modified value";
db.SaveChanges();
But I would like to avoid the first query, something like this:
var entity = new Entity();
entity.Id = myId;
entity.Value = "my modified value";
db.AddOrAttach(entity);
db.SaveChanges();
Is there anything similar? or do I have to perform the first query no matter what?
Thanks
You have to perform the first query no matter what, unfortunately.
One option would be to write a stored procedure that performs a T-SQL MERGE then map it to a function import, though that would require that you pass the scalar values of the entity as parameters (and support for navigation properties would be done), but it would accomplish what you're after.
I ran some quick test code for editing in MVC 3 with EF 4 and it seems to work for edit with following code:
using (var context = new TestStackOverFlowEntities())
{
Person p = new Person();
p.Id = long.Parse(collection["Id"]);
p.FirstName = collection["FirstName"];
p.LastName = collection["LastName"];
context.People.Attach(p);
context.ObjectStateManager.ChangeObjectState(p, System.Data.EntityState.Modified);
context.SaveChanges();
return RedirectToAction("Index");
}
Edit: I checked with creating new object too, you need to change this
context.ObjectStateManager.ChangeObjectState(p, System.Data.EntityState.Added);
when Id == 0 //ie new object.
Quick and dirty code to add new is this:
using (var context = new TestStackOverFlowEntities())
{
Person p = new Person();
p.Id = 0;
p.FirstName = collection["FirstName"];
p.LastName = collection["LastName"];
context.People.Attach(p);
context.ObjectStateManager.ChangeObjectState(p, System.Data.EntityState.Added);
context.SaveChanges();
return RedirectToAction("Index");
}
If you are just trying to limit code to clarify your controllers:
db.Attach(model);
db.SaveChanges(model);
Will update if the Entity Key exists, and create if it does not.

Using ActiveRecord/NHibernate, can I Delete and Refresh without a Flush?

I have the following Unit Test method:
void TestOrderItemDelete()
{
using (new SessionScope())
{
var order = Order.FindById(1234);
var originalItemCount = order.OrderItems.Count;
Assert.IsTrue(originalCount > 0);
var itemToDelete = order.OrderItems[0];
itemToDelete.DeleteAndFlush(); // itemToDelete.Delete();
order.Refresh();
Assert.AreEqual(originalCount - 1, order.OrderItems.Count);
}
}
As you can see from the comment after the DeleteAndFlush command, I had to change it from a simple Delete to get the Unit test to pass. Why is this? The same is not true for my other unit test for adding an OrderItem. This works just fine:
void TestOrderItemAdd()
{
using (new SessionScope())
{
var order = Order.FindById(1234);
var originalItemCount = order.OrderItems.Count;
var itemToAdd = new OrderItem();
itemToAdd.Order = order;
itemToAdd.Create(); // Notice, this is not CreateAndFlush
order.Refresh();
Assert.AreEqual(originalCount + 1, order.OrderItems.Count);
}
}
All of this came up when I started using Lazy Instantiation of the Order.OrderItems relationship mapping, and had to add the using(new SessionScope) block around the test.
Any ideas?
This is difficult to troubleshoot without knowing the contents of your mappings, but one possibility is that you have the ID property of the OrderItem mapped using an identity field (or sequence, etc.) in the DB. If this is the case, NHibernate must make a trip to the database in order to generate the ID field, so the OrderItem is inserted immediately. This is not true of a delete, so the SQL delete statement isn't executed until session flush.