I'm looking for a way to intercept Entity Framework's lazy load proxy implementation, or otherwise control what is returned when accessing a Navigation property that may have no value in the database.
An example of what I'm looking for is this Contact class with mailing address, business phone, etc. that may or may not have a contact person.
public partial class Contact
{
private Nullable<System.Guid> _personId;
public Nullable<System.Guid> PersonId
{
get { return _personId; }
set { SetProperty(ref _personId, value); }
}
public virtual Person Person{ get; set; }
// mailing address, other properties...
}
public partial class Person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
private string _lastName;
public string LastName
{
get { return _lastName;}
set { SetProperty(ref _lastName;value); }
}
}
It is very useful in ASP.net Razor pages, WPF or ad-hoc reporting tools, to be able to use expressions like:
Contact c = repo.GetContact(id);
Console.WriteLine("Contact Person " + c.Person.FirstName);
Which of course fails if there is no PersonId, and hence contact.Person is null.
Tools like Ideablade Devforce have a mechanism to return a "NullEntity" for Person in this case, which allows the WriteLine to succeed. Additionally, the NullEntity for Person can be configured to have a sensible value for FirstName, like "NA".
Is there some way to override the Dynamic Proxy mechanism in EF, or otherwise intercept the reference to Person from Contact to enable this scenario?
I have investigated IDbCommandInterceptor, but that does not seem to intercept virtual navigation to individual entity properties, only navigation to entity collections.
Update _____________________________________
To elaborate on my original question, I can't modify the expression by introducing null conditional operators into the them, as these expressions are incorporated into WPF, ASP.Net Razor binding expressions, and/or report data fields, created by other developers or authors. Also, there may be multiple layers of null properties to deal with, e.g. Contact.Person.Spouse.FirstName, where either Person and/or Spouse might be a "null" property. The Devforce Ideablade implementation deals with this perfectly, but is unfortunately not an option on my current project.
you can use a null-conditional operator from c# like this
c.Person?.FirstName
This means that when Person == null , return null or otherwise return FirstName. You would still need to handle the null value
See : https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-
Related
NHibernate can be extended with new implementations of IUserType, so I can customize how a mapped property is read and stored to/from the database.
An example. If I want DB null varchar to load as "n/a" string, and "n/a" string to be stored as null.
How is this possible with EF 6.2?
I am looking for a solution that doesn't break the change-tracker.
As of EF 6.2, there is no such functionality provided out of the box by the library.
If you decide to move to EF Core instead, there you can use the HasConversion functionality.
However, in your case you still wouldn't be able to use it, because there is one caveat: it can't be used to convert null values. Null always gets converted to null. From docs:
A null value will never be passed to a value converter. A null in a database column is always a null in the entity instance, and vice-versa. This makes the implementation of conversions easier and allows them to be shared amongst nullable and non-nullable properties. See GitHub issue #13850 for more information.
In that case, I suggest that instead of a Value Conversion you configure your string property to have a Backing Field. Then, you can read/write to/from the private backing field, and then have a public property handling the null value.
public class Blog
{
private string _stringFromDb;
public string MyString { get; set; }
[BackingField(nameof(_stringFromDb))]
public string MyString
{
get { return _stringFromDb ?? "n/a"; }
}
public void SetMyString(string myString)
{
// put your validation code here
_stringFromDb = myString;
}
}
In EF 6.2 the closest you could have, as a workaround, is a [NotMapped] property that can be in charge of translating the property you load from the DB.
public string StringDB { get; set; }
[NotMapped]
public string StringConverted
{
get { return MyStringProperty ?? "n/a"; }
set { MyStringProperty = value }
}
If, in addition to this, you want to hide the property being mapped to your DB by making it private, it's not as straightforward as with EF Core's backing field, but you could follow this other answer for instructions on how to achieve it.
What's the best way to set default properties for new entities in DDD? Also, what's the best way to set default states for complex properties (eg. collections)?
My feeling is that default values should be in the models themselves as they are a form of business rule ("by default, we want X's to be Y & Z"), and the domain represents the business. With this approach, maybe a static "GetNew()" method on the model itself would work:
public class Person {
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public bool IsAlive { get; set; }
public List Limbs { get; set; }
public static Person GetNew() {
return new Person() {
IsAlive = true,
Limbs = new List() { RightArm, LeftArm, RightLeg, LeftLeg }
}
}
}
Unfortunately in our case, we need the collection property to be set to all members of another list, and as this model is decoupled from its Repository/DbContext it doesn't have any way of loading them all.
Crappy solution would be to pass as parameter :
public static Person GetNew(List<Limb> allLimbs) {
return new Person() {
IsAlive = true,
Limbs = allLimbs
}
}
Alternatively is there some better way of setting default values for simple & complex model properties?
This is an instance of the factory pattern in DDD. It can either be a dedicated class, such as PersonFactory, or a static method, as in your example. I prefer the static method because I see no need to create a whole new class.
As far as initializing the collection, the GetNew method with the collection as a parameter is something I would go with. It states an important constraint - to create a new person entity you need that collection. The collection instance would be provided by an application service hosting the specific use case where it is needed. More generally, default values could be stored in the database, in which case the application service would call out to a repository to obtain the required values.
Take a look at the Static Builder in Joshua Bloch's Effective Java (Second Edition). In there, you have a static builder class and you chain calls to set properties before construction so it solves the problem of either having a constructor that takes a ton of arguments or having to put setters on every property (in which case, you effectively have a Struct).
I'll try and be as descriptive as I can in this post. I've read a dozen or more SO questions that were peripherally related to my issue, but so far none have matched up with what's going on.
So, for performing audit-logging on our database transactions (create, update, delete), our design uses an IAuditable interface, like so:
public interface IAuditable
{
Guid AuditTargetId { get; set; }
int? ContextId1 { get; }
int? ContextId2 { get; }
int? ContextId3 { get; }
}
The three contextual IDs are related to how the domain model is laid out, and as noted, some or all of them may be null, depending on the entity being audited (they're used for filtering purposes for when admins only want to see the audit logs for a specific scope of the application). Any model that needs to be audited upon a CUD action just needs to implement this interface.
The way that the audit tables themselves are being populated is through an AuditableContext that sits between the base DbContext and our domain's context. It contains the audit table DbSets, and it overrides the SaveChanges method using the EF ChangeTracker, like so:
foreach (var entry in ChangeTracker.Entries<IAuditable>())
{
if (entry.State != EntityState.Modified &&
entry.State != EntityState.Added &&
entry.State != EntityState.Deleted)
{
continue;
}
// Otherwise, make audit records!
}
base.SaveChanges();
The "make audit records" process is a slightly-complex bit of code using reflection and other fun things to extract out fields that need to be audited (there are ways for auditable models to have some of their fields "opt out" of auditing) and all that.
So that logic is all well and good. The issues comes when I have an auditable model like this:
public class Foo: Model, IAuditable
{
public int FooId { get; set; }
// other fields, blah blah blah...
public virtual Bar Bar { get; set; }
#region IAuditable members
// most of the auditable members are just pulling from the right fields
public int? ContextId3
{
get { return Bar.BarId; }
}
#endregion
}
As is pointed out, for the most part, those contextual audit fields are just standard properties from the models. But there are some cases, like here, where the context id needs to be pulled from a virtual complex property.
This ends up resulting in a NullReferenceException when trying to get that property out from within the SaveChanges() method - it says that the virtual Bar property does not exist. I've read some about how ChangeTracker is built to allow lazy-loading of complex properties, but I can't find the syntax to get it right. The fields don't exist in the "original values" list, and the object state manager doesn't have those fields, I guess because they come from the interface and not the entities directly being audited.
So does anyone know how to get around this weird issue? Can I just force eager-loading of the entire object, virtual properties included, instead of the lazy loading that is apparently being stubborn?
Sorry for the long-ish post, I feel like this is a really specific problem and the detail is probably needed.
TIA! :)
My task is to change the ErrorMessage property of the DataAnnotation validation attribute in MVC2.0. For example I should be able to pass an ID instead of the actual error message for the Model property and use that ID to retrieve some content(error message) from a another service e.g database, and display that error message in the View instead of the ID. In order to do this I need to set the DataAnnotation validation attribute’s ErrorMessage property.
[StringLength(2, ErrorMessage = "EmailContentID.")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
It seems like an easy task by just overriding the DataAnnotationsModelValidatorProvider ‘s
protected override IEnumerable GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
However it seems to be a complicated enough.
a. MVC DatannotationsModelValidator’s ErrorMessage property is read only. So I cannot set anything here
b. System.ComponentModel.DataAnnotationErrorMessage property(get and set) which is already set in MVC DatannotationsModelValidator so we cannot set again. If you try to set you get “The property cannot set more than once…” error message appears.
public class CustomDataAnnotationProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
IEnumerable<ModelValidator> validators = base.GetValidators(metadata, context, attributes);
foreach (ValidationAttribute validator in validators.OfType<ValidationAttribute>())
{
messageId = validator.ErrorMessage;
validator.ErrorMessage = "Error string from DB And" + messageId ;
}
//......
}
}
Can anyone please help me on this?
Here is the question: What is your motivation to changing the error message property?
Think this through very carefully, as you are heading down a path where you are obfuscating what is actually happening in the application. Certainly the database informatino is useful, but it is not really part of the validation, nor should it be.
When you head in this direction, you are essentially saying that the validation can only be invalid if there is a database problem. I see two issues with this:
It breaks the separation of concerns. You are reporting a persistance error in the model, which is not where it occurred.
The solution is not unit testable, as you must engage the database.
I don't like either of the two above.
Can you solve this? Possibly if you will create your own custom validation attribute. I would have to check and ensure that is correct. Another option is to aim for custom validation:
http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
This article can also help you head in the direction you desire:
http://ryanrivest.com/blog/archive/2010/01/15/reusable-validation-error-message-resource-strings-for-dataannotations.aspx
Do you want to solve this? Not really if you are attempting to keep a proper separation of concerns in your application. I would not polute my validation error message (this is not valid) with a database error (I am not valid, but the database also blew up). Just my two cents.
There are built in ways to get the error message via a resource. Instead of a database lookup to get a resource at runtime, generate resources from your database and use that for your error messages.
You can then use the ErrorMessageResourceName and ErrorMessageResourceType to allow the DataAnnotation to perform a resource lookup instead of hard-coding a specific string.
public sealed class MyModel
{
[Required(
ErrorMessageResourceName="MyDescriptionResource",
ErrorMessageResourceType=typeof(MyCustomResource))]
public string Description { get; set; }
}
Also you may want to have a look at ValidationAttribute.FormatErrorMessage Method on msdn.
This method formats an error message
by using the ErrorMessageString
property. This method appends the name
of the data field that triggered the
error to the formatted error message.
You can customize how the error
message is formatted by creating a
derived class that overrides this
method.
A quick sample (and not meant to be a definitive example)
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false,
Inherited = true)]
public sealed class PostCodeValidationAttribute
: ValidationAttribute
{
public override bool IsValid(object value)
{
if( value == null )
return true;
string postCode = value as string;
if( string.IsNullOrEmpty(postCode) )
return true;
if ( !PostCode.IsValidPostCode(postCode, this.PostCodeStyle) )
return false;
return true;
}
public PostCodeStyle PostCodeStyle { get; set; }
public override string FormatErrorMessage(string name)
{
return string.Format(
"{0} is not a valid postcode for {1}", name, PostCodeStyle);
}
}
* I've omitted the PostCodeStyle enumeration as well as the PostCode class for validating a postcode.
I'm using VS2010, EF4 feature CTP (latest release), and POCO objects, such as the example below:
class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual IList<Account> Accounts { get; set; }
...
}
class Account
{
public string Number { get; set; }
public int ID { get; set; }
...
}
For the sake of brevity, assume context below is the context object for EF4. I have a dbml mapping between entity types and the database, and I use it like this with no problem:
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
context.Add(doug);
context.Save();
doug.Accounts.Add(new Account() { Name = "foo" });
context.Save(); // two calls needed, yuck
At this point, the database has a Person record with the name "Doug", and an account record "foo". I can query and get those record back just fine. But if I instead try to add the account before I save the Person, the Accounts list is null (the proxy hasn't created an instance on that property yet). See the next example:
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
doug.Accounts.Add(new Account() { Name = "foo" }); // throws null reference exception
context.Add(doug);
context.Save();
Has anybody else encountered this? Even better, has anyone found a good solution?
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
context.Add(doug);
doug.Accounts.Add(new Account() { Name = "foo" });
context.Save();
This will work
Yes and yes!
When you new the POCO up (as opposed to CreateObject from the Context), no proxies are provided for you. This may seem obvious, but I had to explicitly remind myself of this behavior when chasing a similar issue down. (I know this isn't the situation you described in the question, but the overall issue should be acknowledged).
Initializing collections in the constructor of the POCO does not interfere with proper EF4 proxy lazy-loading behavior, from what I've observed in my own testing.
OK, all this being said, I now see your comment to the previous answer -- why don't I have a proxied Addresses collection when I request a new Person from my context? Do you have lazy loading enabled on the context? Seeing how we're dealing with navigation properties, I could see where having lazy loading turned off may make a difference in this situation.
ISTM that if you expect the framework to do all this for you then you wouldn't really have a "POCO", would you? Take your Person class, with the code above. What would you expect the state of the Accounts property to be after construction, with no constructor, if the EF weren't involved? Seems to me that the CLR will guarantee them to be null.
Yes, proxies can initialize this when necessary for materialization of DB values, but in the EF, "POCO" actually means "Plain". Not "something packed with runtime-generated code which we pretend is 'Plain'".