How to Intercept EF operations? - c#

I am currently using Entity Framework for a project and one of my classes have an Enum representing some values.
So far EF is saving the Enums as numbers in the database, but I wanted to save them as their actual string names. For example, the Enum NY is saved as 1, instead of "NY".
I have already seen some ways to make this work, like having a string property with a hidden Enum private field, but I wanted to know if there is a way I can just Intercept EF when it's doing the CRUD operations and then I can change the Enum to a String in the Data Context class.

No, you cannot do that directly: when you map your class with an Enum property, that property is mapped to a database int column, and you cannot change that in any way. I mean that, as you cannot change the model, there is no way to intercept and convert the Enum property value into an string, because the model stubbornly wants an int.
That said, there are several ways to make it work:
having an string property for the key and a [NotMapped] Enum property that updates that key. SEE THE NOTE: But the key must be public, and thus accesible through the application code.
using a class that have only the enum property and is used in your application domain, and a different class which is used for your EF model, and map the values, for example using ValueInjecter or Automapper
I usually take the first path and use an attribute that allows me to define the string key for each Enum value, so you can reuse this pattern in all the cases in which need to do this.
NOTE: this part of the answer was wrong: you can map any property regardles of the modifier (public, protected, private, internal...). EF conventions only include the public properties, and there are no data annotations that can overcome this limitation. But you can use it with the Fluent API. However, as the property is private,you cannot access it directly using the Fluent API. There are several solutions to do it described here: Code First Data Annotations on non-public properties
If you follow this path, you can have a class like this:
public class MyEntity
{
// ...
[NotMapped]
public EnumType Value
{
get { /* return KeyForEnum converted to EnumType value */ }
set { /* set KeyForEnum value from the received EnumType value*/}
}
// Use some mechanism to map this private property
private string KeyForEnum { get; set; }
// ...
}
As you can see, if you use a class like this, in the app the entity will have a property of EnumType type, but in the database it will be an string.
One of the tricks to be able to map it through Fluent API is this:
1) Add an static property that returns an expression able to select the property from an object of this class, i.e.
public static readonly Expression<Func<MyEntity,string>> KeyForEnumExpression
= me => me.KeyForEnum;
2) Use it in the fluent API to get the property mapped, like so:
modelBuilder
.Entity()
.Property(MyEntity.KeyForEnumExpression)
LAST NOTE: This will modify the POCO class by adding the static readonly property. You can use Reflection instead to build an expression to access the private property, like you can see here: EF 4.1 Code First, ¿map private members?. It's in Spanish, but you can look directly at the code

Related

AutoFixture & AutoMoq: Overriding object generation behavior

I'm proposing using AutoFixture and AutoFixture.xUnit at our company, and have gotten the mandate that for certain objects and fields they want random data that is formatted in an expected way. For example, they want PersonName to only populate with realistic names (instead of GUIDs) and PhoneNumber to only make strings that look like phone numbers. But they DON'T want to add data annotations to the actual objects enforcing this, they would just like the test data generated by AutoFixture to be pretty.
I've dealt a bit with ICustomize classes to implement greedy constructor behavior on a few classes. Is there a similar way to override the data generation for specific objects? To (for example) pull names from a list, or generate data to follow a certain regular expression? (keeping in mind that I can't actually add those regular expressions as attributes on the model)
Ok, solved my problem.
Object generation for a given class type can be accomplished via the Fixture.Register method. You can make a method that returns the type you want to override and that will be used instead of the default.
To get meaningful data I just used Faker.Net.
I got the solution Mark pointed out working, and really liked it for general POJOs, but in my case many of my objects had properties that could only be set via the constructor or aggregate setters (like ChangeContactInfo), so unfortunately I needed something a bit more targeted. Here is an example of my solution implementing a name and address generation override:
public class CustomObjectGeneration : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register(GenerateAddress);
fixture.Register(GeneratePersonName);
}
private Address GenerateAddress()
{
return new Address(Faker.Address.StreetAddress(), Faker.Address.SecondaryAddress(), Faker.Address.City(),
Faker.Address.ZipCode(), Faker.Address.UsState(), Faker.Address.Country());
}
private PersonName GeneratePersonName()
{
return new PersonName(Faker.Name.Prefix(), Faker.Name.First(), Faker.Name.First(), Faker.Name.Last(), Faker.Name.Suffix());
}
}

Can I use AutoMapper to map between two fields which are immutable?

