EF5 code first - You cannot use Ignore method on the property - ef-code-first

This question has been asked all over the place, but the SUPPOSED workaround on CodePlex does not work.
I'm hoping someone has some updated information.
I have an EF5 Code First project where I have dozens of entities directly derived from an abstract base class. After creating some new entities that are derived from a class derived from that base class, when my database is initially created I get the following error:
You cannot use Ignore method on the property 'DisplayString' on type
'Doctor' because this type inherits from the type
'Contact' where this property is mapped. To exclude
this property from your model, use NotMappedAttribute or Ignore
method on the base type.
Here's my classes:
public abstract class AbsoluteBaseClass
{
[NotMapped]
public abstract string DisplayString { get; set; }
...
}
public class Contact : AbsoluteBaseClass
{
[NotMapped]
public override string DisplayString
{
get { return string.Format("{0} {1}", FirstName, LastName); }
set { throw new System.NotImplementedException(); }
}
...
}
public class Doctor : Contact
{
...
}
I have other cases like this (class derived from a class derived from the base) and I've got things working, but adding these new classes broke things again.
I've also tried add .Ignore directives (derived class before base) in OnModelCreating and that does not make any difference either.
modelBuilder.Entity<Doctor>().Ignore(p => p.DisplayString);
modelBuilder.Entity<Contact>().Ignore(p => p.DisplayString);
I have several cases where I have entities derived from AbsoluteBaseClass and most times things work, but then I would add another derived class and things would break again. There appears to be no rhyme or reason to this.
I'd REALLY appreciate some advice on how I can definitively get this to work as I add classes. There appears mention around of a fix applied to the EF5 source,then you build the source. Has anyone tried that and got it to work?
Thanks for any advice!
Corey.

In my case, when using Code First (EF6) on an existing database, I created some base classes to handle the common properties like ID.
(Note: the following are inside the OnModelCreating(DbModelBuilder mb) method)
I then needed to ignore the base classes entirely with:
mb.Ignore(new[] {
typeof(BaseClassA),
typeof(BaseClassB)
});
Then, somewhat counterintuitively, I needed to register the base model properties with:
mb.Entity<BaseClassA>().HasKey(m => m.ID);
mb.Entity<BaseClassB>().Whatever...
One of my derived classes needed to ignore one of the base properties (call it NormallyNotIgnored). I used EntityTypeConfiguration, but I assume you could do the same with regular Fluent:
mb.Entity<DerivedClassB1>().Ignore(m => m.NormallyNotIgnored);
This at least has compiled/migrated (with -IgnoreChanges on the migration, since the tables already exist) and resolved the error in question.

Related

Entity Framework Class Manipulation

I'm using Entity Framework (DB First) on a new project and wanted to add some customisation to the classes generated. However, my changes are obviously lost every time that the edmx is refreshed. I was just wondering if there is a design pattern for handling this sort of thing?
As an example, suppose I have a class with a integer property; StatusID - and I'd like to extend the entity class so that the status value can also be accessed/set via the related enum and finally a property that gets a text representation of that Enum from the description attribute. This all works, but those customisations are lost when the model is refreshed. I appreciate that the property can be converted to an enum, so the latter property that gets the description of the enum is perhaps a better example for this question.
I think I know the answer but I just wanted to put this out there in case there were some magic tricks that would allow this to work and prevent those customisations from being lost.
public int StatusID { get; set; }
public Enumerations.ValidationStatus StatusEnum
{
get
{
return (Enumerations.ValidationStatus)StatusID;
}
set
{
StatusID = (int)value;
}
}
public string StatusText
{
get
{
return MyMethodThatGetsTheEnumDescription(StatusEnum);
}
}
Two Solutions to work around the problem:
User Data Transfer Object(DTO) nd put the enum there. then use Automapper or manually map between the DB Model and the DTO Model (best practice)
Instead of enum you can use extension functions on the model and define your getter, setters and any extra properties you want as extension functions to the class
(will add some complexity to your models)

Required attribute in Buddy Class not working with Entity Framework 5 and ASP.NET

