I am attempting to implement retries on my database connections, which I've gotten working, but it's broken the user-initiated transactions I have as well.
Specifically, I see errors like the following:
The configured execution strategy 'RetryExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.
I would very much like to implement the first workaround from Microsoft, but even the second one would be enough. Despite many attempts from different angles, I've been unable to get things working for transactions. I've tried searching existing questions on StackOverflow (and the rest of the internet, really), but to no avail. Perhaps the solutions I've found elsewhere work for single threads with only one connection at a time, but I'm working on a large project where there are both transactions and non-transactions occurring in no predictable order.
Ultimately, what I need is a way to turn off retries for transactions or a fix to make transactions not crash when retrying is turned on.
What I have for my DbContext:
[DbConfigurationType("MyNamespace.RetryConfiguration","MyNamespace")]
public partial class RepoContext : DbContext {
public RepoContext(string entityConnectionString) : base(entityConnectionString)
{
}
}
My RetryExecutionStrategy:
public class RetryExecutionStrategy : DbExecutionStrategy
{
public RetryExecutionStrategy(int maxRetries, TimeSpan maxDelay)
: base(maxRetries, maxDelay)
{
}
protected override bool ShouldRetryOn(Exception e)
{
return true;
}
}
My RetryConfiguration:
public class RetryConfiguration : DbConfiguration
{
public RetryConfiguration()
{
var executionStrategy = SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new RetryExecutionStrategy(3, new TimeSpan(0, 0, 0, 3));
this.SetExecutionStrategy("Devart.Data.PostgreSql", () => executionStrategy);
}
public static bool SuspendExecutionStrategy
{
get
{
return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
}
set
{
CallContext.LogicalSetData("SuspendExecutionStrategy", value);
}
}
}
Unless I am missing something, I believe I implemented Microsoft's example correctly. From my understanding of their example, EF should make a new instance of RetryConfiguration for every repo call. Otherwise, their example doesn't make any sense as it would only ever create a connection with RetryExecutionStrategy rather than DefaultExecutionStrategy, which is needed for user-initiated transactions. Contrary to that, I've stuck a breakpoint on the constructor of RetryConfiguration and discovered it's only ever instantiated once.
This has led to a lot of headaches and attempts at fulfilling their second workaround like the following:
var res = new RetryExecutionStrategy(0, new TimeSpan(0,0,0));
RetryConfiguration.SuspendExecutionStrategy = true;
res.Execute(() =>
{
using (var trans = _repoFactory.GetTransactableRepository())
{
trans.BeginTransaction();
var entry = trans.GetAll<MyTable>().First();
entry.Alive = true;
trans.Save();
trans.Commit();
}
});
RetryConfiguration.SuspendExecutionStrategy = false;
I even tried manually calling DefaultExecutionStrategy.Execute().
var des = new System.Data.Entity.Infrastructure.DefaultExecutionStrategy();
des.Execute(() =>
{
using (var trans = _repoFactory.GetTransactableRepository())
{
trans.BeginTransaction();
var entry = trans.GetAll<MyTable>().First();
entry.Alive = true;
trans.Save();
trans.Commit();
}
});
Even in that situation I get an exception saying that RetryConfiguration does not allow user-initiated transactions.
For what it's worth, I tried adding RetryConfiguration.SuspendExecutionStrategy = true/false to our TransactableRepository class's BeginTransaction(), Commit(), and Rollback() functions. The class itself is just a wrapper for the connection. I was unsurprised when it didn't work, given that Microsoft's example only ever shows it being read from on RetryConfiguration's constructor, but I figured it was worth a try since I'm not terribly familiar with CallContext.
Related
I am currently using the Change Notifications in Active Directory Domain Services in .NET as described in this blog. This will return all events that happen on an selected object (or in the subtree of that object). I now want to filter the list of events for creation and deletion (and maybe undeletion) events.
I would like to tell the ChangeNotifier class to only observe create-/delete-/undelete-events. The other solution is to receive all events and filter them on my side. I know that in case of the deletion of an object, the atribute list that is returned will contain the attribute isDeleted with the value True. But is there a way to see if the event represents the creation of an object? In my tests the value for usnchanged is always usncreated+1 in case of userobjects and both are equal for OUs, but can this be assured in high-frequency ADs? It is also possible to compare the changed and modified timestamp. And how can I tell if an object has been undeleted?
Just for the record, here is the main part of the code from the blog:
public class ChangeNotifier : IDisposable
{
static void Main(string[] args)
{
using (LdapConnection connect = CreateConnection("localhost"))
{
using (ChangeNotifier notifier = new ChangeNotifier(connect))
{
//register some objects for notifications (limit 5)
notifier.Register("dc=dunnry,dc=net", SearchScope.OneLevel);
notifier.Register("cn=testuser1,ou=users,dc=dunnry,dc=net", SearchScope.Base);
notifier.ObjectChanged += new EventHandler<ObjectChangedEventArgs>(notifier_ObjectChanged);
Console.WriteLine("Waiting for changes...");
Console.WriteLine();
Console.ReadLine();
}
}
}
static void notifier_ObjectChanged(object sender, ObjectChangedEventArgs e)
{
Console.WriteLine(e.Result.DistinguishedName);
foreach (string attrib in e.Result.Attributes.AttributeNames)
{
foreach (var item in e.Result.Attributes[attrib].GetValues(typeof(string)))
{
Console.WriteLine("\t{0}: {1}", attrib, item);
}
}
Console.WriteLine();
Console.WriteLine("====================");
Console.WriteLine();
}
LdapConnection _connection;
HashSet<IAsyncResult> _results = new HashSet<IAsyncResult>();
public ChangeNotifier(LdapConnection connection)
{
_connection = connection;
_connection.AutoBind = true;
}
public void Register(string dn, SearchScope scope)
{
SearchRequest request = new SearchRequest(
dn, //root the search here
"(objectClass=*)", //very inclusive
scope, //any scope works
null //we are interested in all attributes
);
//register our search
request.Controls.Add(new DirectoryNotificationControl());
//we will send this async and register our callback
//note how we would like to have partial results
IAsyncResult result = _connection.BeginSendRequest(
request,
TimeSpan.FromDays(1), //set timeout to a day...
PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
Notify,
request
);
//store the hash for disposal later
_results.Add(result);
}
private void Notify(IAsyncResult result)
{
//since our search is long running, we don't want to use EndSendRequest
PartialResultsCollection prc = _connection.GetPartialResults(result);
foreach (SearchResultEntry entry in prc)
{
OnObjectChanged(new ObjectChangedEventArgs(entry));
}
}
private void OnObjectChanged(ObjectChangedEventArgs args)
{
if (ObjectChanged != null)
{
ObjectChanged(this, args);
}
}
public event EventHandler<ObjectChangedEventArgs> ObjectChanged;
#region IDisposable Members
public void Dispose()
{
foreach (var result in _results)
{
//end each async search
_connection.Abort(result);
}
}
#endregion
}
public class ObjectChangedEventArgs : EventArgs
{
public ObjectChangedEventArgs(SearchResultEntry entry)
{
Result = entry;
}
public SearchResultEntry Result { get; set; }
}
I participated in a design review about five years back on a project that started out using AD change notification. Very similar questions to yours were asked. I can share what I remember, and don't think things have change much since then. We ended up switching to DirSync.
It didn't seem possible to get just creates & deletes from AD change notifications. We found change notification resulted enough events monitoring a large directory that notification processing could bottleneck and fall behind. This API is not designed for scale, but as I recall the performance/latency were not the primary reason we switched.
Yes, the usn relationship for new objects generally holds, although I think there are multi-dc scenarios where you can get usncreated == usnchanged for a new user, but we didn't test that extensively, because...
The important thing for us was that change notification only gives you reliable object creation detection under the unrealistic assumption that your machine is up 100% of the time! In production systems there are always some case where you need to reboot and catch up or re-synchronize, and we switched to DirSync because it has a robust way to handle those scenarios.
In our case it could block email to a new user for an indeterminate time if an object create were missed. That obviously wouldn't be good, we needed to be sure. For AD change notifications, getting that resync right that would have some more work and hard to test. But for DirSync, its more natural, and there's a fast-path resume mechanism that usually avoids resync. For safety I think we triggered a full re-synchronize every day.
DirSync is not as real-time as change notification, but its possible to get ~30-second average latency by issuing the DirSync query once a minute.
I'm having difficulty making a change to an entity object through a new context. I've had this work plenty of times before, but in this instance I'm getting the old "The ObjectContext instance has been disposed" exception.
Here's my quick edit/save code:
private void SaveChanges()
{
using (var context = new Manticore.ManticoreContext(Global.ManticoreClient))
{
**context.AttachTo("Users", Global.LoggedInUser);**
Global.LoggedInUser.FirstName = this.FirstNameText.Text;
Global.LoggedInUser.LastName = this.LastNameText.Text;
Global.LoggedInUser.Email = this.EmailText.Text;
context.SaveChanges();
}
}
The Global.LoggedInUser property (which is instantiated):
public static Manticore.User LoggedInUser
{
get
{
return HttpContext.Current.Session["LoggedInUser"] as Manticore.User;
}
set
{
HttpContext.Current.Session["LoggedInUser"] = value;
}
}
And the kicker is here's a quick unit test which works (no assert right now, but no exception being thrown):
private User _testUser;
private TestInstanceBucket _testBucket;
[TestInitialize]
public void TestInitialize()
{
using (var context = new Manticore.ManticoreContext())
{
this._testBucket = new TestInstanceBucket(context);
this._testUser = this._testBucket.TestUser;
context.AddObject("Users", this._testUser);
context.SaveChanges();
}
}
[TestMethod]
public void User_ShouldBeAbleToChangeDetails()
{
using (var context = new ManticoreContext())
{
context.AttachTo("Users", this._testUser);
this._testUser.FirstName = "New";
context.SaveChanges();
}
}
Like I say, I've done code like this before and it's been fine. Have I been lucking out, or could storing the entity in the session be causing problems?
Update
I've moved the code from global to a pagebase class which the page using SaveChanges() inherits. Same problem so it rules our static classes/methods and storing the entity in the session somehow causing problems.
Update
So, after several hours of banging my head against a wall, I have a fix that's fairly simple if annoying. After the initial fetch of the user I now call
context.Detach(user);
I can only assume it's something to do with how fast the context is being disposed with garbage collection in ASP.NET compared to in the test environment.
In my EF experience this issue always seems to occur when the entity is still linked to the old context. I believe it is safest to always just re-load the entity using the new context (query on PK). I know it is not the most efficient but it avoids this problem.
I took a look at this page before answering, if you haven't tried some of its suggestions you might give them a go: EF Add/Attach.
I am new to developing in .NET and C#, but have been a long-time developer, working with C, C++, Java, PHP, etc.
I have an MVC3 extension class for my data models that refers to the database. It is set as "private static" in the class, but I think that it is not keeping up with database changes. In other words, when I change data in the controllers, those changes aren't "noticed" in the db because it is static. Currently, I am creating and disposing of the variable for each use, to compensate.
My questions are:
Am I correct that a static db variable could behave that way?
Is it necessary to dispose of the dynamic variable in the static class, or will garbage collection still take care of it automatically?
Here is a relevant snippet of the class:
namespace PBA.Models {
using System;
using System.Text.RegularExpressions;
using PBA.Models;
using PBA.Controllers;
public static class Extensions {
private static PbaDbEntities db = null;
public static PbaDbEntities GetDb() {
// TODO: find out about static memory/disposal, etc.
//
if (db != null) {
db.Dispose();
}
db = new PbaDbEntities();
return db;
}
public static string GetCheckpointState(this Activity activity, long memberProjectId) {
GetDb(); // TODO: Do I need to do this each time, or will a one-time setting work?
string state = CheckpointController.CHECKPOINT_STATUS_NOT_STARTED;
try {
var sub = db.ActivitySubmissions.
Where(s => s.activityId == activity.activityId).
Where(s => s.memberProjectId == memberProjectId).
OrderByDescending(s => s.submitted).
First();
if (sub != null) {
state = sub.checkpointStatusId;
}
}
catch (Exception e) {
// omitted for brevity
}
return state;
}
}
}
Your code will fail horribly in production.
DataContexts are not thread-safe; you must not share a context between requests.
Never put mutable objects in static fields in multi-threaded applications.
Ignoring exceptions that way is a terrible idea, if you don't want to handle exceptions just don't try/catch, or catch & rethrow. Think about it like this, after you've buried the exception, your program is in an invalid state, b/c something you have no control over error'd out. Now, b/c you've buried the exception, your program can continue to operate but it's in a bad state.
If your code makes it to production, 3.5 yrs from now some jr. programmer is going to get involved in some middle of the night firestorm because all of a sudden the website is broken, even though it used to work. It will be completely impossible to track down where the exception is happening so, this poor guy is going to spend 48 straight hours adding logging code all over the place to track down the problem. He will find that some DBA somewhere decided to rename the column MemberProjectId to MemberProjectIdentifier, which caused your linq to blow up.
Think of the children, handle exceptions, don't bury them.
btw - yes, i have been that guy that has to figure out these types of mistakes.
It seems like you need to read about mvc3 and entity framework before writing coding and asking in here for help on something that's coded full of bad practices.
Answering your questions:
1- no
2- makes no sense as the answer to 1
Do it right, here are some useful documentation: http://msdn.microsoft.com/en-us/library/ie/gg416514(v=vs.98).aspx
EDIT: Adding some explicit fix
You could access your dbcontext from an static class, something like this:
var context = DbProvider.CurrentDb;
The idea is to access your db from here always: from your extension methods and from your controller actions.
Then, the implementation of the DbProvider.CurrentDb will be something like this:
public static classDbProvider {
public static void Initialize(){
HttpContext.Current.ApplicationInstance.BeginRequest += CreateDb;
HttpConetxt.Current.ApplicationInstance.EndRequest += DisposeDb;
}
private static void CreateDb(object sender, EventArgs e) {
HttpContext.Items.Add("CurrentDb", new PbaDbEntities(););
}
private static void DisposeDb(object sender, EventArgs e)
{
Current.Dispose();
HttpContext.Items.Remove("CurrentDb");
}
public static PbaDbEntities CurrentDb{
get {
return (PbaDbEntities)HttpContext.Current.Items["CurrentDb"];
}
}
}
As you can see, it will create a new Db per each request and it will be available ONLY in that request. In that way, your db will be disposed at the end of each request. This pattern is called Open-Session-in-View.
Finally, you need to initialize the DbProvider calling the method
Initialize() in your Global.asax file, in the event Application_start.
Hope it helps.
I don't have any idea of the context here-- if db is simply a connection-like object or not, but it appears you are throwing away and recreating whatever it is unnecessarily.
Best to create a property (for whatever your doing) so to be consistent.
private static Thing _thing;
private static Thing thing{
get{
if(_thing==null){
_thing=new Thing();
}
return _thing;
}
}
I'm building a T4 template that will help people construct Azure queues in a consistent and simple manner. I'd like to make this self-documenting, and somewhat consistent.
First I made the queue name at the top of the file, the queue names have to be in lowercase so I added ToLower()
The public constructor uses the built-in StorageClient API's to access the connection strings. I've seen many different approaches to this, and would like to get something that works in almost all situations. (ideas? do share)
I dislike the unneeded HTTP requests to check if the queues have been created so I made is a static bool . I didn't implement a Lock(monitorObject) since I don't think one is needed.
Instead of using a string and parsing it with commas (like most MSDN documentation) I'm serializing the object when passing it into the queue.
For further optimization I'm using a JSON serializer extension method to get the most out of the 8k limit. Not sure if an encoding will help optimize this any more
Added retry logic to handle certain scenarios that occur with the queue (see html link)
Q: Is "DataContext" appropriate name for this class?
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
What additional changes do you think I should make?
public class AgentQueueDataContext
{
// Queue names must always be in lowercase
// Is named like a const, but isn't one because .ToLower won't compile...
static string AGENT_QUEUE_ACTION_NAME = "AgentQueueActions".ToLower();
static bool QueuesWereCreated { get; set; }
DataModel.SecretDataSource secDataSource = null;
CloudStorageAccount cloudStorageAccount = null;
CloudQueueClient cloudQueueClient = null;
CloudQueue queueAgentQueueActions = null;
static AgentQueueDataContext()
{
QueuesWereCreated = false;
}
public AgentQueueDataContext() : this(false)
{
}
public AgentQueueDataContext(bool CreateQueues)
{
// This pattern of setting up queues is from:
// ttp://convective.wordpress.com/2009/11/15/queues-azure-storage-client-v1-0/
//
this.cloudStorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
this.cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient();
this.secDataSource = new DataModel.SecretDataSource();
queueAgentQueueActions = cloudQueueClient.GetQueueReference(AGENT_QUEUE_ACTION_NAME);
if (QueuesWereCreated == false || CreateQueues)
{
queueAgentQueueActions.CreateIfNotExist();
QueuesWereCreated = true;
}
}
// This is the method that will be spawned using ThreadStart
public void CheckQueue()
{
while (true)
{
try
{
CloudQueueMessage msg = queueAgentQueueActions.GetMessage();
bool DoRetryDelayLogic = false;
if (msg != null)
{
// Deserialize using JSON (allows more data to be stored)
AgentQueueEntry actionableMessage = msg.AsString.FromJSONString<AgentQueueEntry>();
switch (actionableMessage.ActionType)
{
case AgentQueueActionEnum.EnrollNew:
{
// Add to
break;
}
case AgentQueueActionEnum.LinkToSite:
{
// Link within Agent itself
// Link within Site
break;
}
case AgentQueueActionEnum.DisableKey:
{
// Disable key in site
// Disable key in AgentTable (update modification time)
break;
}
default:
{
break;
}
}
//
// Only delete the message if the requested agent has been missing for
// at least 10 minutes
//
if (DoRetryDelayLogic)
{
if (msg.InsertionTime != null)
if (msg.InsertionTime < DateTime.UtcNow + new TimeSpan(0, 10, 10))
continue;
// ToDo: Log error: AgentID xxx has not been found in table for xxx minutes.
// It is likely the result of a the registratoin host crashing.
// Data is still consistent. Deleting queued message.
}
//
// If execution made it to this point, then we are either fully processed, or
// there is sufficent reason to discard the message.
//
try
{
queueAgentQueueActions.DeleteMessage(msg);
}
catch (StorageClientException ex)
{
// As of July 2010, this is the best way to detect this class of exception
// Description: ttp://blog.smarx.com/posts/deleting-windows-azure-queue-messages-handling-exceptions
if (ex.ExtendedErrorInformation.ErrorCode == "MessageNotFound")
{
// pop receipt must be invalid
// ignore or log (so we can tune the visibility timeout)
}
else
{
// not the error we were expecting
throw;
}
}
}
else
{
// allow control to fall to the bottom, where the sleep timer is...
}
}
catch (Exception e)
{
// Justification: Thread must not fail.
//Todo: Log this exception
// allow control to fall to the bottom, where the sleep timer is...
// Rationale: not doing so may cause queue thrashing on a specific corrupt entry
}
// todo: Thread.Sleep() is bad
// Replace with something better...
Thread.Sleep(9000);
}
Q: Is "DataContext" appropriate name for this class?
In .NET we have a lot of DataContext classes, so in the sense that you want names to appropriately communicate what the class does, I think XyzQueueDataContext properly communicates what the class does - although you can't query from it.
If you want to stay more aligned to accepted pattern languages, Patterns of Enterprise Application Architecture calls any class that encapsulates access to an external system for a Gateway, while more specifically you may want to use the term Channel in the language of Enterprise Integration Patterns - that's what I would do.
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
Well, it certainly tightly couples the queue name to the class. This means that if you later decide that you want to decouple those, you can't.
As a general comment I think this class might benefit from trying to do less. Using the queue is not the same thing as managing it, so instead of having all of that queue management code there, I'd suggest injecting a CloudQueue into the instance. Here's how I implement my AzureChannel constructor:
private readonly CloudQueue queue;
public AzureChannel(CloudQueue queue)
{
if (queue == null)
{
throw new ArgumentNullException("queue");
}
this.queue = queue;
}
This better fits the Single Responsibility Principle and you can now implement queue management in its own (reusable) class.
I'm learning some Fluent NHibernate and I've run across the semi-awesome PersistenceSpecification class.
I've set it up in a unit test to verify my mappings and it works great. However, it leaves the record in the database when done. I tried throwing it in a transaction so I can rollback the changes but I get an error:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'AdoTransaction'..
Without a transaction I have to figure out the ID's of the record, retrieve them and delete them and that doesn't seem very elegant.
Any thoughts?
EDIT:
Here is the code snippet:
var factory = GetSessionFactory();
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
new PersistenceSpecification<TimePeriod>(session)
.CheckProperty(x => x.EndDate, DateTime.Today)
.VerifyTheMappings();
transaction.Rollback();
}
Try setting the IsolationLevel on the transaction. This snippet worked for me:
using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
new PersistenceSpecification<Event>(_session)
.CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1))
.VerifyTheMappings();
trans.Rollback();
}
The PersistenceSpecification is usually used with an in-memory database like SQLite, that's why it doesn't roll anything back. I believe there's a constructor overload that takes an ISession instance, have you tried getting a transaction from there then rolling that back after?
I think the issue here is VerifyTheMappings() calls TransactionSave() which does a tx.Commit() to the database. As James indicated, this technique seems to work great for throw away in-memory testing techniques. This would not work in the case of testing mappings against a legacy database.
Setting IsolationLevel.ReadUncommitted will work, but only incidentally, since all it is doing is telling the session that it can read without needing a new transaction (a dirty read, in DBMS parlance) - so Session.Transaction.Commit () doesn't have to commit a database transaction before the verification reads. This also means that it is not necessarily testing what you think it is testing! (I also think this has probably-questionable support amongst non-MS SQL databases). The answer from leebrandt works because of the explicit rollback, not the isolation level (nb. At the time of the answer this helped more than it does now, see note below).
The good news is that the correct way to do this is to just rollback the transaction manually. Session.Transaction is automatically replaced whenever the transaction is committed, so you'll need to hold a reference to it, and you'll have to open one explicitly anyways, since TransactionalSave () checks if the current transaction is active and creates (and disposes!) its own if not. I typically test all of my mappings in the same fixture, where I also verify the factory creation and a few other infrastructural persistence things, so I like the following pattern for this to keep the plumbing down:
class TestFixture {
static ISessionFactory factory = CreateMyFactorySomehowHere();
ISession session;
ITransaction tx;
public void Setup ()
{
session = factory.OpenSession ();
tx = session.BeginTransaction ();
}
public void Cleanup ()
{
tx.Rollback ();
tx.Dispose ();
session.Close ();
}
public void TestAMappingForSomething ()
{
var spec = new PersistenceSpecification<Something> (session);
spec.VerifyTheMappings ();
}
}
Obviously, insert your own test-framework-specific terminology and attributes/annotations wherever, but you get the idea.
I've just now noticed how old this question is: this behavior was fixed in this commit in July 09, to handle existing transactions nicely so that the above works! Clearly this is what you were doing originally anyways.
i think that it's very important to do this testing with your real db, to see that his tables definition r ok, so i'v developed a very simple class that perform a crud test on a mapped entity and roll back at the end;
internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable
{
public T EntityToTest { get; set; }
public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; }
public GenericMappingTesterWithRealDB()
{
Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null);
}
public void RunTest()
{
using (ISession session = SessionFactoryProvider.NewSession)
using (ITransaction transaction = session.BeginTransaction())
{
try
{
session.Save(EntityToTest);
var item = session.Get<T>(EntityToTest.ID);
Assert.IsNotNull(item);
if (PerformEntityManipulationBeforeUpdate != null)
{
PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest);
}
session.Update(EntityToTest);
session.Delete(EntityToTest);
session.Save(EntityToTest);
}
catch (Exception e)
{
Assert.Fail(e.Message, e.StackTrace);
}
finally
{
transaction.Rollback();
}
}
}
}
IIdentifiable in my project is the most basic interface of my entities
the class is using the nunit.framework but u can do it with every testing framework u want
sessionfactoryprovider needs to supply the isession obj
here is a sample of use
/// <summary>
/// Testing the mapping of our entities.
/// there must be a server connection for this kind of test.
/// </summary>
[TestFixture]
internal class someMappingTest
{
[Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")]
[Timeout(20000)]
public void checkthatMappingWorks()
{
// creatw the new entity
TestedType testOn = new TestedType();
// set the initialization values
testOn.Name = "TestProfileExecution";
// create the test object
new GenericMappingTesterWithRealDB<TestedType>
{
// assign an entity
EntityToTest = testOn,
// assign new values for update check
PerformEntityManipulationBeforeUpdate =
delegate(TestedType testedTypeBeingTested)
{
return testedTypeBeingTested.Name = "Updateing Test";
}
}.
// call run test to perform the mapping test.
RunTest();
}
}