Automapping custom types - c#

I am in the process of setting up nhibernate using fluent nhibernate for a relatively simple setup. Automapping is able to do everything currently fine except for one property on my objects.
I have properties of type MongoDB.Bson.ObjectId. This is simple immutable struct that basically represents a binary ID that can be easily represented in string format as well. These properties cause NHibernate to throw an error saying:
An association from the table PostView refers to an unmapped class:
MongoDB.Bson.ObjectId
This is quite expected of course because I don't expect nhibernate to understand what ObjectId is.
Where I am stuck is that what I want is to be able to tell Nhibernate to map this object type to a string representation in the database. I would like to be able to do this while still using automapping so I don't have to explicitly map all of those objects - what I'd like is to be able to just say "Whenever you find this objecttype use this mapping". I've found mention of NHibernate.UserTypes.IUserType which seems to look like it does what I want but I've found nothing that usefully tells me how to use it.
So to summarise the question:
How can I automatically map a custom data type to a known type for storing in the database (and of course the reverse).
I would prefer not to change my objects to storing the string representation of the object if possible.

You have to write a convention for this type.
Something like this:
public class CustomTypeConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType == typeof(MyType));
}
public void Apply(IPropertyInstance target)
{
target.CustomType(typeof(string));
}
}
And add this convention to mappings:
mapping.Conventions.Add(new CustomTypeConvention());

Related

C# mapping data directly to an object

