EF6, lazy loading not working as expected - c#

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.

Related

Entity Framework 6 doesn't detect inheritance

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.

Auto-generated class from EF6 to implement custom interface

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; }
}

Entity Framework on ASP .NET MVC – Extend Entity for Attributes, Methods and Properties

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

Cannot implicitly convert type 'System.Collections.Generic.List<MODEL#1>' to 'System.Collections.Generic.List<Model#2>

I'm using MVC5 with EF6 .I'm getting the below conversion Error
Cannot implicitly convert type
System.Collections.Generic.List<TreaceabilitySystem.GLB_M_PROFITCENTER>
to
System.Collections.Generic.List<TreaceabilitySystem.Models.Profitcenter>
private TSEntities db = new TSEntities();
// GET: Profitcenter
public ActionResult Index()
{
List<Profitcenter> profitcenter = new List<Profitcenter>();
profitcenter = db.GLB_M_PROFITCENTER.ToList(); //Error coming up here
return View(profitcenter.ToList());
}
My models are here:
This Model created through EF when i add table in .edmx
public partial class GLB_M_PROFITCENTER
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public GLB_M_PROFITCENTER()
{
this.GLB_M_USERMASTER = new HashSet<GLB_M_USERMASTER>();
}
public string PROFITCENTER_CODE { get; set; }
public string PROFITCENTER_NAME { get; set; }
public string DESCRIPTION { get; set; }
public bool ISACTIVE { get; set; }
public int CREATEDBY { get; set; }
public System.DateTime CREATED_DATE { get; set; }
public Nullable<int> UPDATEDBY { get; set; }
public Nullable<System.DateTime> UPDATED_DATETIME { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<GLB_M_USERMASTER> GLB_M_USERMASTER { get; set; }
}
And I have created the below model for change the display name and validation purposes
[MetadataType(typeof(Profitcenter))]
public partial class GLB_M_PROFITCENTER { }
public class Profitcenter
{
[Required(ErrorMessage = "*")]
[DisplayName("Profitcenter Code")]
public string PROFITCENTER_CODE { get; set; }
[Required(ErrorMessage = "*")]
[DisplayName("Profitcenter Name")]
public string PROFITCENTER_NAME { get; set; }
[DisplayName("Description")]
public string DESCRIPTION { get; set; }
[DisplayName("Is Active")]
public bool ISACTIVE { get; set; }
[DisplayName("Created By")]
public int CREATEDBY { get; set; }
[DisplayName("Created Timestamp")]
public System.DateTime CREATED_DATE { get; set; }
[DisplayName("Upated by")]
public Nullable<int> UPDATEDBY { get; set; }
[DisplayName("Updated Timestamp")]
public DateTime UPDATED_DATETIME
{
get; set;
}
}
both models are exactly same , Am I missing anything ?
How do I fix this?
both models are exactly same
That doesn't mean you can just assign the one to the other. For this code to work:
Foo foo = new Foo();
Bar bar = foo;
Bar must be a base type of Foo. This isn't the case here, both your Bar and Foo just happen to have the same property names.
You need to map from one to the other:
public Profitcenter Map(GLB_M_PROFITCENTER input)
{
return new Profitcenter
{
PROFITCENTER_CODE = input.PROFITCENTER_CODE,
...
};
}
You can do the mapping of the entire list with Select():
List<Profitcenter> profitcenter = new List<Profitcenter>();
profitcenter = db.GLB_M_PROFITCENTER.Select(Map).ToList();
An automated way of doing this could be using AutoMapper, which works especially well if all properties on both sides are named identically.
That only answers your question partially though. You have two types: GLB_M_PROFITCENTER, an Entity Framework-generated class that represents a database table, and Profitcenter, where you have added attributes that can be used for input validation using the MetadataType attribute.
I'm not a fan of the latter, because you're then using Entity Framework models as viewmodels for your UI layer. You shouldn't, and you can just remove the MetadataType attribute from the partial class definition.
So you can either use the MetadataType, but then never really instantiate that type (after all, it is a metadata type):
List<GLB_M_PROFITCENTER> profitcenter = db.GLB_M_PROFITCENTER.ToList();
return View(profitcenter);
And make your view #model IEnumerable<GLB_M_PROFITCENTER>. Then MVC will read the MetadataType attribute for GLB_M_PROFITCENTER, and apply the metadata (DisplayName, ...) as applied to Profitcenter (but you shouldn't).
Or you can simply apply mapping, thereby decoupling your view model from your entity model (and thus your database), with all additional benefits.
Some programming languages, unlike C# and other C-like languages, allow for what is called "duck typing", which would let you assign from different types if they both "quack the same way".
In C#, however, you can only assign an instance of a class to a variable of the same type, or of a base type (a class which your class extends, or an interface which it implements). Even if you had two classes which looked exactly the same, you wouldn't be able to assign from one of them to the other. .NET prior to version 4.0 didn't even support proper generic covariance and contravariance, meaning you couldn't even assign a IEnumerable<Tderived> to IEnumerable<Tbase> even if Tderived is derived from Tbase.
The solution could be to:
use a tool which will map from one class to the other (i.e. copy between equally named properties), like AutoMapper, or
redesign your app to have a separate assembly which contains common entities to be shared between other assemblies type (not a bad idea either), or
extract an interface so that you can assign to this base interface.
It is not uncommon to use mapping to resolve this issue, since you often want to have plain data transfer objects for moving data between tiers, so using an automated tool for this is ok, but if you can keep all entities in a separate assembly which is referenced by both DAL and business layer, but doesn't know anything about them, then it's an even better approach because it avoids any runtime mapping issues.
GLB_M_PROFITCENTER and Profitcenter are not same types, you just share metadata for sharing of attributes from viewmodel to entity model. You should use linq projection for conversion of one type to other
db.GLB_M_PROFITCENTER.select(e => new Profitcenter() {
/* props mapping*/
}).ToList()
you can also use mapping engine for example AutoMapper
If the member names are the same - use auto mapper - it will automatically convert each type.
Mapper.CreateMap<SourceType, DestinationType>()
Then you can call
Mapper.Map<DestinationType>(instanceofSourceType);

