I have a class on which I'd like to define a unique key using Fluent NHibernate. I think I've done this correctly, but my unit tes,t in which I save two objects with the same value for the field in question, saves both items without error. Here's what I've got:
Mapping override:
public class ItemOverride : IAutoMappingOverride<Item>
{
#region IAutoMappingOverride<Item> Members
public void Override(AutoMapping<Item> mapping)
{
mapping.Map(t => t.Prompt).UniqueKey("UIX_Item_Prompt").Not.Nullable();
}
#endregion
}
Unit Test
[Test]
public void CannotSaveDuplicateItemPrompt()
{
ServiceLocatorInitializer.Init();
IItemManagementService itemManagementService = new ItemManagementService(repository);
Item first = ItemInstanceFactory.CreateValidTransientItem();
first.Prompt = "Duplicate";
itemManagementService.SaveOrUpdate(first);
Item second = ItemInstanceFactory.CreateValidTransientItem();
second.Prompt = "Duplicate";
ActionConfirmation confirm = itemManagementService.SaveOrUpdate(second);
Assert.IsFalse(confirm.WasSuccessful);
Assert.AreEqual(confirm.Message, "");
}
Okay, it works if I wrap the two saves in a transaction. Here's the new Unit Test:
[Test]
[ExpectedException(typeof(GenericADOException))]
public void CannotSaveDuplicateItemPrompt()
{
ServiceLocatorInitializer.Init();
IItemManagementService itemManagementService = new ItemManagementService(repository);
NHibernateSession.Current.BeginTransaction();
Item first = ItemInstanceFactory.CreateValidTransientItem();
first.Prompt = "Duplicate";
itemManagementService.SaveOrUpdate(first);
Item second = ItemInstanceFactory.CreateValidTransientItem();
second.Prompt = "Duplicate";
ActionConfirmation confirm = itemManagementService.SaveOrUpdate(second);
NHibernateSession.Current.Transaction.Commit();
}
Related
I have made a small class, which inherits from DataGrid and takes in classes that derive from a specific interface:
public class RecordDataGrid<T> : DataGrid where T : IRecord
{
public RecordDataGrid()
{
this.AutoGenerateColumns = false;
this.CanUserAddRows = false;
this.CanUserDeleteRows = false;
this.CanUserResizeRows = false;
this.IsReadOnly = true;
this.SelectionMode = DataGridSelectionMode.Single;
this.Margin = new System.Windows.Thickness(0, 10, 0, 0);
var propertyInfos = typeof(T).GetProperties();
var list = new Dictionary<PropertyInfo, DataGridColumnAttribute>();
foreach (var propertyInfo in propertyInfos)
{
var customAttributes = propertyInfo.GetCustomAttributes(true);
foreach (var customAttr in customAttributes)
{
if (customAttr != null && customAttr is DataGridColumnAttribute)
{
list.Add(propertyInfo, (DataGridColumnAttribute)customAttr);
}
}
}
var ordered = (from entry in list orderby entry.Value.OrderIndex ascending select entry).ToDictionary(e => e.Key, e => e.Value);
foreach (var kvp in ordered)
{
var propertyInfo = kvp.Key;
var dgcAttr = kvp.Value;
var column = new DataGridTextColumn();
column.Header = dgcAttr.DisplayName;
column.Binding = new Binding(propertyInfo.Name);
column.Binding.StringFormat = dgcAttr.StringFormat ?? null;
column.Width = dgcAttr.ColumnWidthType == DataGridColumnAttribute.ColumnWidthTypes.Auto ? new DataGridLength(10, DataGridLengthUnitType.Auto) : new DataGridLength(10, DataGridLengthUnitType.Star);
this.Columns.Add(column);
}
}
}
It is very rough at the moment, just testing a few things out. The goal is to make my life easier by letting the DataGrid fill the Columns by itself, based on a custom Attribute:
public class DataGridColumnAttribute : Attribute
{
public string DisplayName { get; private set; }
public string StringFormat { get; private set; }
public ColumnWidthTypes ColumnWidthType { get; private set; }
public int OrderIndex { get; private set; }
public DataGridColumnAttribute(string displayName, int orderIndex, string stringFormat = null, ColumnWidthTypes columnWidthType = ColumnWidthTypes.Auto)
{
DisplayName = displayName;
StringFormat = stringFormat;
OrderIndex = OrderIndex;
ColumnWidthType = columnWidthType;
}
public enum ColumnWidthTypes
{
Auto,
Fill
}
}
Later on, as far as I am concerned, I should be able to use it in xaml like this:
Namespaces:
xmlns:model="clr-namespace:NickX.KswErp.Model.Classes;assembly=NickX.KswErp.Model"
xmlns:ctrl="clr-namespace:NickX.KswErp.ClientApplication.UI.Controls"
Control:
<ctrl:RecordDataGrid x:Name="_gridTransactions" x:TypeArguments="model:TransactionRecord" />
But I get following compilation error:
Only a master tag can specify the "x: TypeArguments" attribute.
(Roughly translated by google translation)
Maybe my approach is completely wrong tho. Should I do it completle in code behind. Or are there better approaches? Please let me know!
Conveniently I just found a thread in a german forum, which answeres my exact question. So people questioning the same in the future:
It is not possible. Easiest thing to do at this point is making a specific class for each model, which again derives from your generic class.
In my case:
public class TransactionDataGrid : RecordDataGrid<TransactionRecord>
{
}
Doesen't seem like a nice solution to me, and probably isn't the best way to do it. But it works.
I try to use foreign collections in ORMLite. However, I dont know how to convert it into list. I try to do something like this :
public class Car implements Serializable {
#DatabaseField(columnName = "carId" , generatedId = true, id=true)
private int id;
#DatabaseField(columnName = "carNumber")
private String mNumber;
#DatabaseField(columnName = "carName")
private String mName;
#ForeignCollectionField(eager = true,columnName = "carParts")
private Collection<Part> mParts;
ArrayList<Part> parts = new ArrayList<>(mParts);
public ArrayList<Part> getParts() {
return parts;
}
public void setParts(ArrayList<Part> parts) {
this.parts = parts;
}
but when I try to use it I get exception :
java.lang.NullPointerException: collection == null
at this line :
ArrayList<Part> parts = new ArrayList<>(mParts);
please, help.
The reason is simple - you have to wait until mParts will be initialized by ORMLite library, then you can create ArrayList from it.
public ArrayList<Part> getParts() {
return new ArrayList<>( mParts );
}
All of my entities (that are mapped to a database table) inherit from an entity class with a dynamic component on it called Attributes e.g.:
public abstract class Entity<T> {
public virtual T Id { get; set; }
private IDictionary _attributes;
public virtual IDictionary Attributes {
get { return _attributes ?? (_attributes = new Hashtable()); }
set { _attributes = value; }
}
}
The Attributes collection allows me to add extra fields to each entity without directly changing the entity itself. This allows me to make my application more modular.
For example say I have the following entity:
public class User : Entity<int> {
public virtual string Name { get; set; }
}
Now say I have a Forum module which needs a NumPosts property against the User. I would add the field against the Users table in the database. This field is non nullable and has a default value of 0. I then map the field using the dynamic component against the User entity.
However when I try inserting the user by saying:
session.Save(new User() { Name = "Test" });
It throws an error as it's expecting me to set a value for NumPosts and the generated SQL would be something like:
INSERT INTO Users (Name, NumPosts) VALUES ('Test', NULL)
However NumPosts does not allow nulls and hence the error. Ideally I'd like it to say the following if the Attributes collection does not contain an entry for NumPosts:
INSERT INTO Users (Name) VALUES ('Test')
An alternative is to say the following which would work fine:
session.Save(new User() { Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } } });
The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this.
For reference here's a bare bones version of session factory method which maps the NumPosts field:
return Fluently.Configure()
...
.ExposeConfiguration(c => {
// Get the persistent class
var persistentClass = c.GetClassMapping("User");
// Create the attributes component
var component = new Component(persistentClass);
// Create a simple value
var simpleValue = new SimpleValue(persistentClass.Table);
// Set the type name
simpleValue.TypeName = "Int32";
// Create a new db column specification
var column = new Column("NumPosts");
column.Value = simpleValue;
column.Length = 10;
column.IsNullable = false;
column.DefaultValue = "0";
// Add the column to the value
simpleValue.AddColumn(column);
// Ad the value to the component
component.AddProperty(new Property() { Name = column.Name, Value = simpleValue });
// Add the component property
persistentClass.AddProperty(new Property() { Name = "Attributes", Value = component });
})
.BuildConfiguration();
I'd appreciate if someone could let me know if this is possible. Thanks
You know how to make it working as described above:
... An alternative is to say the following which would work fine:
session.Save(new User()
{
Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } }
});
... The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this...
In case, that the biggest issue is the explicit Attributes initialization ("...I don't want the modules to have a dependency...") we can use:
12.2. Event system
So, with Listener like this:
[Serializable]
public class MyPersistListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(SaveOrUpdateEvent #event)
{
var entity = #event.Entity as Entity<int>; // some interface IHaveAttributes
if (entity == null) // would be more appropriate
{
return;
}
var numPosts = entity.Attributes["NumPosts"] as int?;
if (numPosts.HasValue)
{
return;
}
entity.Attributes["NumPosts"] = 0;
}
}
Based on this doc snippet:
Configuration cfg = new Configuration();
ILoadEventListener[] stack = new ILoadEventListener[] { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners.LoadEventListeners = stack;
This should be the init in our case:
.ExposeConfiguration(c => {
var stack = new ISaveOrUpdateEventListener [] { new MyPersistListener() };
c.EventListeners.SaveEventListeners= stack;
We are using fluentnhibernate with automapping and we have a naming convention that all columns that are foreign keys, there column name will end with "Key". So we have a convention that looks like this:
public class ForeignKeyColumnNameConvention : IReferenceConvention
{
public void Apply ( IManyToOneInstance instance )
{
// name the key field
string propertyName = instance.Property.Name;
instance.Column ( propertyName + "Key" );
}
}
This works great until we created a component in which one of its values is a foreign key. By renaming the column here it overrides the default name given to the component column which includes the ComponentPrefix which is defined in the AutomappingConfiguration. Is there a way for me to get the ComponentPrefix in this convention? or is there some other way for me to get the column name for components with a property that is a foreign key to end in the word "Key"?
After a lot of fiddling and trial & error (thus being tempted to use your solution with Reflection) I came up with the following:
This method depends on the order of the execution of the conventions. This convention-order happens via a strict hierarchy. In this example, at first, the convention of the component (IDynamicComponentConvention) is being handled and after that the conventions of the inner properties are being handled such as the References mapping (IReferenceConvention).
The strict order is where we make our strike:
We assemble the correct name of the column in the call to Apply(IDynamicComponentConvention instance), put it on the queue. Note that a Queue<T> is used which is a FIFO (first-in-first-out) collection type thus it keeps the order correctly.
Almost immediately after that, Apply(IManyToOneInstanceinstance) is called. We check if there is anything in the queue. If there is, we take it out of the queue and set it as column name. Note that you should not use Peek() instead of Dequeue() as it does not remove the object from the queue.
The code is as follows:
public sealed class CustomNamingConvention : IDynamicComponentConvention, IReferenceConvention {
private static Queue<string> ColumnNames = new Queue<string>();
public void Apply(IDynamicComponentInstance instance) {
foreach (var referenceInspector in instance.References) {
// All the information we need is right here
// But only to inspect, no editing yet :(
// Don't worry, just assemble the name and enqueue it
var name = string.Format("{0}_{1}",
instance.Name,
referenceInspector.Columns.Single().Name);
ColumnNames.Enqueue(name);
}
}
public void Apply(IManyToOneInstance instance) {
if (!ColumnNames.Any())
// Nothing in the queue? Just return then (^_^)
return;
// Set the retrieved string as the column name
var columnName = ColumnNames.Dequeue();
instance.Column(columnName);
// Pick a beer and celebrate the correct naming!
}
}
I Have figured out a way to do this using reflection to get to the underlying mapping of the IManyToOneInspector exposed by the IComponentInstance but was hoping there was a better way to do this?
Here is some example code of how I achieved this:
#region IConvention<IComponentInspector, IComponentInstance> Members
public void Apply(IComponentInstance instance)
{
foreach (var manyToOneInspector in instance.References)
{
var referenceName = string.Format("{0}_{1}_{2}{3}", instance.EntityType.Name, manyToOneInspector.Property.PropertyType.Name, _autoMappingConfiguration.GetComponentColumnPrefix(instance.Property), manyToOneInspector.Property.Name);
if(manyToOneInspector.Property.PropertyType.IsSubclassOf(typeof(LookupBase)))
{
referenceName += "Lkp";
}
manyToOneInspector.Index ( string.Format ( "{0}_FK_IDX", referenceName ) );
}
}
#endregion
public static class ManyToOneInspectorExtensions
{
public static ManyToOneMapping GetMapping(this IManyToOneInspector manyToOneInspector)
{
var fieldInfo = manyToOneInspector.GetType ().GetField( "mapping", BindingFlags.NonPublic | BindingFlags.Instance );
if (fieldInfo != null)
{
var manyToOneMapping = fieldInfo.GetValue( manyToOneInspector ) as ManyToOneMapping;
return manyToOneMapping;
}
return null;
}
public static void Index(this IManyToOneInspector manyToOneInspector, string indexName)
{
var mapping = manyToOneInspector.GetMapping ();
mapping.Index ( indexName );
}
public static void Column(this IManyToOneInspector manyToOneInspector, string columnName)
{
var mapping = manyToOneInspector.GetMapping ();
mapping.Column ( columnName );
}
public static void ForeignKey(this IManyToOneInspector manyToOneInspector, string foreignKeyName)
{
var mapping = manyToOneInspector.GetMapping();
mapping.ForeignKey ( foreignKeyName );
}
}
public static class ManyToOneMappingExtensions
{
public static void Index (this ManyToOneMapping manyToOneMapping, string indexName)
{
if (manyToOneMapping.Columns.First().IsSpecified("Index"))
return;
foreach (var column in manyToOneMapping.Columns)
{
column.Index = indexName;
}
}
public static void Column(this ManyToOneMapping manyToOneMapping, string columnName)
{
if (manyToOneMapping.Columns.UserDefined.Count() > 0)
return;
var originalColumn = manyToOneMapping.Columns.FirstOrDefault();
var column = originalColumn == null ? new ColumnMapping() : originalColumn.Clone();
column.Name = columnName;
manyToOneMapping.ClearColumns();
manyToOneMapping.AddColumn(column);
}
public static void ForeignKey(this ManyToOneMapping manyToOneMapping, string foreignKeyName)
{
if (!manyToOneMapping.IsSpecified("ForeignKey"))
manyToOneMapping.ForeignKey = foreignKeyName;
}
}
My following straight forward test doesn't pass (Though I feel it should). Either I am missing something or is not clear of Property.value constraint. please help me in understanding concept of property.value constraint.
public interface ISomeInterface
{
void SomeMethod(string x, string y);
}
public class SomeClassTest
{
[Test]
public void SomeMethodTest()
{
MockRepository mocks = new MockRepository();
ISomeInterface mockservice = mocks.StrictMock<ISomeInterface>();
using (mocks.Record())
{
mockservice.SomeMethod("xValue", "yValue");
LastCall.Constraints(Property.Value("x", "xValue"),
Property.Value("y", "yValue"));
}
mockservice.SomeMethod("xValue", "yValue");
mocks.Verify(mockservice);
}
}
Exception raised:
Rhino.Mocks.Exceptions.ExpectationViolationException : ISomeInterface.SomeMethod("xValue", "yValue"); Expected #0, Actual #1.
ISomeInterface.SomeMethod(property 'x' equal to xValue, property 'y' equal to yValue); Expected #1, Actual #0.
I would recommend you the following syntax (AAA syntax):
// arrange
var mockservice = MockRepository.GenerateMock<ISomeInterface>();
// act
mockservice.SomeMethod("xValue", "yValue");
// assert
mockservice.AssertWasCalled(
x => x.SomeMethod("xValue", "yValue")
);
This sample class illustrates the options for asserting methods were called with appropriate properties:
public class UsesThing
{
private IMyThing _thing;
public UsesThing(IMyThing thing)
{
_thing = thing;
}
public void DoTheThing(int myparm)
{
_thing.DoWork(myparm, Helper.GetParmString(myparm));
}
public void DoAnotherThing(int myparm)
{
AnotherThing thing2 = new AnotherThing();
thing2.MyProperty = myparm + 2;
_thing.DoMoreWork(thing2)
}
}
Using simple values for assertions may work for methods like the DoTheThing method which uses value types:
[Test]
public void TestDoTheThing()
{
IMyThing thing = MockRepository.GenerateMock<IMyThing>();
UsesThing user = new UsesThing(thing);
user.DoTheThing(1);
thing.AssertWasCalled(t => t.DoWork(1, "one");
}
However, if you need to create an object in your method and pass it as a parameter like in the DoAnotherThing method, this approach will not work since you will not have a reference to the object. You have to check the property values of the unknown object, like this:
[Test]
public void TestDoAnotherThing()
{
IMyThing thing = MockRepository.GenerateMock<IMyThing>();
UsesThing user = new UsesThing(thing);
user.DoAnotherThing(1);
thing.AssertWasCalled(t => t.DoMoreWork(null), t => t.IgnoreArguments().Constraints(Property.Value("MyProperty", 3))));
}
The new Rhino syntax would look like the following, but I am crashing VS 2008 when I use it:
thing.AssertWasCalled(t => t.DoMoreWork(Arg<AnotherThing>.Matches(Property.Value("MyProperty", 3))));