I'm having some serious issues with Fluent Nhibernate in my ASP.NET WebForms app when trying to modify a child object and then saving the parent object.
My solution is currently made of 2 projects :
Core : A class library where all entities & repositories classes are located
Website : The ASP.NET 4.5 WebForms application
Here is my simple mapping for my Employee object:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Username);
Map(x => x.FirstName);
Map(x => x.LastName);
HasMany(x => x.TimeEntries).Inverse().Cascade.All().KeyColumn("Employee_id");
}
}
Here is my my mapping for the TimeEntry object:
public class TimeEntryMap : ClassMap<TimeEntry>
{
public TimeEntryMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateCreated);
Map(x => x.Date);
Map(x => x.Length);
References(x => x.Employee).Column("Employee_id").Not.Nullable();
}
}
As stated in the title, i'm using one session per request in my web app, using this code in Gobal.asax:
public static ISessionFactory SessionFactory = Core.SessionFactoryManager.CreateSessionFactory();
public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
protected Global()
{
BeginRequest += delegate
{
System.Diagnostics.Debug.WriteLine("New Session");
CurrentSession = SessionFactory.OpenSession();
};
EndRequest += delegate
{
if (CurrentSession != null)
CurrentSession.Dispose();
};
}
Also, here is my SessionFactoryManager class:
public class SessionFactoryManager
{
public static ISession CurrentSession;
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Website.Properties.Settings.WebSiteConnString")))
.Mappings(m => m
.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.BuildSessionFactory();
}
public static ISession GetSession()
{
return (ISession)HttpContext.Current.Items["current.session"];
}
}
Here is one of my repository class, the one i use to handle the Employee's object data operations:
public class EmployeeRepository<T> : IRepository<T> where T : Employee
{
private readonly ISession _session;
public EmployeeRepository(ISession session)
{
_session = session;
}
public T GetById(int id)
{
T result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Get<T>(id);
tx.Commit();
}
return result;
}
public IList<T> GetAll()
{
IList<T> result = null;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().ToList();
tx.Commit();
}
return result;
}
public bool Save(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.SaveOrUpdate(item);
tx.Commit();
result = true;
}
return result;
}
public bool Delete(T item)
{
var result = false;
using (ITransaction tx = _session.BeginTransaction())
{
_session.Delete(_session.Load(typeof (T), item.Id));
tx.Commit();
result = true;
}
return result;
}
public int Count()
{
var result = 0;
using (ITransaction tx = _session.BeginTransaction())
{
result = _session.Query<T>().Count();
tx.Commit();
}
return result;
}
}
Now, here is my problem. When i'm trying to insert Employee(s), everything is fine. Updating is also perfect... well, as long as i'm not updating one of the TimeEntry object referenced in the "TimeEntries" property of Employee...
Here is where an exception is raised (in a ASPX file of the web project):
var emp = new Employee(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
emp.Save();
Here is the exception that is raised:
[NonUniqueObjectException: a different object with the same identifier
value was already associated with the session: 1, of entity:
Core.Entities.Employee]
Basically, whenever I try to
Load an employee and
Modify one of the saved TimeEntry, I get that exception.
FYI, I tried replacing the SaveOrUpdate() in the repository for Merge(). It did an excellent job, but when creating an object using Merge(), my object never gets it's Id set.
I also tried creating and flushing the ISession in each function of my repository. It made no sense because as soon as i was trying to load the TimeEntries property of an Employee, an exception was raised, saying the object could not be lazy-loaded as the ISession was closed...
I'm at lost and would appreciate some help. Any suggestion for my repository is also welcome, as i'm quite new to this.
Thanks you guys!
This code
var emp = new Employee(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
emp.Save();
is creating a new Employee object, presumable with an ID of 1 passed through the constructor. You should be loading the Employee from the database, and your Employee object should not allow the ID to be set since you are using an identity column. Also, a new Employee would not have any TimeEntries and the error message clearly points to an Employee instance as the problem.
I'm not a fan of transactions inside repositories and I'm really not a fan of generic repositories. Why is your EmployeeRepository a generic? Shouldn't it be
public class EmployeeRepository : IRepository<Employee>
I think your code should look something like:
var repository = new EmployeeRepository(session);
var emp = repository.GetById(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
repository.Save(emp);
Personally I prefer to work directly with the ISession:
using (var txn = _session.BeginTransaction())
{
var emp = _session.Get<Employee>(1);
foreach (var timeEntry in emp.TimeEntries)
{
timeEntry.Length += 1;
}
txn.Commit();
}
This StackOverflow Answer gives an excellent description of using merge.
But...
I believe that you are facing issues with setting up a correct session pattern for your application.
I you suggest to take a look at session-per-request pattern
where in you create a single NHibernate session object per request. the session is opened when the request is received and closed/flushed on generating a response.
Also make sure that instead of using SessionFactory.OpenSession() to get a session try using SessionFactory.GetCurrentSession() which puts the onus on NHibernate to return you the current correct session.
I hope this pushes you in the right direction.
Related
I have recently completed a course about Microservices and RabbitMQ using ASP.NET Core and noticed that event processing uses dynamic invocation. The code is the following (only relevant parts retained ):
public sealed class RabbitMqBus : IEventBus
{
private readonly IMediator _mediator;
private readonly Dictionary<string, List<Type>> _handlers;
private readonly List<Type> _eventTypes;
}
public void Subscribe<TEvent, THandler>() where TEvent : Event where THandler : IEventHandler<TEvent>
{
string eventName = typeof(TEvent).Name;
var handlerType = typeof(THandler);
if (!_eventTypes.Contains(typeof(TEvent)))
_eventTypes.Add(typeof(TEvent));
if (!_handlers.ContainsKey(eventName))
_handlers.Add(eventName, new List<Type>());
if (_handlers[eventName].Any(s => s == handlerType))
throw new ArgumentException($"Handler type {handlerType.Name} is already registered for {eventName}");
_handlers[eventName].Add(handlerType);
StartBasicConsume<TEvent>();
}
private async Task ConsumerReceived(object sender, BasicDeliverEventArgs e)
{
string eventName = e.RoutingKey;
string message = Encoding.UTF8.GetString(e.Body);
try
{
await ProcessEvent(eventName, message);
}
catch (Exception exception)
{
Console.WriteLine(exception);
throw;
}
}
private async Task ProcessEvent(string eventName, string message)
{
if (_handlers.ContainsKey(eventName))
{
using var scope = _serviceScopeFactory.CreateScope();
var subscriptions = _handlers[eventName];
foreach (var subscription in subscriptions)
{
var handler = scope.ServiceProvider.GetService(subscription);
if (handler == null)
continue;
//TODO: check if this can be made typed and avoid messy dynamic invocation at the end
Type eventType = _eventTypes.SingleOrDefault(t => t.Name == eventName);
object #event = JsonConvert.DeserializeObject(message, eventType);
Type concreteType = typeof(IEventHandler<>).MakeGenericType(eventType);
await (Task) concreteType.GetMethod("Handle").Invoke(handler, new[] {#event});
}
}
}
public interface IEventHandler
{
}
public interface IEventHandler<in TEvent>: IEventHandler
where TEvent: Event
{
Task Handle(TEvent #event);
}
My issue is with the code following the TODO because it relies on reflection and dynamic invocation of a method with a clear name ("Handle").
My first improvement is eliminating the magic string through nameof:
Type eventType = _eventTypes.SingleOrDefault(t => t.Name == eventName);
Event #event = (Event) JsonConvert.DeserializeObject(message, eventType);
Type concreteType = typeof(IEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod(nameof(IEventHandler<Event>.Handle)).Invoke(handler, new object[] { #event });
However, reflection is still used. I understand that this is required because Handle is defined as a generic method (part of a generic interface) and the received event is dynamically constructed.
Is there a way to refactor this code to avoid reflection?
I'm having some trouble with Sqlite in memory.
I have a class that has a CPF field - similar to US' SSN. As a bussiness rule, the CPF must be unique in the system.
So I've decided to make a check on the class that has this field. Now maybe there's code smell here: I check with the ORM if this is a Conflicting CPF.
private CPF cpf;
public virtual CPF CPF
{
get { return cpf; }
set
{
if (this.ormCreated) //Do not check if it is loaded from the DB. Otherwise, it loops, generating a StackOverflow exception
{
cpf = value;
}
else
{
this.setNewCpf(value);
}
}
}
private void setNewCpf(CPF newCpf)
{
if (this.cpf == newCpf)
{
return;
}
if (Helper.Orm.IsConflictingCpf(newCpf))
{
throw new ConflictingCpfException();
}
else
{
cpf = newCpf;
}
}
And here is the implementation, on the ORM Helper class.
bool OrmHelper.IsConflictingCpf(CPF cpf)
{
int? cpfNumber = cpf.NumeroSemDV;
if (cpfNumber.HasValue)
{
var teste = findByCpfNumber<Client>(cpf);
return
(
findByCpfNumber<Client>(cpf) != null ||
findByCpfNumber<Adversary>(cpf) != null
);
}
else
{
//CPFSemDV = Nullable
return false;
}
}
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
using (var session = this.NewSession())
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
session.Close();
}
}
return null;
}
The problem happens in my tests. I'm using FluentNHibernate and In memory SQLite.
protected override FluentConfiguration PersistenceProvider
{
get
{
return Fluently
.Configure()
.Database(
SQLiteConfiguration
.Standard
.InMemory()
.ShowSql()
);
}
}
Here is the failing test.
protected override void Given()
{
base.Given();
var clients = new List<Client>();
Client client1 = new Client("Luiz Angelo Heinzen")
{
Capaz = true,
CPF = new CPF(18743509),
eMail = "lah#furb.br"
};
session.Save(client1);
session.Evict(client1);
}
[Then]
public void Motherfaker()
{
Client fromDb;
var clientsFromDb = session.Query<Client>()
.Where(c => c.eMail == "lah#furb.br");
fromDb = clientsFromDb.FirstOrDefault<Client>();
Assert.AreEqual(fromDb.FullName, "Luiz Angelo Heinzen");
}
The reason it fails? In the beginning it was failing because the table didn't exist. In memory sqlite destroys the schema on each new session. So I changed the code to return the same session on the NewSession(). But now it fails with a NHibernate exception: Session is closed. I've tested and if change the findByCpfNumber from this
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
using (var session = this.NewSession())
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
session.Close();
}
}
return null;
}
to this
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
//using (var session = this.NewSession())
var session = this.NewSession();
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
//session.Close();
this.CloseSession(session);
}
}
this.CloseSession(session);
return null;
}
the error doesn't happen anymore. Obviously, I'd have to implement the CloseSession method. It would close the Session on the Production database and it would do nothing if Sqlite is being used.
But I'd rather configure SQLite in someway that it wouldn't dispose the session. I've read here about release_mode, Pooling and Max Pool atributes. But I can't seem to find it in the FluentNHibernate so can't even test to see if it would work. I have the FluentNHibernate cloned and it seems to set the release_mode set to on_close, but that doesn't help.
I've even tried:
public override ISession NewSession()
{
if (this.session == null)
{
if (sessionFactory == null)
{
CreateSessionFactory();
}
this.session = sessionFactory.OpenSession();
}
if (!session.IsOpen)
{
sessionFactory.OpenSession(session.Connection);
session.Connection.Open();
}
return session;
}
But it keeps telling me that the Session is closed. So, anyone has any suggestions on how to approach this?
Or does this so smelly that's beyond salvation?
I hope this is clear enough. And forgive my mistakes: I'm from Brazil and not a native english speaker.
Thanks,
Luiz Angelo.
i would check for uniqueness when creating CPFs in the system and have an additional Unique constraint in the database to enforce that. Then if you set cascading to none for each reference to CPF (default is none) it is not possible to assigne newly created duplicate CPFs to an Entity and save it without exception, so it can't happen accidently.
I had the same problem. What's happening is that in-memory SQLite will drop the entire schema when the connection is closed. If you create a session that you hold on to for all tests, it will retain the structure for all other sessions.
For code and a fuller explanation, check out this answer: Random error when testing with NHibernate on an in-Memory SQLite db
"How can i use engine in my console application"
I shouldn't use the ITemplate-interface and Transform-Method.
I am using Tridion 2011
Could anyone please suggest me.
You can't. The Engine class is part of the TOM.NET and that API is explicitly reserved for use in:
Template Building Blocks
Event Handlers
For all other cases (such as console applications) you should use the Core Service.
There are many good questions (and articles on other web sites) already:
https://stackoverflow.com/search?q=%5Btridion%5D+core+service
http://www.google.com/#q=tridion+core+service
If you get stuck along the way, show us the relevant code+configuration you have and what error message your get (or at what step you are stuck) and we'll try to help from there.
From a console application you should use the Core Service. I wrote a small example using the Core Service to search for items in the content manager.
Console.WriteLine("FullTextQuery:");
var fullTextQuery = Console.ReadLine();
if (String.IsNullOrWhiteSpace(fullTextQuery) || fullTextQuery.Equals(":q", StringComparison.OrdinalIgnoreCase))
{
break;
}
Console.WriteLine("SearchIn IdRef:");
var searchInIdRef = Console.ReadLine();
var queryData = new SearchQueryData
{
FullTextQuery = fullTextQuery,
SearchIn = new LinkToIdentifiableObjectData
{
IdRef = searchInIdRef
}
};
var results = coreServiceClient.GetSearchResults(queryData);
results.ToList().ForEach(result => Console.WriteLine("{0} ({1})", result.Title, result.Id));
Add a reference to Tridion.ContentManager.CoreService.Client to your Visual Studio Project.
Code of the Core Service Client Provider:
public interface ICoreServiceProvider
{
CoreServiceClient GetCoreServiceClient();
}
public class CoreServiceDefaultProvider : ICoreServiceProvider
{
private CoreServiceClient _client;
public CoreServiceClient GetCoreServiceClient()
{
return _client ?? (_client = new CoreServiceClient());
}
}
And the client itself:
public class CoreServiceClient : IDisposable
{
public SessionAwareCoreServiceClient ProxyClient;
private const string DefaultEndpointName = "netTcp_2011";
public CoreServiceClient(string endPointName)
{
if(string.IsNullOrWhiteSpace(endPointName))
{
throw new ArgumentNullException("endPointName", "EndPointName is not specified.");
}
ProxyClient = new SessionAwareCoreServiceClient(endPointName);
}
public CoreServiceClient() : this(DefaultEndpointName) { }
public string GetApiVersionNumber()
{
return ProxyClient.GetApiVersion();
}
public IdentifiableObjectData[] GetSearchResults(SearchQueryData filter)
{
return ProxyClient.GetSearchResults(filter);
}
public IdentifiableObjectData Read(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
public ApplicationData ReadApplicationData(string subjectId, string applicationId)
{
return ProxyClient.ReadApplicationData(subjectId, applicationId);
}
public void Dispose()
{
if (ProxyClient.State == CommunicationState.Faulted)
{
ProxyClient.Abort();
}
else
{
ProxyClient.Close();
}
}
}
When you want to perform CRUD actions through the core service you can implement the following methods in the client:
public IdentifiableObjectData CreateItem(IdentifiableObjectData data)
{
data = ProxyClient.Create(data, new ReadOptions());
return data;
}
public IdentifiableObjectData UpdateItem(IdentifiableObjectData data)
{
data = ProxyClient.Update(data, new ReadOptions());
return data;
}
public IdentifiableObjectData ReadItem(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
To construct a data object of e.g. a Component you can implement a Component Builder class that implements a create method that does this for you:
public ComponentData Create(string folderUri, string title, string content)
{
var data = new ComponentData()
{
Id = "tcm:0-0-0",
Title = title,
Content = content,
LocationInfo = new LocationInfo()
};
data.LocationInfo.OrganizationalItem = new LinkToOrganizationalItemData
{
IdRef = folderUri
};
using (CoreServiceClient client = provider.GetCoreServiceClient())
{
data = (ComponentData)client.CreateItem(data);
}
return data;
}
Hope this gets you started.
I'm using a Linq query to retrieve entities from an SQL server using the Entity Framework. When I update an entitiy, the EF is caching the result. I suspect this is because the ObjectContext is in a static variable (below). The only way to refresh the data using my code below is to call a method and set _db to null when there might be stale data displayed (Eg: in a GridView). Is there a way to just prevent it from caching, or to add some sort of end request handler to call this method on my data layer instead of needing to detect when there may be stale data displayed?
private static ServiceEntities _db;
protected static ServiceEntitiesDb
{
get
{
if (_db == null)
{
_db = new ServiceEntities();
_db.Contacts.MergeOption = MergeOption.OverwriteChanges; // failed
}
return _db;
}
}
public static IEnumerable<Contact> GetContactsByName(string name) {
var items = Db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
The slightly verbose solution (which I wanted to avoid) is to wrap it in a using block. Ie:
public static IEnumerable<Contact> GetContactsByName(string name) {
var items = Db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
Becomes
public static IEnumerable<Contact> GetContactsByName(string name) {
using (var db = new SomeContext()) {
var items = db.Contacts;
var filteredName = items.Where(i => (i.Name??string.Empty).IndexOf(name) >=0);
return filteredName;
}
}
I am mocking a wrapper to an MSMQ. The wrapper simply allows an object instance to be created that directly calls static methods of the MessageQueue class.
I want to test reading the queue to exhaustion. To do this I would like the mocked wrapper to return some good results and throw an exception on the fourth call to the same method. The method accepts no parameters and returns a standard message object.
Can I set up this series of expectations on the method in Moq?
Yup, this is possible if you don't mind jumping through a few minor hoops. I've done this for one of my projects before. Alright here is the basic technique. I just tested it out in Visual Studio 2008, and this works:
var mockMessage1 = new Mock<IMessage>();
var mockMessage2 = new Mock<IMessage>();
var mockMessage3 = new Mock<IMessage>();
var messageQueue = new Queue<IMessage>(new [] { mockMessage1.Object, mockMessage2.Object, mockMessage3.Object });
var mockMsmqWrapper = new Mock<IMsmqWrapper>();
mockMsmqWrapper.Setup(x => x.GetMessage()).Returns(() => messageQueue.Dequeue()).Callback(() =>
{
if (messageQueue.Count == 0)
mockMsmqWrapper.Setup(x => x.GetMessage()).Throws<MyCustomException>();
});
A few notes:
You don't have to return mocked messages, but it's useful if you want to verify expectations on each message as well to see if certain methods were called or properties were set.
The queue idea is not my own, just a tip I got from a blog post.
The reason why I am throwing an exception of MyCustomException is because the Queue class automatically throws a InvalidOperationException. I wanted to make sure that the mocked MsmqWrapper object throws an exception because of Moq and not because of the queue running out of items.
Here's the complete code that works. Keep in mind that this code is ugly in some places, but I just wanted to show you how this could be tested:
public interface IMsmqWrapper
{
IMessage GetMessage();
}
public class MsmqWrapper : IMsmqWrapper
{
public IMessage GetMessage()
{
throw new NotImplementedException();
}
}
public class Processor
{
private IMsmqWrapper _wrapper;
public int MessagesProcessed { get; set; }
public bool ExceptionThrown { get; set; }
public Processor(IMsmqWrapper msmqWrapper)
{
_wrapper = msmqWrapper;
}
public virtual void ProcessMessages()
{
_wrapper.GetMessage();
MessagesProcessed++;
_wrapper.GetMessage();
MessagesProcessed++;
_wrapper.GetMessage();
MessagesProcessed++;
try
{
_wrapper.GetMessage();
}
catch (MyCustomException)
{
ExceptionThrown = true;
}
}
}
[Test]
public void TestMessageQueueGetsExhausted()
{
var mockMessage1 = new Mock<IMessage>();
var mockMessage2 = new Mock<IMessage>();
var mockMessage3 = new Mock<IMessage>();
var messageQueue = new Queue<IMessage>(new [] { mockMessage1.Object, mockMessage2.Object, mockMessage3.Object });
var mockMsmqWrapper = new Mock<IMsmqWrapper>();
mockMsmqWrapper.Setup(x => x.GetMessage()).Returns(() => messageQueue.Dequeue()).Callback(() =>
{
if (messageQueue.Count == 0)
mockMsmqWrapper.Setup(x => x.GetMessage()).Throws<InvalidProgramException>();
});
var processor = new Processor(mockMsmqWrapper.Object);
processor.ProcessMessages();
Assert.That(processor.MessagesProcessed, Is.EqualTo(3));
Assert.That(processor.ExceptionThrown, Is.EqualTo(true));
}