Session.queryover() and session.save() are working well. All logics getting data from or saving data to the database are working well.Changes to database through session.update() and session.delete() are not reflected. No exeption was thrown. Using session.SaveOrUpdate did not solve this problem.
This is my mapping
[Serializable]
public class Requirement
{
public virtual int Id { get; set; }
public virtual int CreditRequired { get; set; }
public virtual string Name { get; set; }
public virtual IList<CourseRequirement> CourseRequirements
{
get
{
return new RequirementBC().getCourseRequirement(Id);
}
}
public Requirement()
{ }
public Requirement(DataRow row)
{
Id = int.Parse(row["Id"].ToString());
CreditRequired = int.Parse(row["CreditRequired"].ToString());
Name = row["Name"].ToString();
}
}
public class RequirementMap : ClassMapping<Requirement>
{
public RequirementMap()
{
Table("Requirements");
Id<int>(x => x.Id, m => { m.Column("Id"); m.Generator(Generators.Native); });
Property<int>(x => x.CreditRequired, m => { m.Column("CreditRequired");});
Property<string>(x => x.Name, m => { m.Column("Name"); });
}
}
This is my logic
[Serializable]
public class RequirementBC
{
ISession session = NHibernateHelper.GetCurrentSession();
public void UpdateRequirement(int reqId, string newName, int creditsRequired)
{
session.BeginTransaction();
var req = session.QueryOver<Requirement>().Where(x => x.Id == reqId).SingleOrDefault<Requirement>();
var old = session.QueryOver<Requirement>().Where(x => x.Name == newName && x.Id != reqId).SingleOrDefault<Requirement>();
if (old != null)
throw new Exception("Requirement with that name already exists");
req.Name = newName;
req.CreditRequired = creditsRequired;
session.Update(req);
session.Flush();
session.Transaction.Commit();
}
}
Logic for getting current session
public static ISession GetCurrentSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CURRENT_NHIBERNATE_SESSION_KEY] as ISession;
if (currentSession == null)
{
currentSession = sessionFactory.OpenSession();
context.Items[CURRENT_NHIBERNATE_SESSION_KEY] = currentSession;
}
if (currentSession.Connection.State == System.Data.ConnectionState.Closed)
{
currentSession = sessionFactory.OpenSession();
}
if (!currentSession.IsConnected)
{
currentSession = sessionFactory.OpenSession();
}
if (!currentSession.IsOpen)
{
currentSession = sessionFactory.OpenSession();
}
if (currentSession.IsDirty())
{
currentSession.Clear();
}
return currentSession;
}
From my searches on this forum and others, those who have encountered such were getting one exception or the other, but no exception was thrown in my own case which makes the problem difficult to trace.
Any help will be appreciated.
You shouldn't have to flush the session if you are using transactions. Also I'm not sure about the syntax you are using for transactions. If I were to do this it would look like this:
public void UpdateRequirement(int reqId, string newName, int creditsRequired)
{
using(ISession session = NHibernateHelper.GetCurrentSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
var req = session.QueryOver<Requirement>().Where(x => x.Id == reqId).SingleOrDefault<Requirement>();
var old = session.QueryOver<Requirement>().Where(x => x.Name == newName && x.Id != reqId).SingleOrDefault<Requirement>();
if (old != null)
throw new Exception("Requirement with that name already exists");
req.Name = newName;
req.CreditRequired = creditsRequired;
transaction.Commit();
}
}
}
It is not required to call Update() on an object already tracked by the session. (And Update() will not necessarily execute an UPDATE SQL statement.)
Where do you open and commit your transaction?
Your GetCurrentSession() looks weird - so far I've not felt the need to have all those check my "get-current-session" methods. Particularly the if-dirty-then-clear looks strange. I would typically expect to be able to call "get-current-session" multiple times within a unit-of-work without it have the side effect of throwing away any changes to far.
Related
I have the Cache layer described below. The problem is that I need to get Lists or specific element from List, and then modify it, then persist it to db, and update the cache list.
So to avoid problems between the cache layer & DB layer, when getting data from cache I should make/get a copy of it, so it doesn't get changed down the way (if it's changes DB layer throws an error).
Question is how to properly approach this copy strategy, and other question is should the lock be present for add/remove operations too? (if yes, what is the right way to lock prior to modify?)
The cache is a singleton DI through services.
services.AddSingleton<MyCache>();
public class MyCache
{
private readonly AsyncLock _mutex = new AsyncLock();
public MemoryCache Cache { get; set; }
public MyCache()
{
Cache = new MemoryCache(new MemoryCacheOptions());
// populate cache from DB
}
public async Task<List<UDT>> GetUDTsForUser(string id, bool fetchFromDB = false)
{
List<UDT> list = new List<UDT>();
Cache.TryGetValue(id, out list);
if (list == null)
{
using (await _mutex.LockAsync())
{
Cache.TryGetValue(id, out list);
if (list == null && fetchFromDB)
{
var DAO = new DAO();
list = (await DAO.GetInfoForUser(id))?.UDTs;
if (list != null)
{
Cache.Set(id, list);
}
}
}
}
return list;
}
public async Task<UDT> GetUDTForUser(string id, long udtId)
{
List<UDT> list = new List<UDT>();
Cache.TryGetValue(id, out list);
if (list == null)
{
using (await _mutex.LockAsync())
{
Cache.TryGetValue(id, out list);
if (list == null)
{
var DAO = new DAO();
list = (await DAO.GetInfoForUser(id)).UDTs;
Cache.Set(id, list);
return list.FirstOrDefault(u => u.Id == udtId);
}
}
}
return list.FirstOrDefault(u => u.Id == udtId);
}
public void AddElement(UDT udt)
{
if (udt == null)
return;
var udtList = Cache.GetOrCreate(udt.GroupId, entry => {
return new List<UDT>();
});
if (udtList.Contains(udt) == false)
{
udtList.Add(udt);
Cache.Set(udt.GroupId, udtList);
}
}
public void RemoveElement(string groupId, long udtId)
{
var udtList = Cache.Get<List<UDT>>(groupId);
if (udtList != null)
{
udtList.RemoveAll(e => e.Id == udtId);
Cache.Set(groupId, udtList);
}
}
}
I have fields for audit trail in each table (InsertedBy, InsertedDate, UpdatedBy and UpdatedDate), I build solution to reduce redundant before by override savechange():
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(e =>
e.State == System.Data.Entity.EntityState.Added || e.State == System.Data.Entity.EntityState.Modified))
{
Auditing.ApplyAudit(entry, User);
}
return base.SaveChanges();
}
public class Auditing
{
public static void ApplyAudit(DbEntityEntry entityEntry, int User)
{
Type type = entityEntry.Entity.GetType();
if (entityEntry.State.ToString() == "Added")
{
if (type.GetProperty("InsertedBy") != null)
{
entityEntry.Property("InsertedBy").CurrentValue = User;
}
if (type.GetProperty("InsertedDate") != null)
{
entityEntry.Property("InsertedDate").CurrentValue = DateTime.Now;
}
}
else if (entityEntry.State.ToString() == "Modified")
{
if (type.GetProperty("InsertedBy") != null)
{
entityEntry.Property("InsertedBy").IsModified = false;
}
if (type.GetProperty("InsertedDate") != null)
{
entityEntry.Property("InsertedDate").IsModified = false;
}
if (type.GetProperty("UpdatedBy") != null)
{
entityEntry.Property("UpdatedBy").CurrentValue = User;
}
if (type.GetProperty("UpdatedDate") != null)
{
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
}
}
the question is:
is using reflection within each entity before modified or added waste in memory and performance ? if yes is there is best practice for this ?
is this another code snippet better in performance or just use reflection also?
public static void ApplyAudit(DbEntityEntry entityEntry, long User)
{
if (entityEntry.State.ToString() == "Added")
{
entityEntry.Property("InsertedBy").CurrentValue = User;
entityEntry.Property("InsertedDate").CurrentValue = DateTime.Now;
}
else if (entityEntry.State.ToString() == "Modified")
{
entityEntry.Property("InsertedBy").IsModified = false;
entityEntry.Property("InsertedDate").IsModified = false;
entityEntry.Property("UpdatedBy").CurrentValue = User;
entityEntry.Property("UpdatedDate").CurrentValue = DateTime.Now;
}
}
is entityEntry.Property("InsertedBy") uses reflection ?
Reflection is slow (slow is subjective) and if you want to avoid it, then you need to get rid of such code as below:
Type type = entityEntry.Entity.GetType();
if (type.GetProperty("InsertedBy") != null)
Even if it was not slow, the code above is still "buggy" because a programmer may mistakenly write InsertBy instead of InsertedBy. This can easily be avoided with help from the compiler using the approach below.
Use an interface and implement it in all entities that require audit.
public interface IAuditable
{
string InsertedBy { get; set; }
// ... other properties
}
public class SomeEntity : IAuditable
{
public string InsertedBy { get; set; }
}
public class Auditor<TAuditable> where TAuditable : IAuditable
{
public void ApplyAudit(TAuditable entity, int userId)
{
// No reflection and you get compiler support
if (entity.InsertedBy == null)
{
// whatever
}
else
{
// whatever
}
}
}
As mentioned in the comments, you will get compiler support and reflection is not used anymore. I would even go a step further and not pass the int userId. I will bring the code for figuring out the userId and put it in this class. That way the class is self sufficient and clients do not need to provide it this information.
Usage:
var e = new SomeEntity();
var auditor = new Auditor<SomeEntity>();
auditor.ApplyAudit(e, 1); // 1 is userId, I am just hardcoding for brevity
Or use it from your context:
public override int SaveChanges()
{
var auditables = ChangeTracker.Entries().Where(e =>
e.State == System.Data.Entity.EntityState.Added || e.State == System.Data.Entity.EntityState.Modified)
.OfType<IAuditable>();
var auditor = new Auditor<IAuditable>();
foreach (var entry in auditables)
{
// 1 is userId, I am just hardcoding for brevity
auditor.ApplyAudit(entry, 1);
}
return base.SaveChanges();
}
This means that all entities who are auditable will need to implement the IAuditable interface. EF generates partial classes for your entities but do not modify those partial classes because the next time you run the custom tool, it will be wiped out.
Instead, create another partial class with the same name and implement the IAuditable.
public partial class SomeEntity : IAuditable {}
An even better approach is to create a custom T4 template so it creates the partial class with the code : IAuditable. Please see this article for how to do that.
I'm currently writing integration tests using nunit for a previously untested server that was written in C# using ApiController and Entity Framework. Most of the tests run just fine, but I've ran into two that always cause the database to time out. The error messages look something like this:
System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
System.Data.Entity.Core.UpdateException : An error occurred while updating the entries. See the inner exception for details.
System.Data.SqlClient.SqlException : Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
System.ComponentModel.Win32Exception : The wait operation timed out
The first test that's timing out:
[TestCase, WithinTransaction]
public async Task Patch_EditJob_Success()
{
var testJob = Data.SealingJob;
var requestData = new Job()
{
ID = testJob.ID,
Name = "UPDATED"
};
var apiResponse = await _controller.EditJob(testJob.ID, requestData);
Assert.IsInstanceOf<StatusCodeResult>(apiResponse);
Assert.AreEqual("UPDATED", testJob.Name);
}
The other test that's timing out:
[TestCase, WithinTransaction]
public async Task Post_RejectJob_Success()
{
var rejectedJob = Data.SealingJob;
var apiResponse = await _controller.RejectJob(rejectedJob.ID);
Assert.IsInstanceOf<OkResult>(apiResponse);
Assert.IsNull(rejectedJob.Organizations);
Assert.AreEqual(rejectedJob.JobStatus, JobStatus.OnHold);
_fakeEmailSender.Verify(
emailSender => emailSender.SendEmail(rejectedJob.Creator.Email, It.Is<string>(emailBody => emailBody.Contains(rejectedJob.Name)), It.IsAny<string>()),
Times.Once());
}
These are the controller methods that these tests are using:
The timeout always happens on the first call to await db.SaveChangesAsync() within the controller. Other controller methods that are being tested also call SaveChangesAsync without any problem. I've also tried calling SaveChangesAsync from within the failing tests and it works fine there. Both of these methods they are calling work normally when called from within the controller, but time out when called from the tests.
[HttpPatch]
[Route("editjob/{id}")]
public async Task<IHttpActionResult> EditJob(int id, Job job)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != job.ID)
{
return BadRequest();
}
Job existingJob = await db.Jobs
.Include(databaseJob => databaseJob.Regions)
.FirstOrDefaultAsync(databaseJob => databaseJob.ID == id);
existingJob.Name = job.Name;
// For each Region find if it already exists in the database
// If it does, use that Region, if not one will be created
for (var i = 0; i < job.Regions.Count; i++)
{
var regionId = job.Regions[i].ID;
var foundRegion = db.Regions.FirstOrDefault(databaseRegion => databaseRegion.ID == regionId);
if (foundRegion != null)
{
existingJob.Regions[i] = foundRegion;
db.Entry(existingJob.Regions[i]).State = EntityState.Unchanged;
}
}
existingJob.JobType = job.JobType;
existingJob.DesignCode = job.DesignCode;
existingJob.DesignProgram = job.DesignProgram;
existingJob.JobStatus = job.JobStatus;
existingJob.JobPriority = job.JobPriority;
existingJob.LotNumber = job.LotNumber;
existingJob.Address = job.Address;
existingJob.City = job.City;
existingJob.Subdivision = job.Subdivision;
existingJob.Model = job.Model;
existingJob.BuildingDesignerName = job.BuildingDesignerName;
existingJob.BuildingDesignerAddress = job.BuildingDesignerAddress;
existingJob.BuildingDesignerCity = job.BuildingDesignerCity;
existingJob.BuildingDesignerState = job.BuildingDesignerState;
existingJob.BuildingDesignerLicenseNumber = job.BuildingDesignerLicenseNumber;
existingJob.WindCode = job.WindCode;
existingJob.WindSpeed = job.WindSpeed;
existingJob.WindExposureCategory = job.WindExposureCategory;
existingJob.MeanRoofHeight = job.MeanRoofHeight;
existingJob.RoofLoad = job.RoofLoad;
existingJob.FloorLoad = job.FloorLoad;
existingJob.CustomerName = job.CustomerName;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!JobExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
[HttpPost]
[Route("{id}/reject")]
public async Task<IHttpActionResult> RejectJob(int id)
{
var organizations = await db.Organizations
.Include(databaseOrganization => databaseOrganization.Jobs)
.ToListAsync();
// Remove job from being shared with organizations
foreach (var organization in organizations)
{
foreach (var organizationJob in organization.Jobs)
{
if (organizationJob.ID == id)
{
organization.Jobs.Remove(organizationJob);
}
}
}
var existingJob = await db.Jobs.FindAsync(id);
existingJob.JobStatus = JobStatus.OnHold;
await db.SaveChangesAsync();
await ResetJob(id);
var jobPdfs = await DatabaseUtility.GetPdfsForJobAsync(id, db);
var notes = "";
foreach (var jobPdf in jobPdfs)
{
if (jobPdf.Notes != null)
{
notes += jobPdf.Name + ": " + jobPdf.Notes + "\n";
}
}
// Rejection email
var job = await db.Jobs
.Include(databaseJob => databaseJob.Creator)
.SingleAsync(databaseJob => databaseJob.ID == id);
_emailSender.SendEmail(
job.Creator.Email,
job.Name + " Rejected",
notes);
return Ok();
}
Other code that might be relevant:
The model being used is just a normal code-first Entity Framework class:
public class Job
{
public Job()
{
this.Regions = new List<Region>();
this.ComponentDesigns = new List<ComponentDesign>();
this.MetaPdfs = new List<Pdf>();
this.OpenedBy = new List<User>();
}
public int ID { get; set; }
public string Name { get; set; }
public List<Region> Regions { get; set; }
// etc...
}
To keep the database clean between tests, I'm using this custom attribute to wrap each one in a transaction (from http://tech.trailmax.info/2014/03/how-we-do-database-integration-tests-with-entity-framework-migrations/):
public class WithinTransactionAttribute : Attribute, ITestAction
{
private TransactionScope _transaction;
public ActionTargets Targets => ActionTargets.Test;
public void BeforeTest(ITest test)
{
_transaction = new TransactionScope();
}
public void AfterTest(ITest test)
{
_transaction.Dispose();
}
}
The database connection and controller being tested is build in setup methods before each test:
[TestFixture]
public class JobsControllerTest : IntegrationTest
{
// ...
private JobsController _controller;
private Mock<EmailSender> _fakeEmailSender;
[SetUp]
public void SetupController()
{
this._fakeEmailSender = new Mock<EmailSender>();
this._controller = new JobsController(Database, _fakeEmailSender.Object);
}
// ...
}
public class IntegrationTest
{
protected SealingServerContext Database { get; set; }
protected TestData Data { get; set; }
[SetUp]
public void SetupDatabase()
{
this.Database = new SealingServerContext();
this.Data = new TestData(Database);
}
// ...
}
This bug was apparently caused by the use of await within a TransactionScope. Following the top answer to this question, I added the TransactionScopeAsyncFlowOption.Enabled parameter when constructing the TransactionScope and the timeout issue went away.
I need to migrate some data in an Orchard module running within a Orchard 1.9.0 installation. The issue here is that the data is stored in a foreign DB on another server, not in the Orchard DB. So when the migration class methods get called, internally Orchard uses the Orchard.Data.ISessionLocator interface to retrieve the DB connection. Sadly overriding this behavior is not possible but i had the idea to hook into the session locator thing by creating a custom session locator.
The custom session locator looks like this and is based on the existing class Orchard.Data.SessionLocator:
public class CustomSessionLocator : Orchard.Data.ISessionLocator, Orchard.Data.ITransactionManager, System.IDisposable
{
// public
public CustomSessionLocator(Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder)
{
Logger = Orchard.Logging.NullLogger.Instance;
IsolationLevel = System.Data.IsolationLevel.ReadCommitted;
mSessionFactoryHolder = aSessionFactoryHolder;
mSessions = new System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession>();
if (mForeignDBConnection == null)
{
string lConnectionString = "data source=myServer;initial catalog=myDB;persist security info=True;user id=xxx;password=xxx;MultipleActiveResultSets=True;";
mForeignDBConnection = new System.Data.SqlClient.SqlConnection(lConnectionString);
}
}
public NHibernate.ISession For(System.Type aEntityType)
{
Logger.Debug("Acquiring session for {0}", aEntityType);
Demand();
return mSessions[CurrentSessionScope];
}
public void Demand()
{
EnsureSession(IsolationLevel);
}
public void RequireNew()
{
RequireNew(IsolationLevel);
}
public void RequireNew(System.Data.IsolationLevel aLevel)
{
DisposeSession();
EnsureSession(aLevel);
}
public void Cancel()
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null && !lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
{
Logger.Debug("Rolling back transaction");
lSession.Transaction.Rollback();
}
}
public void Dispose()
{
DisposeSession();
}
public enum SessionScope
{
OrchardDefault,
ForeignDB
}
public Orchard.Logging.ILogger Logger { get; set; }
public System.Data.IsolationLevel IsolationLevel { get; set; }
public SessionScope CurrentSessionScope { private get; set; }
// private
private void DisposeSession()
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
{
try
{
if (!lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
{
Logger.Debug("Committing transaction");
lSession.Transaction.Commit();
}
}
finally
{
Logger.Debug("Disposing session");
var lConnection = lSession.Connection;
lSession.Close();
lSession.Dispose();
lSession = null;
mSessions[CurrentSessionScope] = null;
}
}
}
private void EnsureSession(System.Data.IsolationLevel aLevel)
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
return;
var lSessionFactory = mSessionFactoryHolder.GetSessionFactory();
Logger.Debug("Opening NHibernate session");
if (CurrentSessionScope == SessionScope.ForeignDB)
{
lSession = lSessionFactory.OpenSession(mForeignDBConnection);
// open connection otherwise the following lSession.BeginTransaction() fails with exception
if (mForeignDBConnection.State == System.Data.ConnectionState.Closed)
mForeignDBConnection.Open();
}
else
lSession = lSessionFactory.OpenSession();
mSessions[CurrentSessionScope] = lSession;
lSession.BeginTransaction(aLevel);
}
private readonly Orchard.Data.ISessionFactoryHolder mSessionFactoryHolder;
private System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession> mSessions;
private static System.Data.SqlClient.SqlConnection mForeignDBConnection;
}
Then i have a migration data interpreter that looks like this:
public class ForeignDataMigrationInterpreter : Orchard.Data.Migration.Interpreters.DefaultDataMigrationInterpreter
{
public ForeignDataMigrationInterpreter(
Orchard.Environment.Configuration.ShellSettings aShellSettings,
Orchard.Data.ISessionLocator aSessionLocator,
System.Collections.Generic.IEnumerable<Orchard.Data.Migration.Interpreters.ICommandInterpreter> aCommandInterpreters,
Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder,
Orchard.Reports.Services.IReportsCoordinator aReportsCoordinator)
: base(aShellSettings, aSessionLocator, aCommandInterpreters, aSessionFactoryHolder, aReportsCoordinator)
{
mSessionLocator = aSessionLocator as CustomSessionLocator;
}
public override void Visit(Orchard.Data.Migration.Schema.CreateTableCommand aCommand)
{
#if LIVE
if (IsForeignDBCommand(aCommand.Name, ""))
mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.ForeignDB;
else
mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.OrchardDefault;
#endif
base.Visit(aCommand);
}
...
private bool IsForeignDBCommand(...)
{
return ...;
}
private CustomSessionLocator mSessionLocator;
}
As you can see, the basic procedure with foreign data is
Start Orchard
Migration class method is called which contains SchemaBuilder.CreateTable()
ForeignDataMigrationInterpreter.Visit(CreateTableCommand) is called
CurrentSessionScope of custom session locator is updated to SessionScope.ForeignDB
CreateTableCommand is passed into base class
CustomSessionLocator.For() is called which
ends in CustomSessionLocator.EnsureSession() which
returns the session X for scope SessionScope.ForeignDB
base class enlists CreateTableCommand to transaction of session X
fast forward some unrelated additional steps and the transaction is commited BUT it never returns and a timeout exception occures
My questions are
Is it even possible to migrate foreign data this way?
Why does timeout occur?
Let's assume I have an instance of this class.
public class MyClass
{
public string LocationCode;
public string PickUpCode
}
There is another class that takes a List<MyClass> as input and saves to the DB.
Now I need to apply some business rules:
For example if LocationCode is null this item in the List<MyClass> must be skipped and the foreach loop must continue to the next item in the list.
I've written the following code and the items with null LocationCode are indeed skipped but the var instance = new SomeClass(); somehow remains in memory so when the loop reaches an valid item and proceeds to save it in the DB, it also saves all the previously skipped instances of var instance = new SomeClass();. Which means I have null entries in the DB.
I'm using NHibernate and Evictdoesn't seam to be doing the trick. Any suggestions?
public void Save(List<MyClass> listOfItems)
{
using (UnitOfWork.Start())
{
var repository = new Repository();
try
{
foreach (var item in listOfItems.Select(i => i.Item).Where(item => item != null))
{
var instance = new SomeClass();
if (pickUpCode != null)
{
instance.PickUpCode = pickUpCode;
}
else
{
instance.PickUpCode = null;
}
if (locationCode != null)
{
instance.StartLocation = locationCode
}
else
{
UnitOfWork.CurrentSession.Evict(instance);
continue;
}
repository.SaveSomeClass(instance);
}
}
catch (Exception ex)
{
_log.Error(" Unhandled error", ex);
}
}
}
** Because someone asked, here's some code on UnitOfWork.Start()
public static class UnitOfWork
{
public static IUnitOfWork Start();
}
public interface IUnitOfWork : IDisposable
{
bool IsInActiveTransaction { get; }
IUnitOfWorkFactory SessionFactory { get; }
IGenericTransaction BeginTransaction();
IGenericTransaction BeginTransaction(IsolationLevel isolationLevel);
void Flush();
void TransactionalFlush();
void TransactionalFlush(IsolationLevel isolationLevel);
}
Why don't you fail first and avoid all of this?
Example being:
foreach (var item in listOfItems.Select(i => i.Item).Where(item => item != null))
{
if (item.LocationCode == null){
continue;
}
var instance = new SomeClass();
if (pickUpCode != null)
{
instance.PickUpCode = pickUpCode;
}
else
{
instance.PickUpCode = null;
}
// if we reach here, location code is definitley not null, no need for the check
instance.StartLocation = locationCode
repository.SaveSomeClass(instance);
}
Alternatively, you could add the check to you LINQ where clause
foreach (var item in listOfItems.where(item=> item != null && item.LocationCode != null)
Without more code on how UnitofWork.Start works its hard to suggest. But, It's worth trying by implementing IDisposable on SomeClass.