I have a DbContext with ProxyCreationEnabled set to true (actually it's the default value).
As far as I remember, this enables EF to load proxy entities from database, so any change we make to properties are recognized by the change tracker, and we can call SaveChanges() like this:
using (var db = new MyDbContext())
{
var people = db.People.Where(p => p.Status = PersonStatus.New).ToList();
foreach (var person in people)
{
person.Name = "Something";
}
db.SaveChanges();
}
The problem is: why would EF not use the proxy for a specific class, even though ProxyCreationEnabled is true? The class is not sealed, so it should be able to use proxy.
Here is my sample class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime RegisterDate { get; set; }
public PersonStatus Status { get; set; }
}
To generate proxy for property it should be virtual
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime RegisterDate { get; set; }
public virtual PersonStatus Status { get; set; }
}
To get change tracking proxies, the
basic rule is that your class must be
public, non-abstract or non-sealed.
Your class must also implement public
virtual getters/setters for all
properties that are persisted.
Finally, you must declare collection
based relationship navigation
properties as ICollection<T> only.
They cannot be a concrete
implementation or another interface
that derives from ICollection<T> (a
difference from the Deferred Loading
proxy)
Related
I need to audit data in some tables in my server database so my clients can take partial updates (by table/entity). Data in the server database is only edited from the server website.
The audits will be requested by a client: WHERE Id > [Clients Last Id], the server will then do some processing and then return the latest audits to keep themselves up to date.
I can't seem to get to a generic pattern that will work across the board for all of my models:
public class Domain {
public int Id { get; set; }
public string Property1 { get; set; }
public int Property2 { get; set; }
}
Then I think I want to be able to do something like so:
public class DomainContext : DbContext {
public DbSet<Domain> Domain { get; set; }
public DbSet<History<Domain>> DomainHistory { get; set; }
}
This is my problem class taking this route, I want to inherit from Domain so things like property changes and EF migrations (in code first) will 'just work'. But I Cannot derive from 'T' because it is a type parameter
public class History<T>
: T //Cannot derive from 'T' because it is a type parameter
where T : class {
public DateTime CreatedOn { get; set; }
public int CreatedByUserId { get; set; }
public int EntityFK { get; set; }
// This will always be the current version
//public T Entity { get; set; }
// I could store a snapshot of the state at the time of the audit
public string XMLData { get; set; }
}
I don't know if my use of generics is warranted here but I'm basically trying to get to a point where I can do the below so my models play nicely with EF Migrations:
Domain d = GetDomainModel();
History<Domain> dh = new History<Domain>();
dh.Property1 = d.Property1;
dh.Property2 = d.Property2;
How can this be done?
For a basic audit of your entities you can use a base class and intercept the changue type in a override of savechangues in context, like this (sorry of format, i write in mobile):
public class AuditBase
{
//Adapt your requirements, the propertys are exists in db
public datetime creationdate { get; set; }
public datetime modificationdate { get; set; }
public string creationuser { get; set; }
public string modificationuser { get; set; }
}
public class ModelBBDD : AuditBase
{ }
You can override the SaveChanges method of Context. In the method, you can through the ChangueTracker property of Database Class for added or updates dto's, like this:
var dto = entity as auditbase;
if (dto == null) continue;
if (dto.state == entitystate.added)
{
((Auditbase)entity).creationdate = datetime.now;
((Auditbase)entity).creationuser = environment.username;
}
else if( dto.state == entitystate.modified)
...
...
If you can log all the changues of properties, you can trough all the properties of dto with reflection and type in SaveChanges, and save values in log.
Consider this entity:
public class CondRule
{
public virtual decimal Id { get; set; }
public virtual string Name { get; set; }
public virtual CondRuleType RuleType { get; set; }
public virtual string Statement { get; set; }
}
and CondRuleType is:
public class CondRuleType
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
It is obvious that there is a one to one relation between CondRule and CondRuleType entities.
Also I have CondRuleDto:
public class CondRuleDto
{
public decimal Id { get; set; }
public string Name { get; set; }
public CondRuleType RuleType { get; set; }
}
I have mapped CondRule to CondRuleDto using AutoMapper:
Mapper.CreateMap<CondRule, CondRuleDto>();
When I call Session.Get to get CondRule by id and the map the result to CondRuleDto, AutoMapper does not resolve proxies (here RuleType).
Here is my code:
var condRule = Session.Get<CondRule>(id);
var condRuleDto = Mapper.Map<CondRuleDto>(condRule);
When I watch condRuleDto, RuleType property is a NHibernate proxy. I want AutoMapper to map RuleType proxy to a POCO. How to make this work?
PS: I have to mention that when I use query and use automapper's Project, it will result a list with no proxies (I know that Project make this happen. May be I need something like Project to use after Session.Get):
Session.Query<CondRule>().Project().To<CondRuleDto>().ToList()
Casts won't change the underlying object (i.e. your CondRuleType will be still a proxy even if you map its instance to another property of type CondRuleType).
It seems like you need to create a custom mapping where CondRule.RuleType is mapped creating a new instance of CondRuleType.
In one of my projects I use Entity Framework with virtual navigation properties on the entities. This means the entities are loaded from the database or created with IDbSet<T>.Create() a DynamicProxy is returned. Because I only make navigation properties virtual, this proxy does lazy loading and no change tracking (all properties need to be virtual to get a change tracking proxy).
My assumption was that the DynamicProxy takes care of initializing virtual ICollection<T> properties, as it does when the entity is loaded from the database. But when I create a new entity using IDbSet<T>.Create(), these navigation properties remain null.
Then I tried to make all properties virtual so I get a DynamicProxy with change tracking and to my surprise these navigation properties are initialized.
See the following example:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
static class Program
{
static void Main()
{
using (var db = new BloggingContext())
{
var changeTrackingBlog = db.ChangeTrackingBlogs
.Create(); // returns a DynamicProxy
var changeTrackingBlogPostCount = changeTrackingBlog
.Posts
.Count; // Posts has type EntityCollection<Post>
var lazyLoadingBlog = db.LazyLoadingBlogs
.Create(); // returns a DynamicProxy
var lazyLoadingBlogPostCount = lazyLoadingBlog.Posts
.Count; // Posts == null
}
}
}
public class BloggingContext : DbContext
{
public IDbSet<Post> Posts { get; set; }
public IDbSet<ChangeTrackingBlog> ChangeTrackingBlogs { get; set; }
public IDbSet<LazyLoadingBlog> LazyLoadingBlogs { get; set; }
}
public class Post
{
[Key]
public int PostId { get; set; }
public virtual ChangeTrackingBlog ChangeTrackingBlog { get; set; }
public virtual LazyLoadingBlog LazyLoadingBlog { get; set; }
}
public class ChangeTrackingBlog
{
[Key]
public virtual int BlogId { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class LazyLoadingBlog
{
// Not all properties are virtual, so no Change tracking, just lazy loading
[Key]
public int BlogId { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
I hope someone can explain what's happening here.
The usual way to avoid the null reference is to initialize the collection in the constructor:
public LazyLoading()
{
Posts = new List();
}
I believe it may be better practise to use a backing field - something to do with anonymous constructors not being called in certain circumstances (serialization or something - apologies for vagueness). So I do this:
public class LazyLoadingBlog
{
private ICollection<Post> _Posts = new List<Post>();
public virtual ICollection<Post> Posts
{
get { return _Posts ; }
//protected set lets EF override for lazy loading
protected set { _Posts = value;
}
}
Unfortunately I can't explain why you don't get an error when you mark all the properties virtual...
I'm creating a EF5 entity model with the designer (VS2012), and used the EF5 DbContext generator as code generation item.
My model contains an entity deriving from another (not abstract).
So let's say the base entity is called BaseEntity, and the derived entity is DerivedEntity.
Now I see in the generated context class, that there is no
Public DbSet<DerivedEntity> DerivedEntities { get; set; }
defined.
Only
Public DbSet<BaseEntity> BaseEntities { get; set; }
is defined.
Is this normal ? And if yes, how do I query the derived entities in linq ?
I'm used to query like this:
using(var ctx = new EntityContainer)
{
var q = from e in ctx.DerivedEntities <-- but this is now not possible since it doesn't exist
select e;
return q.ToList();
}
Thanks for replying.
EDIT:
As requested, generated classes posted:
public partial class Scheduling
{
public int Id { get; set; }
public string Subject { get; set; }
public System.DateTime BeginDate { get; set; }
public System.DateTime EndDate { get; set; }
}
public partial class TeamScheduling : Scheduling
{
public int TeamId { get; set; }
public Nullable<int> AssignmentId { get; set; }
public virtual Team Team { get; set; }
public virtual Assignment Assignment { get; set; }
}
public partial class EntityContainer : DbContext
{
public EntityContainer()
: base("name=EntityContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Team> Teams { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<AssignmentPreference> AssignmentPreferences { get; set; }
public DbSet<Scheduling> Schedulings { get; set; }
}
As you see, the EntityContainer class does not contain
public DbSet<TeamScheduling> TeamSchedulings { get; set; }
This is expected when you use inheritance the way you have. context.Schedulings contains both Scheduling objects and TeamScheduling objects. You can get the TeamScheduling objects only by asking for context.Schedulings.OfType<TeamScheduling>(). Note that you cannot meaningfully use context.Schedulings.OfType<Scheduling>() to get the others: that will also include the TeamScheduling objects.
You could alternatively try context.Set<TeamScheduling>(), but I'm not entirely sure that will work.
If your intention is to have two tables come up, say a parent Scheduling entity as well as a child TeamScheduling entity that has a foreign key back to the Scheduling entity, consider using a Table-per-Type (TPT) mapping as discussed here.
In essence, you should modify your "OnModelCreating" method to have the following code:
modelBuilder.Entity<TeamScheduling>().ToTable("TeamScheduling");
This explicitly tells EF that you want to have the TeamScheduling subclass to be represented as its own table. Querying it via LINQ would be simple as you would be able to do something like the following:
var teamScheds = context.Set<TeamScheduling>().Where(s => s.Id == 1).FirstOrDefault();
I have the following class
class MCustomer : DomanEntity
{
public MCustomer()
{
}
public virtual iCustomerEntity CustomerDetials { get; set; }
public virtual SolicitationPreferences SolicitationPreferences { get; set; }
}
public interface iCustomerEntity
{
Contact Contact { get; set; }
}
public class PersonEntity: DomanEntity, iCustomerEntity
{
public PersonEntity()
{
Intrests = new List<Intrest>();
Children = new List<PersonEntity>();
}
public virtual Contact Contact { get; set; }
public virtual DateTime BirthDate { get; set; }
public virtual IList<Intrest> Intrests { get; set; }
public virtual PersonEntity Spouse { get; set; }
public virtual IList<PersonEntity> Children { get; set; }
}
When I use fluent NHibernate AutoMapping I receive this error:
NHibernate.MappingException: An association from the table MCustomer refers to an unmapped class: Calyx.Core.Domain.CRM.iCustomerEntity
How do I set up a property in my domain model that has an Interface type?
I don't think, that you can do that.
When you would try to load your MCustomer (session.Load<MCustomer>(id)), NHibernate would only know, that you want to get MCustomer, that has an iCustomerEntity. It would not know which implementation (PersonEntity or CoderEntity?) to use. How would it know which mapping to use to retrieve the data for iCustomerEntity?
https://www.hibernate.org/hib_docs/nhibernate/html/inheritance.html
Its a standard Nhibernate pattern. I'm trying to do the same thing
Looks like a "any" mapping to me. You should look into that. And as far as I can see FNH does not support that yet.