I have the following two classes (many properties elided for brevity).
Service Layer POCO:
public class TicketFlag
{
public ContactKey ContactKey;
}
LINQ to SQL generated POCO:
public class TicketFlag
{
public string ContactKey;
}
When trying to use AutoMapper to map between these two on service calls -> database save, I'm getting the following exception:
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
---> System.ArgumentException: Type 'ContactKey' does not have a default constructor
ContactKey does not have a default constructor on purpose. Basically, it takes a string and a list of objects and can serialize/deserialize itself.
I have tried creating a mapping function (and it's inverse) like so:
Mapper.CreateMap<string, ContactKey>().ConvertUsing(s => ContactKeySerializer.Serialize(s));
But I'm still getting complaints because ContactKey doesn't have a default constructor.
Is there a way to get AutoMapper to not use the default constructor to do it's property mapping? Really, just mapping properties on the ContactKey isn't sufficient - I need to have it call the constructor, or get spit out from my ContactKeySerializer class.
First, you should probably be using properties for these things, not fields. However, I doubt that's part of your problem.
Instead of trying to create a map from string to ContactKey, you could try to make this part of the map from one TicketFlag to the other:
Mapper.CreateMap<LINQtoSQL.TicketFlag, Service.Layer.TicketFlag>()
.ForMember(dest => dest.ContactKey,
mem => mem.ResolveUsing(src => ContactKeySerializer.Serialize(src.ContactKey)));
I think that would prevent the error you're getting.
AutoMapper is complaining that you don't have a default constructor because AutoMapper needs to create an empty instance of the target class before it can map values to it. It can't call your ContractKey's parameterized constructor - how would it?
In this case it might seem simple, if the constructor looks like this:
public ContracktKey(string keyValue){}
But what if it had two parameters?
public ContracktKey(string keyValue, string otherValue){}
How would it know where to put the value? What if you only provided one string?
I think it would be best to follow others' advice and map the two TicketFlag objects.

Is it possible to map a varchar that has spaces to an enum with FluentNHibernate

The title asks it all. In the database I have legacy data that contains titles of documents that have spaces such as "Title Holder" and so on. I want to be able to map these directly to an enum with Fluent NHibernate but I am encountering parsing errors. I have been unable to find any indication of a custom converter I can use, are there any recommendations someone can make?
My mapping looks like this.
Map(x => x.DocumentName).Nullable().CustomSqlType("varchar(50)");
You are going to need to implement your own NHibernate IUserType and the bulk of your logic will be in the NullSafeGet() and NullSafeSet() methods.
You'll also need to create your own internal enum to string mapping. You could use a dictionary that would hold the string as the key and the enum value as the value and so your logic would basically revolve around looking up values in that dictionary to convert from a string to an enum and vice versa. Another option could be to use an attribute to decorate each of your enum values with the string version of it's name and then at runtime do conversion with reflection...
Here are some examples of creating a custom IUserType: ( The first link below should really point you in the right direction )
Mapping Strings to Booleans Using NHibernate’s IUserType
Mapping different types - IUserType
Implementing custom types in nHibernate
It is possible to write a custom type that gets rid of the spaces when the data is read from database and then you can map the converted string to an enum. Problem with this approach would be when saving data back to database because you would not know where to add the space back in (unless you are happy with spaghetti code to keep track of where to insert the spaces back).
Alternatively, you can have an additional property on the class of type enum that returns the enum based on what is in the property mapped to database. Example below
public class Document
{
public virtual string DocumentName {get; set;}
public EDocumentName Name
{
get
{
if (DocumentName == "Title Holder")
{
return EDocumentName.TitleHolder;
}
}
set
{
if(value == EDocumentName.TitleHolder)
{
DocumentName = "Title Holder";
}
}
}
}
public enum EDocumentName
{
TitleHoldder
}

Entity Framework Code First CTP5 : How to define non primitive types

I'm testing the CTP5 for entity framwork code first, and i've run into this problem
I've got a class that has a property of type Uri (System.Uri), but it looks like it's unable to automatically identify how to store that, so i get an error like
Problem in mapping fragments starting at line 23:No mapping specified for properties WebPage.Uri in Set WebPage
How can i tell the model to map the Uri to a varchar, for example, with the url of the uri??
The actual POCO model has to bind to primitive types. You can use a complex type binding such as:
[ComplexType()]
public class UriHelper
{
public string StringRepresentation {get;set;}
public Uri ActualUri()
{
return new Uri(StringRepresentation);
}
}
And in your actual object reference this complex type as the Uri reference if you absolutely need to. Your mapping would then reference the property for the actual value as a String. The final option is to create a custom mapping from URI to string and vice versa for the EF engine to use. However, I would not advise this. The actual database property is of type varchar or nvarchar, not URI. Thus EF doesn't know what a URI is.

Not-Null constraints in POCO Objects

I am currently writing an financial application, and we have a pretty standard customer table. It consists of many mandatory fields, and some optional like Cell/Fax etc.. I'm using NHibernate as a ORM and have all the mappings right. It already works.
I just wonder, how do I "express" in code that a field is not-null without commenting? I have the hbm.xml files that document this, but it's kinda awkward to look at them for things like this.
The other thing that comes to mind is that I don't want the repository to throw NHibernate Exceptions at my Logic, so maybe I should go the validation route in the Controller.
Still, how can I make the POCO code express that some fields can be null?
As you can see, I want to have Cellular and Fax be optional while Phone mandatory. They are all just composite mappings, so the mapping file just specifies that the single elements of each have to be not-null, but I hate to do the Person.Cellular != null check all the time to avoid having a NullReferenceException.
There are a few ways of doing this depending on your POCO behaviour and coding style.
Firstly, you could use nullable types to express that this field is nullable and it would therefore be implicit that the rest are not nullable.
Alternatively you could introduce a Phone value type as the type for the Phone property of the POCO you illustrated, implying that because it is not a primitive type it is "more important" - this would also enable you to encapsulate phone number validation within the class itself.
In my mind, to be a true POCO object, it need not worry about the underlying nullability within the database table it is persited in... it should actually have validation and value types that express its behaviour as a stand alone entity; thus before it gets to NHibernate it is already in a valid state.
Make notnull properties readonly and write to them via a public constructor. Make the default constructor protected or private.
public class DomainObject{
private string nnp;
protected DomainObject(){}
public DomainObject(string nnp){
this.nnp = nnp;
}
public string NotNullProp {get {return nnp;}}
public string NullableProp {get;set;}
}

Categories