I have a POCO domain model which is wired up to the entity framework using the new ObjectContext class.
public class Product
{
private ICollection<Photo> _photos;
public Product()
{
_photos = new Collection<Photo>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Photo> Photos
{
get
{
return _photos;
}
}
public void AddPhoto(Photo photo)
{
//Some biz logic
//...
_photos.Add(photo);
}
}
In the above example i have set the Photos collection type to IEnumerable as this will make it read only. The only way to add/remove photos is through the public methods.
The problem with this is that the Entity Framework cannot load the Photo entities into the IEnumerable collection as it's not of type ICollection.
By changing the type to ICollection will allow callers to call the Add mentod on the collection itself which is not good.
What are my options?
Edit:
I could refactor the code so it does not expose a public property for Photos:
public class Product
{
public Product()
{
Photos = new Collection<Photo>();
}
public int Id { get; set; }
public string Name { get; set; }
private Collection<Photo> Photos {get; set; }
public IEnumerable<Photo> GetPhotos()
{
return Photos;
}
public void AddPhoto(Photo photo)
{
//Some biz logic
//...
Photos.Add(photo);
}
}
And use the GetPhotos() to return the collection. The other problem with the approach is that I will loose the change tracking abilities as I cannot mark the collection as Virtual - It is not possible to mark a property as private virtual.
In NHibernate I believe it's possible to map the proxy class to the private collection via configuration. I hope that this will become a feature of EF4. Currently i don't like the inability to have any control over the collection!
The way to do this is to have a protected virtual property which is mapped in your model and a public property that returns an IEnumerable.
public class Product
{
public Product()
{
PhotoCollection = new Collcation<Photo>();
}
public int Id { get; set; }
public string Name { get; set; }
protected virtual ICollection<Photo> PhotoCollection {get; set; }
public IEnumerable<Photo> Photos
{
get { return PhotoCollection ; }
}
public void AddPhoto(Photo photo)
{
//Some biz logic
//...
PhotoCollection .Add(photo);
}
}
Anton, it would help me understand your problem more if you can explain why is it that you do not want developers to access the Add method of your collection. Is this because the list is strictly read-only, or is it because you want to run some custom business logic when a new entity is added?
Anyway... I am going to assume that you are trying to do the latter (i.e. run custom business logic when the collection is modified). I have done a similar solution on a project of mine, and the idea is as follows:
The TT template that produces POCOs in EF4 creates all collections as TrackableCollection lists. This class has an event called 'CollectionChanged' which you can subscribe to and listen to any changes to your collection.
So you can do something as follows:
public class Product
{
public Product()
{
Photos.CollectionChanged += ListCollectionChanged;
}
public int Id { get; set; }
public string Name { get; set; }
public TrackableCollection<Photo> Photos
{
get
{
// default code generated by EF4 TT
}
set
{
// default code generated by EF4 TT
}
}
private void ListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
// A new item has been added to collection
case NotifyCollectionChangedAction.Add:
{
T newItem = (T) e.NewItems[0];
// Run custom business logic
}
break;
// An existing item has been removed
case NotifyCollectionChangedAction.Remove:
{
T oldItem = (T) e.OldItems[0];
// Run custom business logic
}
break;
}
}
}
The nice thing about the above solution is that you still use your Product entity in an 'EF' manner... were any developer in your team can simply access a property of the entity directory and need run an explicit hard typed function.
Bit late to the party but this is what Observable objects are for. Allow the data structure to do what it does best. Use ObservableCollection as your field type if you don't want to build your own collection that does what you need and expose the regular ICollection type from your property. You can run any logic in the parent entity you need when the related entities in the collection change via the CollectionChanged event. If you need to selectively enable or disable modifications it's easy enough to extend an existing collection type or write a proxy collection that allows a call to a method to toggle the mutability of the collection (ISupportInitialize can be used to good effect for representing this ability BTW).
(Apologies for my initial post brevity - I was answering from my phone)
You can construct your collection through a LINQ query over an EF entity set. However, you keep the resulting collection as internal data member to your business class and expose the IEnumerable<Photo> returned by calling AsEnumerable() on the entity set as a result of the public photo.
You could cache the IEnumerable<Photos> internally as well, so that you don't call AsEnumerable() every time your caller asks for the collection. Of course, that means that if the user needs to update the collection through your public methods, you might have to refresh the cached IEnumerable. This might pose small issue if the caller has also cached the pointer to the previous IEnumerable.
Alternatively, if your caller will always work with the full entity set, the EntitySet class (of which all your EF sets will inherit) implements IEnumerable<TEntity>, so you can directly return the entity set to your caller.
Note that if you want the loading of the collection from an EF entity set to happen outside of the scope of your business class, you can make a constructor on your class that takes an ICollection. This way, once you create your object, the collection is sealed in it, and exposed only as an IEnumerable.
Why not try the following and leave use properties?
private ICollection<Photo> photos{get; set;}
public IEnumerable<Photo> Photos
{
get {return (IEnumberable<Photo>)photos;}
}
Alternatively you could use the decorator pattern to encapsulate the class into one which the collection can't be directly modified.
Related
This is a long one.
So, I have a model and a viewmodel that I'm updating from an AJAX request. Web API controller receives the viewmodel, which I then update the existing model using AutoMapper like below:
private User updateUser(UserViewModel entityVm)
{
User existingEntity = db.Users.Find(entityVm.Id);
db.Entry(existingEntity).Collection(x => x.UserPreferences).Load();
Mapper.Map<UserViewModel, User>(entityVm, existingEntity);
db.Entry(existingEntity).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch
{
throw new DbUpdateException();
}
return existingEntity;
}
I have automapper configured like so for the User -> UserViewModel (and back) mapping.
Mapper.CreateMap<User, UserViewModel>().ReverseMap();
(Note that explicitly setting the opposite map and omitting the ReverseMap exhibits the same behavior)
I'm having an issue with a member of the Model/ViewModel that is an ICollection of a different object:
[DataContract]
public class UserViewModel
{
...
[DataMember]
public virtual ICollection<UserPreferenceViewModel> UserPreferences { get; set; }
}
The corresponding model is like such:
public class User
{
...
public virtual ICollection<UserPreference> UserPreferences { get; set; }
}
The Problem:
Every property of the User and UserViewModel classes maps correctly, except for the ICollections of UserPreferences/UserPreferenceViewModels shown above. When these collections map from the ViewModel to the Model, rather than map properties, a new instance of a UserPreference object is created from the ViewModel, rather than update the existing object with the ViewModel properties.
Model:
public class UserPreference
{
[Key]
public int Id { get; set; }
public DateTime DateCreated { get; set; }
[ForeignKey("CreatedBy")]
public int? CreatedBy_Id { get; set; }
public User CreatedBy { get; set; }
[ForeignKey("User")]
public int User_Id { get; set; }
public User User { get; set; }
[MaxLength(50)]
public string Key { get; set; }
public string Value { get; set; }
}
And the corresponding ViewModel
public class UserPreferenceViewModel
{
[DataMember]
public int Id { get; set; }
[DataMember]
[MaxLength(50)]
public string Key { get; set; }
[DataMember]
public string Value { get; set; }
}
And automapper configuration:
Mapper.CreateMap<UserPreference, UserPreferenceViewModel>().ReverseMap();
//also tried explicitly stating map with ignore attributes like so(to no avail):
Mapper.CreateMap<UserPreferenceViewModel, UserPreference>().ForMember(dest => dest.DateCreated, opts => opts.Ignore());
When mapping a UserViewModel entity to a User, the ICollection of UserPreferenceViewModels is also mapped the User's ICollection of UserPreferences, as it should.
However, when this occurs, the individual UserPreference object's properties such as "DateCreated", "CreatedBy_Id", and "User_Id" get nulled as if a new object is created rather than the individual properties being copied.
This is further shown as evidence as when mapping a UserViewModel that has only 1 UserPreference object in the collection, when inspecting the DbContext, there are two local UserPreference objects after the map statement. One that appears to be a new object created from the ViewModel, and one that is the original from the existing model.
How can I make automapper update an existing Model's collection;s members, rather than instantiate new members from the ViewModel's collection? What am I doing wrong here?
Screenshots to demonstrate before/after Mapper.Map()
This is a limitation of AutoMapper as far as I'm aware. It's helpful to keep in mind that while the library is popularly used to map to/from view models and entities, it's a generic library for mapping any class to any other class, and as such, doesn't take into account all the eccentricities of an ORM like Entity Framework.
So, here's the explanation of what's happening. When you map a collection to another collection with AutoMapper, you are literally mapping the collection, not the values from the items in that collection to items in a similar collection. In retrospect, this makes sense because AutoMapper has no reliable and independent way to ascertain how it should line up one individual item in a collection to another: by id? which property is the id? maybe the names should match?
So, what's happening is that the original collection on your entity is entirely replaced with a brand new collection composed of brand new item instances. In many situations, this wouldn't be a problem, but when you combine that with the change tracking in Entity Framework, you've now signaled that the entire original collection should be removed and replaced with a brand new set of entities. Obviously, that's not what you want.
So, how to solve this? Well, unfortunately, it's a bit of a pain. The first step is to tell AutoMapper to ignore the collection completely when mapping:
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.UserPreferences, opts => opts.Ignore());
Notice that I broke this up into two maps. You don't need to ignore the collection when mapping to your view model. That won't cause any problems because EF isn't tracking that. It only matters when you're mapping back to your entity class.
But, now you're not mapping that collection at all, so how do you get the values back on to the items? Unfortunately, it's a manual process:
foreach (var pref in model.UserPreferences)
{
var existingPref = user.UserPreferences.SingleOrDefault(m => m.Id == pref.Id);
if (existingPref == null) // new item
{
user.UserPreferences.Add(Mapper.Map<UserPreference>(pref));
}
else // existing item
{
Mapper.Map(pref, existingPref);
}
}
In the meantime there exists an AutoMapper Extension for that particular problem:
cfg.AddCollectionMappers();
cfg.CreateMap<S, D>().EqualityComparison((s, d) => s.ID == d.ID);
With AutoMapper.EF6/EFCore you can also auto generate all equality comparisons. Plaese see AutoMapper.Collection AutoMapper.EF6 or AutoMapper.Collection.EFCore
According to the AutoMapper source file that handles all ICollection (among other things) and the ICollection Mapper:
The collection is cleared by a call to Clear() then added again, so as far as I can see there is no way that AutoMapper will be able to automagically do the mapping this time.
I would implement some logic to loop over the collections and AutoMapper.Map the ones that are the same
I'm attempting to separate my DbContext from a winforms application that I'm currently using to better support a multi-user environment as well as an upcoming website. After doing a bit of research I've going with implementing a data access layer (DAL) for the winforms app/website to connect to and having the end-users work with disconnected entities. My question is regarding the best way I would go about saving updates to my entities when one of the entities in a child collection has been updated.
For instance, if I have the following structure (simplified)
public class Company
{
public int CompanyID { get; set; }
public string CompanyName { get; set; }
public ICollection<Employee> Employees { get; set; } // Non-virtual as we aren't lazy-loading
}
public class Employee
{
public int CompanyID { get; set; }
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Claim> Claims { get; set; }
}
public class Claim
{
public DateTime ClaimDate { get; set; }
public ICollection Documentation { get; set; }
}
public class Document
{
public byte[] DocumentImage { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
}
Inside the winforms application, I have multiple Binding Source's set-up to display the employee's information
For Example:
employeeBinding.DataSource = typeof(Employee); // Eventually set to an IEnumerable<Employee>
claimBinding.DataSource = employeeBinding;
claimBinding.DataMember = "Claims";
documentationBinding.DataSource = claimBinding;
documentationBinding.DataMember = "Documentation";
However, by setting things up like this I'm unable to make calls on the "CurrentChanged" event of each binding source to save each entity since it has changed (unless I have references stored to the previous entity inside the form). So what I have thought to do was something similar to below in the DAL and iterate through each of the child collections.
public void UpdateEmployee(Employee employee)
{
using (myContext context = new myContext())
{
context.Employees.Attach(employee);
context.Entry<Employee>(employee).State = EntityState.Modified;
foreach(var claim in employee.Claims)
{
context.Entry<Claim>(claim).State = EntityState.Modified;
foreach(var doc in claim.Documentation)
{
context.Entry<Document>(doc).State = EntityState.Modified;
}
}
context.SaveChanges();
}
}
However, I feel that this route can get ugly quick with some more complex entities and relationships. Could someone help point me to the best route to handle this or should I have references to the current entities in the code so when the "CurrentChanged" event fires I can just update each individual entity?
Thank you very much.
When you work with Entity Framework you have the ChangeTracker, even if you are using this "Disconected entities" you can have the ChangeTracker tracking the entities, to have this you just need to attach them to the context and before you call the SaveChanges you call .DetectCHanges() You dont really need to have this specific code, you can use generics for this:
public void Update<TEntity>(TEntity entity)
{
using (myContext context = new myContext())
{
context.Set<TEntity>.Attach(entity);
context.ChangeTracker.DetectChanges();
context.SaveChanges();
}
}
the call to the method would be:
Update<Employee>(employees);
Also i think is better for you to use a BindingSouce as the DataSource, and set the DataSource of the BindingSource as a List instead of typeof(Employee)
I could be wrong but I don't believe DetectChanges will be able to determine that there have been changes made to a disconnected entity. When the entity is attached, it will have an EntityState of "Unchanged" so wouldn't the DbContext do nothing with it until you mark it's state as "Modified". Also, as indicated in the following URL, "DetectChanges" is called for a number of methods (including "Attach") anyways and the explicit call would not be needed.
http://msdn.microsoft.com/en-us/data/jj556205.aspx
As for the BindingSource, I was illustrating that that BindingSource will be set to typeof(Employee) as if I was setting up my code in the constructor before the load events where I would actually get my data and set it's datasource to an IEnumerable from the DAL call. If I didn't do this, I would run into issues when attempting to bind to the "DataMember" properties as the other BindingSources wouldn't be able to find the properties indicated.
I don't believe that the code you provided as a sample fixes the issue I'm running into regarding child collections being updated. When testing with LinqPad they'll be updated if the parent entity has changed as well, but not if there have been zero changes to the parent. That's why I was iterating through all child collections and marking them as "Modified".
First, a little background. I'm developing a REST API using ASP.NET Web API and Entity Framework 5 however the requirements of the system are such that several layers of logic sit between my ApiControllers and my DbContext. These layers of logic involve detaching my entities from the DbContext, applying sets of hypothetical changes to the entities in memory (a process I'm calling materialization of a change set) then allowing users to inspect the new state of the system should these changes get applied. The new state of the entities is not saved to the database immediately. Instead, the materialization is held in memory on the web server and users can inspect either the current data or one of the many materialization of a variety of change sets.
Now for my problem.
public interface IIdentifiable
{
long Id { get; set; }
}
public class Foo : IIdentifiable
{
public long Id { get; set; }
public string Name { get; set; }
public List<Bar> Bars { get; set; } // Navigation Property
}
public class Bar : IIdentifiable
{
public long Id { get; set; }
public string Name { get; set; }
public long FooId { get; set; } // Foreign Key Property
public Foo Foo { get; set; } // Navigation Property
}
public class Materialization
{
IEnumerable<Foo> Foos { get; set; }
IEnumerable<Bar> Bars { get; set; }
}
public interface IRepository<TItem> : IQueryable<TItem>, ICollection<TItem>, IDisposable
where TItem : class, IIdentifiable
{
IRepository<TItem> Include<TProperty>(Expression<Func<TItem, TProperty>> path);
// Other methods
}
public class MateriailizationRepository<TItem> : IRepository<TItem>
where TItem : class, IIdentifiable
{
private Materialization _materialization;
public MateriailizationRepository(Materialization materialization)
{
_materialization = materialization;
}
public IRepository<TItem> Include<TProperty>(Expression<Func<TItem, TProperty>> path)
{
// Populate navigation property indicated by "path"
}
// Other methods
}
Each Bar has a foreign key property indicating the Foo it belongs to but the Bar.Foo and Foo.Bars navigation properties are not populated as this would complicate with the materialization process. Hence, after materialization has completed, Materialization.Foos and Materialization.Bars contain collections of objects that refer to each other by foreign key properties but not by navigation properties (i.e. the values of all navigation properties are null or empty List<T>s). I want to be able to do something like the following in my ApiController.
public IQueryable<Foo> Get (bool includeBars = false)
{
Materialization materialization;
// Materialize
using (IRepository<Foo> repository = new MateriailizationRepository<Foo>(materialization))
{
IRepository<Foo> query = repository;
if (includeBars)
query = query.Include(f => f.Bars);
return query;
}
}
MateriailizationRepository<Foo>'s primary responsibility is to fetch materialized Foo objects but since it has a reference to the entire Materialization I would like to be able to include materialized Bar objects from Materiailization.Bars on demand.
How would I go about implementing MateriailizationRepository.Include() to mimic the IQueryable.Include() extension method?
Here are a couple of options:
Look at using another context to implement your MaterializationRepositories and have it backed by an in memory database such as Effort, if that's still working nowadays.
Re-implement the 'Include' functionality yourself on the Materialization. The Expression can be broken down to find the type of the navigation property. Using naming conventions you can work out what foreign key property you need to interrogate to get the correct identifier. To find the target repository you could use reflection over the Materialization looking for the public property of type IEnumerable of the type of the navigation property. As long as you knew the name of the primary key of the target entity (by convention, say) you could then use the foreign key value to find it.
If you have a small number of entity types you'd probably be better off having some kind of switch statement and do some of it manually rather than via reflection.
Apologies that this isn't a fully worked through implementation, but I hope it leads in the right direction.
From what I have read POCO classes should be persistence ignorant and should not contain references to repositories.
Q1. Given the above, how would I populate the QuestionBlocks collection? I have read that POCO's should contain behavior so you don't end of with an anemic model, so I'm kind of confused as how one is supposed to do that without persistence. If that's the case then what kind of behavior would you put in a POCO?
Ex:
public class Survey
{
public int SurveyId { get; set; }
public string Title { get; set; }
public int BrandId { get; set; }
public DateTime Created { get; set; }
public List<SurveyQuestionBlock> QuestionBlocks { get; set; }
[ResultColumn]
public string Name { get; set; }
/// <summary>
/// Constructor
/// </summary>
public Survey()
{
Created = DateTime.Now;
QuestionBlocks = new List<SurveyQuestionBlock>();
}
}
I would append another view: POCO states for objects which are not dependent on any framework. The wiki definition of a POJO is much more meaningful to me then the one for POCO:
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
To paraphrase the wiki definition of the POJO, we can say that POCO object might not be forced to:
I. Extend prespecified class:
public class MyClass : AnyFramework.ObjectBase {...
II. Implement prespecified interfaces
public class MyClass : AnyFramework.IHaveDependency {...
III. Contain prespecified attribute
[AnyFramework.KeyAttribute]
public class MyClass {...
Given this (almost anything else is allowed) in the meaning of taking care about the object state. Other words, if object will check Business logic, it is correct.
But any POCO object can be used in a framework. Today it is mostly for ORM which is responsible for persistence. All application tiers are working with POCO objects, while data layer is responsible for loading and persisting (CRUD). This is mostly done via Proxies of these POCO objects.
So, POCO could be used as full business object, which can take care about itself (check correctness of collection items, properties...). This makes it different from DTO
Given the above, how would I populate the QuestionBlocks collection?
When reading from a database, the persistence infrastructure should populate the QuestionBlocks collection - reconstitution. Reconstruction should not invoke behavior, it should only set appropriate fields on the POCO. This is the responsibility of the repository. A repository is typically referenced from an application service, which sets up the stage for invoking entity behavior.
If that's the case then what kind of behavior would you put in a POCO?
The behavior in the POCO entity should be concerned with making changes to the entity itself as well as maintaining invariants - ie ensuring the integrity of the entity. In your example, the simplest kind of behavior on the POCO should be method for adding a new question block to the collection on the survey. Ideally, you would make many of the properties on the survey entity read-only:
public class Survey
{
public int SurveyId { get; private set; }
public string Title { get; private set; }
public int BrandId { get; private set; }
public DateTime Created { get; private set; }
public IList<SurveyQuestionBlock> QuestionBlocks { get; private set; }
public string Name { get; private set; }
public void AddQuestionBlock(string questionBlockInfo)
{
this.QuestionBlocks.Add(new SurveyQuestionBlock(...));
}
public Survey()
{
Created = DateTime.Now;
QuestionBlocks = new List<SurveyQuestionBlock>();
}
}
The persistence layer should be able to set the values of the read-only properties via reflection. You can go a step further and only expose the question blocks collection as a read-only collection to ensure that it can only be modified from within the entity itself.
I have the following two entities (using Code First) in my application:
public class Note
{
public int NoteId { get; set; }
public string Text { get; set; }
}
public class Decision
{
// PK/FK
public int NoteId { get; set; }
// other fields ...
public virtual Note Note { get; set; }
}
I configured my relationship like this:
modelBuilder.Entity<Decision>().HasRequired(d => d.Note).WithOptional();
A Decision must have a note but a Note does not always have a decision. A 1:1 mapping with one side being optional.
I would like a property on my note that lets me know if there is a decision for it. Something like:
public bool HasDecision
{
get
{
// not sure what to do here
}
}
Is there a way to do this without having Decision be a lazy loaded property on Note?
You would need to do an explicite query. There is no such thing like "lazy loading proxies for scalar properties". Lazy loading is only supported for navigation properties. Your entity must have a reference to a context if you want to have HasDecision as a property on the entity. I would prefer to create a repository or service method like so:
public bool HasDecision(Note note)
{
return _context.Decisions.Any(d => d.NoteId == note.NoteId);
}