I am creating a library that pulls HTML tables from the web and converts them to objects. I have a ColumnConfigurator object that pre-configures how a specific table is laid out:
config.Column().Text().MapTo(/*Class.TextPropertyName*/); //What I want to be able to do
config.Column().Date().MapTo(/*Class.DatePropertyName*/);
The above code is the general idea of what I want to be able to do in the configuration (note that the first function creates a new column and returns it, and the subsequent methods set the object's configuration), and then when the table is being mapped to I'd like to retrieve the parameter passed into MapTo and have it automatically resolve to the correct property of a given object.
Here's what's confusing me the most: I want to make the main table object generic (ParsingTable<T>) so that theoretically any object can be mapped to. This means that none of the properties are available ahead of time. I'd like to pass the T.Property into the method so it knows how to automap. Is there a way to do this? I've read a small bit about reflection but heard it's bad for performance.
For information on creating a fluent interface (what you are doing for the configuration), see here: creating API that is fluent
For the MapTo method to accept the property name from a generic class, you will end up with something like this:
public class ParsingTable<T>
{
/*** All the other stuff ***/
public static IColumnConfiguration MapTo(this IColumnConfiguration config, Expression<Func<T, object>> property)
{
if (property.Body is MemberExpression)
{
config.Property = (property.Body as MemberExpression).Member as PropertyInfo;
}
else
{
config.Property = (((UnaryExpression)property.Body).Operand as MemberExpression).Member as PropertyInfo;
}
}
}
public interface IColumnConfiguration
{
PropertyInfo Property { get; set; }
}
Then when you are doing the parsing, you would call the PropertyInfo.SetValue() function for each value/column.
You are correct that this will likely be a little on the slower end compared to a direct property set. It depends highly on your situation if this is a big deal or not. The next step would be to compile a lambda expression for each column set if you really want to avoid the SetValue() call.
Note, I took some of the code above from here: http://blog.raffaeu.com/archive/2010/06/26/how-to-write-fluent-interface-with-c-and-lambda.aspx

Using AutoMapper to map Wrapper<T> to T' by convention

I have a service which wraps enum-values in a SafeEnum type, to enable addition of new enum values without breaking the contract.
Here's an example:
public class Customer
{
public int Id { get; set; }
public SafeEnum<CustomerType> Type { get; set; }
}
public class CustomerModel
{
public int Id { get; set; }
public CustomerModelType Type { get; set; }
}
When mapping from Customer to CustomerModel using AutoMapper, is there a way to automatically map from SafeEnum<T> to T', where T is the wrapped type and T' is the matching type in the model?
I know this can be fixed by configuring for each relevant enum-type, but I am looking for a more elegant solution.
I have stumbled with the same problem recently. From what i've gather there is no official support from Automapper for this kind of scenario (there's no mention of generic wrappers or anything similar on the documentation and i couldn't find any web resource with how to do it), but it can be accomplished with a little bit of work.
First you need to create a class that implements the IObjectMapper interface. This interface has two methods IsMatch(ResolutionContext context) and Map(ResolutionContext context, IMappingEngineRunner mapper)
When mapping two objects, the IsMatch method is used internally by Automapper to determine if a given instance of IObjectMapper can be used to map from the source type to the destination type. On the ResolutionContext object you have the SourceType, DestinationType, SourceValue, DestinationValue, an instance of MappingEngine among other things that can help you determine if you can map the two types with your current mapper.
The Map method is responsible for the actual mapping between the two types.
So, on the IsMatch method you should check if the source or destination type are instances of your wrapper class. And then, on the Map method, when mapping from the wrapped value, you could unwrap the value with reflection, and use the mapping engine provided on the ResolutionContext to map the unwrapped value to it's destination type, and then returning it.
Similarly, when mapping from any type to a wrapped type, you could get the type argument of the closed generic wrapper, use the mapping engine provided on the resolution context to map from the source type, to the type enclosed by your generic wrapper, and wrap the result on an instance of the wrapper class of the appropiate type.
Finally, you need to include this mapper when configuring your MappingEngine on the application startup (This doesn't work with with the static method of the Mapper class, since you can't change the default mappers on it), like this:
var configuration = new ConfigurationStore(new TypeMapFactory(), new IObjectMapper[] { new WrapperMapper()}.Union(MapperRegistry.Mappers));
var engine = new MappingEngine(configuration);
The MapperRegistry.Mappers static property is a collection of all the default Automapper mappers. If you don't include these, you'll lose all the default functionality.
Finally, here's a working fiddle:
https://dotnetfiddle.net/vWmRiY
It'll probably need some work to adapt it to your particular use case, but the general idea is there. This could be used to wrap primitive types, complex types, or anything that is supported by automapper.

Make EF convert properties of a castable-to-string-type to string before sending to the database

Is there a way I can tell EF to convert my MultilingualString properties to string before sending them to the database (and the other way around when fetching from database) ? I want to wrap the behaviour to reuse it.
public class MyEntity
{
// I want this property to be considered a string by EF (it is castable to string)
public MultilingualString MyProperty { get; set; }
}
public class MultilingualString
{
public static implicit operator string(MultilingualString mlString)
{
return mlString.ToJson();
}
public static explicit operator MultilingualString(string json)
{
return new MultilingualString(json);
}
...
}
Three thoughts:
One is to to generate your classes from a T4 template that will generate the backing field and the [NotMapped] MultilingualString. Technically you'll be writing less code, and each T4 template could use a method you put in from another assembly to keep from repeating the generation code in each template. I do acknowledge that this idea isn't going to make you feel better, though.
Thought two is to use PostSharp, attribute up your backing fields and have a PostSharp extension create the multilingual fields, or vice versa. The large downside to this is that static analysis tools are not going to like it much.
Thought three is to make MultilingualString a ComplexType that contains the string field as a public property and also has whatever other methods you have built the class to perform at the moment. It will generate a more messy database column name, if you care about that.
I reckon 3 has the most value.
EDIT:
For option 3 you can control the column names if you need to:
modelBuilder.Entity<MyEntity>().Property(x => x.MyProperty.StringValue)
.HasColumnName("i_dba_aprvd_rdbl_col_nm")
There's no reason this fluent configuration couldn't be generated using reflection to find all such complex property values if there was a rule by which the column name could be determined, e.g. it was the name of the property of type MultilingualString on the entity.

OData Exception: A recursive loop of complex types is not allowed

I expose a complex type through OData. The class is like this:
public class RemoteFile
{
[Key]
public int Id { get; set; }
[Required]
public string Resource { get; set; }
public virtual ICollection<RemoteFile> RelatedFiles { get; set; }
}
And I expose it through OData:
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.ComplexType<RemoteFile>();
Here is what I got when I start the project:
An exception of type 'System.ArgumentException' occurred in System.Web.Http.OData.dll but was not handled in user code
Additional information: The complex type 'RemoteFile' has a reference to itself through the property 'RelatedFiles'. A recursive loop of complex types is not allowed.
If there is a handler for this exception, the program may be safely continued.
Any suggestion is welcomed.
It sounds like it makes more sense for RemoteFile to be an entity type, not a complex type. Entity types can have properties that point to the originating type, which is how you've set up RemoteFile. Your definition of the type also has a key property, which is used for entity types, not complex types. (Think of complex types as a convenient way to group a bunch of scalar properties. Entity types are the first-class types of your system where each instance can be uniquely identified.)
So instead of this:
modelBuilder.ComplexType<RemoteFile>();
Try this:
modelBuilder.EntitySet<RemoteFile>(“RemoteFiles”);
That line will create both the entity type RemoteFile and the entity set RemoteFiles. An entity set is the container for all the instances of an entity type.
So why is recursion allowed for entity types but not complex types? When you ask for an entity, by default the server won't fetch the data of referenced entities. You can explicitly ask for the data of a referenced entity by using $expand in the query, but you can't expand infinitely. On the other hand, complex values will always be included when you ask for their parent. So if you have a circular complex value, you'll create a stack overflow when you try to serialize it.
Do you need to explicitly ignore the navigation property by any chance?
modelBuilder.ComplexType<RemoteFile>().Ignore(x => x.RemoteFile);
Hope that helps :)
I had the same problem. I had a model with more than 100 entities, and I tried to add only two, for make tests.
The solution: ADD ALL ENTITIES to ODataConventionModelBuilder, something like this:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Entity1>("Entity1");
builder.EntitySet<Entity2>("Entity2");
builder.EntitySet<Entity3>("Entity3");
//... and thus for ALL YOUR ENTITIES.
// If you don't want to expose any entity like EntitySet, simply add to builder like EntityType:
builder.EntityType<Entity4>("Entity4");
Even if you do not add entities, the builder scans all types like Complex Types, and relationships fail. Therefore it is necessary to specify that all scanned types are Entities.
If you don't want to expose all like EntitySet, you can add to builder like EntityType, and your client reference will use this class but not will give you access to a EntitySet (CRUD Operations). This Entities only can be used indirectly through relationships of exposed entities.
The error message "The complex type 'RemoteFile' has a reference to itself through the property 'RelatedFiles'. A recursive loop of complex types is not allowed." is due to a limitation of the Web API OData library, specifically the inner workings of the ODataConventionModelBuilder class. This is a blocking issue for many people, tracked here on GitHub.

