I am using EF 6. I have a table in db for which the auto-generated class looks like this:
public partial class tblPreparation
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblPreparation()
{
this.tblPreparationItem = new HashSet<tblPreparationItem>();
}
public int id { get; set; }
public string name { get; set; }
public System.DateTime date { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<tblPreparationItem> tblPreparationItem { get; set; }
}
In my code, I want this class to extend another class EntityObject, which is in the namespace System.Data.Entity.Core.Objects.DataClasses (and implement another interface). So I created wrote this partial class:
public partial class tblPreparation : EntityObject, IMyInterface
{
}
It doesn't throw a syntax error but when I run the application I get runtime error: "The type 'tblPreparation' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive or generic, and does not inherit from EntityObject." What am I missing?
I assum it's because they are not in the same namespace. Is there a way to fix this?
I may have misunderstood your comment on the namespace, but for clarity, a Partial class is only actually a partial class when it is in the same namespace of the corresponding partial class, otherwise what you have are just two separate single classes with the same name claiming to be partial. If this is the case, the fix is simple. Put them in same namespace.
However, it is more likely due to adding the EntityObject to the class hierarchy, as oerkelens mentioned. EF 6 creates proxies of your POCOs, for this reason your classes must have parameterless constructors. Adding another class may prevent the db context from creating proxies of your objects.
Remove just that class from the hierarchy, check whether you can materialise these entities to verify or rule it out.
Edit - No, it definitely is due to EntityObject.
I reproduced this by first having my entity implement some interface in a partial class. That worked great. Then I had partial class inherit from EntityObject that failed with your error.
After reproducing this error, I created a class called MyStupidClass and replaced EntityObject with MyStupidClass and I could still materialise entities (even with the top level properties of EntityObject).
So it depends on the class you introduced to the hierarchy.
class Program
{
static void Main(string[] args)
{
using (var db = new schedulerEntities())
{
var schedules = db.Schedules.ToArray();
foreach (var schedule in schedules)
{
Console.WriteLine($"{schedule.Cron} - {schedule.FriendlyDescription}");
}
}
Console.ReadLine();
}
}
public partial class Schedule: MyStupidClass, IScheduler
{
public string FirstName { get; set; }
}
public class MyStupidClass
{
public EntityKey EntityKey { get; set; }
public EntityState State { get; set; }
}
interface IScheduler
{
long Id { get; set; }
string Name { get; set; }
string Cron { get; set; }
}
Related
I have the following class setup
public abstract class SearchElement
{
public int Id { get; set; }
public SearchElement parent { get; set; }
public int Order { get; set; }
public UserQuery UserQuery { get; set; }
}
public class SearchGroup : SearchElement
{
public virtual ICollection<SearchElement> SearchObjects { get; set; }
public bool IsAndOperator { get; set; }
public SearchGroup()
{
this.SearchObjects = new List<SearchElement>();
}
}
public abstract class SearchCondition<IContext, OutputType> : SearchElement
{
public ComparisonTypes Comparison { get; set; }
public string Value { get; set; }
public abstract Expression<Func<OutputType, bool>> BuildConditionQuery(IContext context);
}
public class SearchPackage : SearchCondition<ISearchContext, ProjectParticipantQuestionnaireResponseGroup>
{
public override System.Linq.Expressions.Expression<Func<ProjectParticipantQuestionnaireResponseGroup, bool>> BuildConditionQuery(ISearchContext context)
{
return this.BuildCondition<ProjectParticipantQuestionnaireResponseGroup, int>(r => r.Package.Id, int.Parse(this.Value), this.Comparison);
}
}
Now for some reason, when in the EntityFramework context I specify:
public DbSet<SearchElement> SearchElements { get; set; }
The SearchGroup class gets detected and the appropriate fields get created in the SearchElement table. However, the SearchPackage class does not get detected and it's fields are not created in the SearchElement table.
I can of course create a DbSet for the SearchPackage, but there are multiple similar classes (same inheritance, although some with difference values) and I don't want to create a DbSet for each of them. Does anyone has suggestions about what I can do?
For clarity: I am using Entity Framework 6.1.3 and C# 4.5.1
EF 6 cannot map a CLR generic type. (Sorry, I can't find an authoritative reference on this right now.) This is the problem, not one of inheritance generally. When EF traverses your inheritance "tree," it gets to SearchCondition<,> and gives up.
Finding a way around this will probably require a rethinking of your object model to something more serialization-friendly. Is there a way that you can split your object-model into a set of services (that might contain generics) that interacts with a set of more easily mapped DTOs?
Another (possible, untested) option: Make an ISearchElement mapped interface. SearchPackage should implement it directly. This way, SearchPackage--and other SearchCondition<,> implementors should get "picked up" by EF inheritance traversal.
The issue:
Entity object has it properties related to databases on its own, but the needs in the programming area is differ, sometimes we want to add it some more:
Properties – that is for temporary logic.
Methods – for clean code and for programming necessaries.
Finally yet importantly – Attribute for authorization, display, filters etc.
However, obviously we do not want our program to be maintainability without needs to rewrite code just after we update the model.
For properties and methods, the Entity Framework platform generated all the object from model as partial classes and the .NET environment allow us to extend them as we wish:
Remember to check that our partial sit in same namespaces (Notice that when we create them in model directory or in them own directory Visual Studio create addition namespace).
public partial class ErrorLog
{
public long pk { get; set; }
public int lineNumber { get; set; }
public Nullable<int> error { get; set; }
}
Our partial:
public partial class ErrorLog
{
public string getErrorDescription()
{
return d[(int)error];
}
private static Dictionary<int, string> d = new Dictionary<int, string>()
{
{1,"desc1" },
{2,"desc2" },
{3,"desc3" },
{4,"desc4" }
};
}
For attributes:
We can add new interface
public interface IErrorLogsMetaData
{
[Display(Name = "Id")]
long pk { get; set; }
[Display(Name = "The line Number")]
int lineNumber { get; set; }
[Display(Name = "The Error")]
Nullable<int> error { get; set; }
}
Implement them on our Entity (even extended) object.
For that we need to reflect and book it in global.asax by using:
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ErrorLog), typeof(IErrorLogsMetaData)), typeof(ErrorLog));
TypeDescriptor – familiar for us from reflection, its get information about type.
AddProviderTransparent – is the method called from my partially trusted code and get metadata from associated class.
The first parameter is the provider and it TypeDescriptionProvider from the type we want to decorate and the attributed interface, the second parameter is the target type for decription.
Another Option
Make your partial view to implement the IErrorLogsMetaData and then you don't need to associate at Global.asax
As you can see, the database first entity model classes are partial, so you can create your own partial class, for example if you have:
public partial class SomeClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
You can do something like this:
Add some class to your project, name it SomeClassPartial:
//SomeClassPartial.cs
namespace YourNamespace
{
[MetadataType(typeof(SomeClassMetadata))]
public partial class SomeClass
{
//add your new properties/some_logic here
public string NewPropX { get; set; }
public string NewPropY { get; set; }
}
public partial class SomeClassMetadata
{
//metadata for your existing model properties
[Display(Name = "Property 1")]
public string Prop1 { get; set; }
[Display(Name = "Property 2")]
public string Prop2 { get; set; }
}
}
In your SomeClassMetadata class you can add data annotation attributes to your existing properties with MetadataType attribute, which will specify the metadata class to associate with a data model class, and with that you can tell you partial SomeClass class to get that attributes from SomeClassMetadata class. To add new custom properties, you can use SomeClass partial class.
MSDN Link: MetadataTypeAttribute Class
Using EF 6, Lazy Loading Enabled is set to True in the model. Here's an example of my problem:
var agent = context.AgentDetail.Where(a => a.Agent.GroupCode == "1234");
Running that will return 5 results. If after that I run (for the purpose of testing only)
var code = agent.FirstOrDefault().Agent.GroupCode;
I get a null reference exception because Agent is null.
Here are my entities:
public partial class AgentDetail : Entity<int>
{
public Nullable<System.DateTime> Date { get; set; }
public string Name { get; set; }
public decimal Balance { get; set; }
...
public virtual Agent Agent { get; set; }
}
public partial class Agent : Entity<int>
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Agent()
{
this.AgentAspNetUsers = new HashSet<AgentAspNetUsers>();
this.AgentDetail = new HashSet<AgentDetail>();
}
public string GroupCode { get; set; }
...
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<AgentAspNetUsers> AgentAspNetUsers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<AgentDetail> AgentDetail { get; set; }
}
How could it give me 5 results in the first query, then? I can't figure out what's wrong here, any help would be appreciated.
From Requirements for Creating POCO Proxies
A custom data class must be declared with public access.
A custom data class must not be sealed
A custom data class must not be abstract
A custom data class must have a public or protected constructor that does not have parameters. Use a protected constructor without parameters if you want the CreateObject method to be used to create a proxy for the POCO entity. Calling the CreateObject method does not guarantee the creation of the proxy: the POCO class must follow the other requirements that are described in this topic.
The class cannot implement the IEntityWithChangeTracker or IEntityWithRelationships interfaces because the proxy classes implement these interfaces.
The ProxyCreationEnabled option must be set to true.
Each navigation property must be declared as public, virtual (Overridable in Visual Basic), and not sealed (NotOverridable in Visual Basic) get accessor. The navigation property defined in the custom data class must have a corresponding navigation property in the conceptual model. For more information, see Loading Related POCO Entities.
Check this points on your classes. In your pasted code AgentDetail havent public/protected constructor.
Try to define relationship between entities. It should work if your lazy loading has enabled.
Suppose, i have main class for data representation and this class have configuration field. This field must be able to answer some questions related to main class (assume, that this is one question - 'IsMainClassReadyToUse'). But inner structure of this class may be different.
Because of it, i want create abstract class Configurator and depending on situation use various Configuratos that implement its functional.
So, i have following code:
public class SimpleConfigurator : Configurator
{
public int FieldA { get; set; }
public override bool IsDataClassReadyToUse()
{
return ParentDataClass.FieldA == FieldA;
}
}
public class ComplexConfigurator : Configurator
{
public virtual List<int> FieldsB { get; set; }
public override bool IsDataClassReadyToUse()
{
return ParentDataClass.FieldsB.All(x => FieldsB.Any(y => y == x));
}
}
public abstract class Configurator
{
public int ConfiguratorId { get; set; }
public virtual DataClass ParentDataClass { get; set; }
public abstract bool IsDataClassReadyToUse();
}
public class DataClass
{
public int DataClassId { get; set; }
public virtual Configurator Configurator { get; set; }
public int FieldA { get; set; }
public virtual List<int> FieldsB { get; set; }
}
public class DataDbContext : DbContext
{
public DbSet<DataClass> DataClasses { get; set; }
}
But the problem appears when i try use DataClass instance with Configurator of type ComplexConfigurator.
Because of LazyLoading i need to load FieldsB from ComplexConfigurator, but abstract class Configurator doesn't contain such field and i can't write such code:
new DataDbContext().DataClasses
.Include(m => m.Configurator)
.Include(m => m.Configurator.FieldsB);
I tried to disable LazyLoading, adding such constructor in DataDbContext:
public DataDbContext()
{
Configuration.LazyLoadingEnabled = false;
}
But when i try get access to FieldsB it still be null.
So, how can i implement such architecture with Entity Framework?
Or maybe i should choose another architecture for such task?
I think you should try access you configurator such as
((ComplexConfigurator)yourObject.Configurator).FieldsB
But I'm afraid EF works wrong with List<int> property (when I tried do that sometimes I've got a fail) and better way is to create class Option and field List<Option> Options into your configurator instead of List with integers.
You also should check your DB scheme (there's should be a table "Configurators" with idenitifator field and all SimpleConfigurator and ComplexConfigurator's fields). May be you should add DbSet<Configurator> into your DbContext definition.
You can read this article for getting more information about inheritance and EF.
We have a base class called Base, and few inherited classes called Derived1 and Derived2. To place classes in separate tabels we can using attributes or fluentAPI, but in this case we have writing this all the time when defining new classes, but new inherited classes are frequent.
In the end, all work with base's successors must go through Bases of LolContext.
So, is it possible to make EF map all classes that inherits from Base at corresponding tabels automaticaly?
public class OneContext : DbContext
{
public DbSet<Base> Bases { get; set; }
public OneContext()
: base("OneDatabase")
{
}
}
public class Base
{
[Key]
public int Id { get; set; }
public int prop1 { get; set; }
}
public class Derived1 : Base
{
public int prop { get; set; }
}
public class Derived2 : Base
{
public int prop { get; set; }
}
#Update1
I will clarify my question.
Is there any method to put all 'Base' inherited classes in theirs names named tabels(or some kind of names or id) without specifying [Table("")] atribute for every new successor and defining new DbSet<> for every new successor in OneContext?
There is no way to configure EF to automatically use TPT over TPH. The only requirement for implementing TPT, however, is to explicitly set a table name for derived entity classes (which can be as simple as using the TableAttribute on the class definition). Creating a DbSet<T> property for the derived entity class is optional.