My database has a field that is not nullable, but can contain an empty string. When I try to save the record using connection.SaveChanges(), I get an exception saying "The MyField field is required."
I have created a BuddyClass as follows, but I still get the message:
namespace MyNamespace {
[MetadataType(typeof(QuesT_Metadata))] public partial class QuesT { }
public class QuesT_Metadata {
[Required(AllowEmptyStrings = true)
public string MyField { get; set; }
}
}
I can use the ErrorMessage attribute to change the message in the error that is thrown, so I know the Buddy Class is working properly, but apparently the Required attribute is not.
I also tried including attribute DisplayFormat(ConvertEmptyStringToNull = false), but got the same result.
I have done this before, and also the first reference below seems to say it should work, so I'm stumped. Can anyone help?
References (Only the first two seem directly relevant, but the others may still be helpful):
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute.allowemptystrings.aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute.convertemptystringtonull.aspx
How to make an Entity Framework property NOT NULL, but not required in form submission
Data annotation attributes not working using buddy class metadata in an MVC app
Data validation with custom attributes (AttributeTargets.Class) on EF buddy classes
I'm in the same boat here... I've got several instances of your exact behavior which are working just fine....
and now, one particular field won't behave...
But, if I leave off the "Required(AllowEmptyStrings = true)" attribute, things go back to working just fine. Which, I guess is what I'm really looking for, as the attribute in question doesn't really make all that much sense (Required, but allow the user not to answer).....
For me the bigger question is why it sometimes works, and sometimes doesn't ?
But at a miminum, deleting the one like of code should solve the problem for you.
I have worked around this by trapping the error:
public static class ExtensionMethods {
public static void SaveChangesWithEmptyStrings(this DbContext context) {
try {
context.SaveChanges();
}
catch (DbEntityValidationException ex) {
foreach (DbEntityValidationResult result in ex.EntityValidationErrors)
foreach (DbValidationError error in result.ValidationErrors) {
Type t = result.Entry.Entity.GetType();
PropertyInfo pi = t.GetProperty(error.PropertyName);
pi.SetValue(result.Entry.Entity, "");
}
context.SaveChanges(); // Try again
}
}
}

Nancy and abstract base module class / creating phantom class instance

I'm building a small application with Nancy
I want to have a kind of base class, that other modules can inherit from.
See below (there is a reason this isn't an abstract class, which I'll explain below)
public class ImportModule<T> : NancyModule
{
protected ImportModule()
: this(typeof(T).Name.ToLower())
{
Get["/"] = _ => "need to select an action - xxx";
Get["/importnew"] = _ => ImportNew(); //note - method omitted for brevity
}
}
When I run my app, I get
Unable to resolve type: My.NameSpace.TypedImporter`1
As a sidenote, if the ImportModule class is abstract, this doesn't happen
Ok-
Now, I could have a class like this:
public class MyCustomImporter : ImportModule<MyCustomType>
{
//overrides....
}
But, elsewhere, in a "DefaultImportModule" I have the following:
var importerModule = Activator.CreateInstance(typeof(ImportModule<>).MakeGenericType(type));
So I need to be able to create a type of importer, based on a type that's passed in
(Basically, if a user hits the url /customer, it's doing the same as a class like this would)
public class CustomerImporter : ImporterModule<Customer>
{
}
So, as I see it, I have two choices:
1) Stop nancy trying to map my ImportModule
2) Instantiate a "phantom class" that inherits from ImportModule and make ImportModule abstract again
There is a discussion in the Nancy group that may be related to your problem, it is certainly related to the non-abstract base modules not being recognised by Nancy. Here is what Andreas HÃ¥kansson had to say:
Nancy locates everything that's inheriting NancyModule and assumes
it's something it can instantiate. Nancy is aware that it cannot
create an instance of an abstract module. So if you have a base-module
that's not abstract then Nancy will attempt to new it up. I'd like to
think that for 99% of the time, having a non-abstract base class is a
broken design.
And here is the link to the discussion:Unable to resolve type: Nancy.Routing.DefaultRouteResolver

Unable to delete child entities from a POCO using Unit Of Work pattern

I am using POCO classes on an EF4 CTP5 project and I am having trouble deleting child properties. Here's my example (hopefully not too long).
Relevant Portions of the Tour Class
public partial class Tour
{
public Guid TourId { get; private set; }
protected virtual List<Agent> _agents { get; set; }
public void AddAgent(Agent agent)
{
_agents.Add(agent);
}
public void RemoveAgent(Guid agentId)
{
var a = Agents.Single(x => x.AgentId == agentId);
_agents.Remove(Agents.Single(x => x.AgentId == agentId));
}
}
Command Handler
public class DeleteAgentCommandHandler : ICommandHandler<DeleteAgentCommand>
{
private readonly IRepository<Core.Domain.Tour> _repository;
private readonly IUnitOfWork _unitOfWork;
public DeleteAgentCommandHandler(
IRepository<Core.Domain.Tour> repository,
IUnitOfWork unitOfWork
)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
public void Receive(DeleteAgentCommand command)
{
var tour = _repository.GetById(command.TourId);
tour.RemoveAgent(command.AgentId);
// The following line just ends up calling
// DbContext.SaveChanges(); on the current context.
_unitOfWork.Commit();
}
}
Here's the error that I get when my UnitOfWork calls DbContext.SaveChanges()
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
This is happening because EF wont just automatically delete the an Agent entity from the database just because it has been removed from the Agents collection in my Tour class.
I need to explicitly call dbContext.Agents.DeleteObject(a);, but my problem is, I don't have access to the dbContext from within my POCO.
Is there any way to handle this scenario?
With your current architecture I am afraid you need to feed your DeleteAgentCommandHandler with a second repository (IRepository<Core.Domain.Agent>, I guess) and then call something like Delete(command.AgentId) on that second repository.
Or you could extend your IUnitOfWork to be a factory of repositories, so the interface would get an additional method like T CreateRepository<T>() which allows you to pull any instance of your generic repository from the unit of work. (Then you only need to inject IUnitOfWork into the DeleteAgentCommandHandler, and not the repositories anymore.)
Or stay away from generic repositories in your business/UI layer. If Agent is completely dependent on Tour it doesn't need to have a repository at all. A non-generic ITourRepository could have methods to handle the case of removing an agent from a tour in the database layer appropriately.
This does seem like something that should work. I've found this post which suggests this feature is being investigated for future versions:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/
I've also found another post (somewhere - unfortunately I lost it) which suggested adding the parent entity's key to the child entity in your DbContext OnModelCreating method like this:
modelBuilder.Entity<Agent>()
.HasKey(AgentId)
.HasKey(TourId);
Currently this throws an exception at runtime using code-first, although I have got this working when using an EDMX file by hacking the XAML to include the parent key in the store data model as well as the conceptual data model. I think this difference in behaviour is because in the case of the EDMX file, EF trusts that the store metadata it holds is accurate, whereas code-first checks the database to see whether it's model matches.
Another way which may work although I haven't yet tried it yet, is to include the parent key as a compound key in the child table so that code-first is happy. Obviously changing the database or hacking the XAML are both less than ideal and workarounds at best.

Can I create a column of nvarchar(MAX) using FluentMigrator?

Using FluentMigrator, the default creation of a Column using .AsString() results in an nvarchar(255). Is there a simple way (before I modify the FluentMigrator code) to create a column of type nvarchar(MAX)?
You could create an extension method to wrap .AsString(Int32.MaxValue) within .AsMaxString()
e.g.
internal static class MigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsMaxString(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsString(int.MaxValue);
}
}
OK, I found it. Basically, use .AsString(Int32.MaxValue). Pity there's not a .AsMaxString() method, but I guess it's easy enough to put in...
You can use AsCustom("nvarchar(max)") and pack it to extension
If you often create columns/tables with the same settings or groups of columns, you should be creating extension methods for your migrations!
For example, nearly every one of my tables has CreatedAt and UpdatedAt DateTime columns, so I whipped up a little extension method so I can say:
Create.Table("Foos").
WithColumn("a").
WithTimestamps();
I think I created the Extension method properly ... I know it works, but FluentMigrator has a LOT of interfaces ... here it is:
public static class MigrationExtensions {
public static ICreateTableWithColumnSyntax WithTimestamps(this ICreateTableWithColumnSyntax root) {
return root.
WithColumn("CreatedAt").AsDateTime().NotNullable().
WithColumn("UpdatedAt").AsDateTime().NotNullable();
}
}
Similarly, nearly every one of my tables has an int primary key called 'Id', so I think I'm going to add Table.CreateWithId("Foos") to always add that Id for me. Not sure ... I actually just started using FluentMigrator today, but you should always be refactoring when possible!
NOTE: If you do make helper/extension methods for your migrations, you should never ever ever change what those methods do. If you do, someone could try running your migrations and things could explode because the helper methods you used to create Migration #1 works differently now than they did earlier.
Here is the code for creating columns incase it helps you create helper methods: https://github.com/schambers/fluentmigrator/blob/master/src/FluentMigrator/Builders/Create/Column/CreateColumnExpressionBuilder.cs
How about extending like this:
public static class StringMaxMigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsStringMax(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
public static IAlterColumnOptionSyntax AsStringMax(this IAlterColumnAsTypeSyntax alterColumnAsTypeSyntax)
{
return alterColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
}

Resources