PetaPoco and Ignore attribute

I have the following class:
public class Foo
{
public int Id { get; set; }
...
public Boo Boo1 { get; set; }
public Boo Boo2 { get; set; }
}
I want to exclude Boo1 and Boo2 properties but I don't want to decorate those properties with PetaPoco.Ignore attribute. I want to have pure POCO objects. Can I execute Ignore command in code or do I have to create query/stored procedure and manually map all fields?
Any help would be greatly appreciated!
Looks like PetaPoco can't be told in any other way that fields/properties should be ignored than by using attributes. You can either Ignore a few members, or if you're not mapping the majority of a class then you can specify explicit column mapping for the class and decorate the ones you DO want mapped. I understand your hesitance to add ORM-specific cruft to a "pure" POCO, but unfortunately that information has to be somewhere, and as PetaPoco doesn't use mapping files (or much of a configuration at all, really), the class is where it goes.
The only thing you could do is create a DTO/DAO that will be what is mapped, then create implicit or explicit operators to convert between the domain class and its DTO. The DTO, then, can simply not have the fields you don't want to include. That keeps both classes POCO (depending on your feelings regarding operator methods), and it just adds a relatively simple step of casting the query result to your domain class.
In my branch here:
https://github.com/schotime/PetaPoco
You can fluently describe your models like I have described here: http://schotime.net/blog/index.php/2011/05/16/fluent-petapoco-external-mappings/ and also use the convention based mapping like here: http://schotime.net/blog/index.php/2012/02/13/petapoco-convention-based-fluent-mapping/
This is a great place for an anonymous type.
In your method for saving foo
public void InsertFoo(Foo f)
{
var db = new Database("connection");
var petaPocoFooObj = new {f.Id}
db.Insert("FooTable", "FooId", petaPocoFooObj);
}
It's just a little more work, although it could be a PITA if your classes are deeply nested.

Categories

Resources