Attempted Serialization Of ICollection even with IgnoreDataMember and JsonIgnore

I am having some issues avoiding serialization of certain ICollection properties in Web Api. It seems the common suggestion to avoiding serialization is to add IgnoreDataMember or JsonIgnore. I don't want to serialize the one-to-many and one-to-one properties.
I have the following model:
public class Player
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order=0)]
public Guid PlayerId { get; set; }
[IgnoreDataMember]
public virtual ICollection<Player> Friends { get; set; }
[IgnoreDataMember]
[Required]
public string Password { get; set; }
[MaxLength(100)]
[Index(IsUnique = true)]
public string Username { get; set; }
[IgnoreDataMember]
public virtual ICollection<WordChallenge> IssuedChallenges { get; set; }
[IgnoreDataMember]
public virtual ICollection<WordChallenge> ReceivedChallenges { get; set; }
}
However when I do a POST to the endpoint I'm getting the following exception:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
In this case it should only be attempting to serialize PlayerId and Username
Since it was requested, the following manages the dbContext:
public async Task<IEnumerable<Player>> GetFriends(Guid playerId)
{
//Handles common exceptions and manages the dbcontext. In this case context is disposed off after the interaction is done.
return await DbInteraction(
async dbModel =>
{
var player = await GetPlayerById(playerId, dbModel);
return player.Friends.ToList();
});
}
Controller:
// GET: api/Friend/5
public async Task<ReturnObject<IEnumerable<Player>>> Get(Guid token, Guid id)
{
var playerService = new PlayerService(base._wordModelFactory);
var fields = await playerService.GetFriends(id);
return ReturnData(fields);
}
Instead of marking members you don't want to be serialized with IgnoreDataMember, in your case (and in general) it's better to mark class with DataContact and mark members you want to be serialized with DataMember.
Still you might wonder why you observe such behavior with IgnoreDataMember and navigation properties. My guess would be - to support lazy loading when you access navigation property, EF might create dynamic proxy class for your POCO class. Note that your navigation properties are marked with "virtual". That is for EF to be able to override them in that dynamic proxy class and add lazy loading behavior. IgnoreDataMember attribute is not inherited, and so in that inherited proxy class navigation properties are not longer marked with it, and so serializer will try to include them.

Categories

Resources