سؤال

Does FluentNHibernate's automap support creating a multi-column unique constraint via convention?

I can easily create a single column unique constraint:

public void Apply(IPropertyInstance instance)
{
    if(instance.Name.ToUpper().Equals("NAME"))
        instance.Unique();
}

With an acceptance criteria to find only ReferenceEntity types.

I now have an entity that I want to create a multiple column unique constraint over. I was planing on decorating the entity's properties with an attribute to indicate they form part of the same unique key, ie:

public class Foo : Entity
{
    [Unique("name_parent")]
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }

    [Unique("name_parent")]
    public virtual Bar Parent { get; set; }
}

And have the convention look for these attributes and create a unique across all fields that share the same unique key.

The IProperyConvention interface allows you to specify Unique on the specific instance (column) but without visibility over other columns.

Update

The process of posting this helped me think through a bit more of it, so I wrote this:

var propertyInfo = ((PropertyInstance)(instance)).EntityType.GetMember(instance.Name).FirstOrDefault();

if (propertyInfo != null)
{
    var attributes = propertyInfo.GetCustomAttributes(false);

    var uniqueAttribute = attributes.OfType<UniqueAttribute>().FirstOrDefault();

    if (uniqueAttribute != null)
    {
        instance.UniqueKey(uniqueAttribute.Key);
    }
}

Stepping through the code it hits the instance.UniqueKey(...) line twice (as expected), however the constraint that is created (pgsql);

ADD CONSTRAINT foo_name_key UNIQUE(name);

When I would have expected

ADD CONSTRAINT name_parent UNIQUE(name, bar);

Update 2

When using the Override() method in the AutoMap config, I can specify the correct unique keys:

.Override<Foo>(map => {
    map.Map(x => x.Name).UniqueKey("name_parent");
    map.Map(x => x.Description);
    map.References(x => x.Parent).UniqueKey("name_parent");
})

This might be an issue with the auto-mapper, not sure. Not ideal still as it isn't generic via attribute decoration, but for now the datamodel is reflecting domain requirements.

هل كانت مفيدة؟

المحلول

When I did this I created 2 conventions One was an AttributePropertyConvention and the other was an IReferenceConvention. My attribute looked like this:

public class UniqueAttribute : Attribute
{
    public UniqueAttribute(string uniqueKey)
    {
        UniqueKey = uniqueKey;
    }

    public string UniqueKey { get; set; }

    public string GetKey()
    {
        return string.Concat(UniqueKey, "Unique");
    }
}

My property convention:

public class UniquePropertyConvention : AttributePropertyConvention<UniqueAttribute>
{
    protected override void Apply(UniqueAttribute attribute, IPropertyInstance instance)
    {
        instance.UniqueKey(attribute.GetKey());
    }
}

And finally the reference convention:

public class UniqueReferenceConvention : IReferenceConvention
{
    public void Apply(IManyToOneInstance instance)
    {
        var p = instance.Property.MemberInfo;

        if (Attribute.IsDefined(p, typeof(UniqueAttribute)))
        {
            var attribute = (UniqueAttribute[])p.GetCustomAttributes(typeof(UniqueAttribute), true);
            instance.UniqueKey(attribute[0].GetKey());
        }
    }
}

Hopefully this works for your scenario.

نصائح أخرى

Don't do it.... enforce uniqueness in your domain ;)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top