So as usual I have an issue with ria service + nhibernate. The question is how to make an entity property
, mapped using “references”, visible on the client side. The problem is that when you load an entity
without this field and try to save it , then missing values are updated as NULL inside db. Here’s class schema:
public class A
{
public virtual ComplexProperty property {get;set;}
}
public class AMap
{
public AMAP()
{
References(p=>p.property).Nullable().Column(“COMPLEX_PROPERTY_ID”);
}
}
(I've skipped parts with mapping/declaring key property as it's made inside underlying classes)
Usual trick with include and association attribute(like with HasMany) does not work as there is no real foreign_key property inside
class A
found a solution that's working for me. It's enough to add "fake" foreign key property to class A which is not mapped to database. It allows to define association:
public class A
{
[Include]
[Association("Relation1","ComplexPropertyId","Id")]
public virtual ComplexProperty property {get;set;}
public virtual int ? ComplexPropertyId {get;set;}
}
Last thing to do is to set ComplexPropertyId manually on the client side after retrieving objects from db(mappings remain as they were).
public IQueryable<A> GetA()
{
var item = repository.Query<A>();
foreach(var a in item) a.ComplexPropertyId = a.ComplexProperty.Id;
return item;
}
Related
I am using EF Core and I have a scenario where the user can create a custom field and then creates options for that custom fields.
public class CustomField : Entity<long>
{
[Required]
public string Name { get; private set; }
public bool IsRequired { get; private set; }
public List<CustomFieldOption> customFieldOptions;
public virtual IReadOnlyCollection<CustomFieldOption> CustomFieldOptions => customFieldOptions;
protected CustomField()
{
}
public CustomField(long id, string name, bool isRequired, List<CustomFieldOption> customFieldOptions)
{
Id = id;
Name = name;
IsRequired = isRequired;
this.customFieldOptions = customFieldOptions;
}
}
public class CustomFieldOption : Entity<long>
{
[Required]
[MaxLength(256)]
public string Text { get; private set; }
protected CustomFieldOption()
{
}
public CustomFieldOption(string text)
{
Text = text;
}
}
public class Client : Entity<long>
{
public Name Name { get; set; }
private List<ClientCustomFieldOptionValue> customFieldOptionValues { get; set; } = new List<ClientCustomFieldOptionValue>();
public IReadOnlyCollection<ClientCustomFieldOptionValue> CustomFieldOptionValues => customFieldOptionValues;
public Client(Name name)
{
}
public Result AddCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
public Result RemoveCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
}
public class ClientCustomFieldOptionValue
{
public CustomFieldOption CustomFieldOption { get; private set; }
protected CustomFieldOptionValue()
{
}
public ClientCustomFieldOptionValue(CustomFieldOption customFieldOption)
{
CustomFieldOption = customFieldOption;
}
}
CustomFieldOption seems to be a Value Object as the text it holds is something that doesn't need an Id. But then in terms of store persistency needs an Id to be stored in database on a different table where it can be queries by Id etc...
I am not sure if I shall add it as an Entity because ValueObjects do not have Id.
One other problem I have is validation. If it is an Entity how can I validate Text property. I know validation on constructor is a bad idea. If I validate it in the ApplicationLayer then wherever I create a new object I have to validate that is not empty and the length.
If I forget to add validation in one of the application services and pass null Text then I create an inconsistent state.
Update #1
A Client can select one or many options of a custom field. I suppose these needed to be stored on a separate table ClientCustomFieldOptionValue. In that case is this an entity or a valueobject? And what about CustomFieldOption. Does it become an Entity? I am quite confused when to use Entity or ValueObjects
Try not to think of persistency details while designing domain model.
According to your description, CustomFieldOption expresses an individual property with no business relations to any other structure, thus:
it should not hold a business identifier
it should encapsulate its own validations
Meaning it fits the concept of a value-object (validation inside ctor).
When it comes to persistency, your repository model should be capable of storing CustomFieldOption objects in a child table (with DB identifier) referencing the parent table (CustomField objects)
On the query side, repository should be capable of aggregating data from these two tables into a single CustomField entity.
(How exactly you implement such DB capabilities depends on the ORM you choose to work with, EF in your case)
Just one observation, if you will use Ef Core and the containing entity has a one to many relationship with the value objects, you will have this limitation:
Owned Entity types, Ef Core
Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance
If you are mapping your entities and value objects using DbContext, you usually define an owned entity type for a value object or use a record type.
For owned entities, this creates a column in your table like this: EntityName_ValueObject (i.e. Person_Address) but this works for a single value object not a collection when you don't know in advance the number of items in the collection.
It is correct that you should not concern with persistence when designing your domain, but is also correct to think that having a value object with an identity does not make sense.
Most important, you should be aware of this potential issue early on.
I have an application based on ASP.Net MVC 6 / ASP.Net Core 1.0, which exposes CRUD methods through a Web API.
I am using Entity Framework 7.0.0-rc1, I plan to migrate everything to EF Core 1.0 / ASP.Net Core 1.0 but haven't done this yet.
Here's a snippet of the type of model which causes me an design issue :
public class ParentEntity
{
public int Id { get; set; }
public virtual ICollection<ChildEntity> ChildCollection { get; set; } = new ChildCollection();
}
public class Child
{
public int Id { get; set; }
public virtual ParentEntity ParentEntity { get; set; }
public int? ParentEntityId { get; set; }
}
All relationships are properly ensured in my ApplicationContext class, and all CRUD operations work as expected, including Adds and Updates of the Child entities when a ParentEntity is saved.
For example, here's the kind of Save method I expose through the API :
// On some web API Save method :
[HttpPost]
public JsonResult Save(ParentEntity entity)
{
if (entity.Id > 0) _context.Update(entity);
else _context.Add(entity);
var result = _context.SaveChanges();
return Json(result);
}
The entity passed to the save method is coming from my AngularJS backend. As long as child entities have been added or modified, everything works as expected.
Let's now imagine the back end has received a ParentEntity loaded with two ChildEntity named A and B.
Now on the backend I remove the B child entity and post it to the save method.
The backend saves the changes of the data it receives : all updates are persisted on ParentEntity and on ChildEntity A.
But the B ChildEntity remains in the datastore, which seem logical since there was no indication on what to do with it.
So my question is, since it seems to me it is a very common case : how do I deal with that ?
I have imagined storing somewhere the child collection, save the changes, reload the root entity, compare the collection, determine which should be removed, then remove it, but it seems pretty hard and dirty work to me. Is there a better way ?
The answer to your question is here
https://msdn.microsoft.com/da-dk/magazine/mt694083
According to some tips explained in the MSDN blog post above, I have implemented a Client state field on every entity, through it's base class :
public enum ObjectState
{
Unchanged = 0,
Added = 1,
Modified = 2,
Deleted = 3
}
public interface IObjectWithState
{
ObjectState ClientState { get; set; }
}
public abstract class BaseEntity : IBaseEntity, IPrimaryKey
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public bool IsNew => Id <= 0;
[NotMapped]
public ObjectState ClientState { get; set; } = ObjectState.Unchanged;
}
Whenever a child entity is removed from a collection on the client side, instead of being physically removed and never posted back, I will keep it on the collection, and mark it as deleted.
Then I just have to deal with that on server side :
_context.ChildEntityCollection.RemoveRange(
entity.ChildCollection.Where(c => c.ClientState == ObjectState.Deleted).ToList()
);
_context.SaveChanges();
I implemented the whole enum on every entity, but currently I only use the Deleted value, and only through a relation. Up till now, no need for other values, or any value on root entities.
I am having an entity which holds the virtual collection of another entity. When i try to insert the data by filling the virtual collection for the newly inserted objects it is throwing the error that object with same key already exists.
I know that when the entity is not created it will have identity field with 0 value. But i need to store the collection of data when i store the data in main table.
public virtual void Insert(TEntity entity)
{
((IObjectState)entity).ObjectState = ObjectState.Added;
entityDbSet.Attach(entity);
dataContext.SyncObjectState(entity);
}
This is the insert method that i am using. and below is the poco classes (partial implementation for extending the classes to hold the collection of data) for this operation.
public partial class UserDefinedData
{
public int ID { get { return this.UserSelectedDValueID; } set { this.UserSelectedDValueID = value; } }
public string Name { get { return this.entityTypeName; } }
public virtual AM_AssetLocations AM_AssetLocations { get; set; }
}
public partial class AM_AssetLocations
{
// Reverse navigation
public virtual ICollection<UserDefinedData> UserDefinedDatas { get; set; }
}
I am passing the data using json. Which is also seems correct. as the virtual collection of data is added to the entity correctly.
{"entity":{"ID":"0","CreatedByID":"0","CreatedDate":"04-13-2014 10:48","ModifiedByID":"","ModifiedDate":"","DeletedByID":"","DeletedDate":"","Deleted":"false","Name":"h","Active":"true","DisplayOrder":"0","Test Decimal":"10","":"","Test Number":"10","Test Plain Text":"h","Test RTF":"<p>hsj</p>","Test Yes No":"false","Test Yes No 2":"true","TestDate":"01-Apr-2014","TestDateTime":"10:00 AM","UserDefinedDatas":[{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"123","ValueNumber":"10"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"124","ValueListItemID":"25"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"122","ValueNumber":"10"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"117","ValueString":"h"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"119","ValueString":"<p>hsj</p>"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"125","ValueYesNo":0},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"126","ValueYesNo":1},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"120","ValueDate":"01-Apr-2014"},{"EntityType":"AM_AssetLocations","EntityTypeID":"0","CreatedByID":"0","UserDefinedFieldID":"121","ValueDate":"08-Apr-2014 10:00 AM"}]}}
Please help me to solve this issue.
Note : Just to solve this same key exception if i try to assign the identity field my self it is throwing referential integrity exception. I know that storing the realative collection should work fine. but it is not working for me. please give me some guidance and solution for this.
Thanks,
sachin
Attach is for attaching existing entities.
Context should assign proper state itself, there's no need to do it manually in your case
public virtual void Insert(TEntity entity)
{
//((IObjectState)entity).ObjectState = ObjectState.Added;
context.TEntityDbSet.Add(entity);//Add, not Attach!
//dataContext.SyncObjectState(entity);
context.SaveChanges()
}
..
http://msdn.microsoft.com/en-us/data/jj592676.aspx
Hi Thanks all for helping out. I changed the way to perform this operation. I have created the stored procedure which accepts the main entity and user defined table type of child collection which can be passed as dataset from .net page. And this works fine for me.
thanks.
I have entity like below
public abstract class MyBaseClass
{
public int Id { get; set; }
}
public class MyConcrete : MyBaseClass
{
public int TemplateName { get; set; }
public int Total { get; set; }
}
public class MyOtherConcrete : MyBaseClass
{
public int TemplateName { get; set; }
public int MyProperty { get; set; }
public string OtherProperty { get; set; }
}
using default initialization, EF will make table with columns like bellow:
Id
TemplateName
TemplateName1 // <-- this is the problem
Total
MyPorperty
MyOtherProperty
now my question how to configure EF so all the TemplateName property on derived class automatically mapped into TemplateName column without making another column. is it possible to configure it on OnModelCreating method?
EDIT
actually above was simplified version of my problem. i have 10 more entities some property might duplicated everywhere and i don't want to add any abstraction layers.
i have tried manually map the column on the OnModelCreating but having "Each property name in a type must be unique. Property name 'TemplateName' was already defined" exception any idea?
EDIT 2
so, i found here, that said it is impossible to do such thing like above in EF, it is weird for me..
Move TemplateName into MyBaseClass to avoid this problem.
If necessary, you can use intermediate base classes to hold properties shared by only a subset of your classes.
After searching through the net, so far that was not possible to do that. since i realize that my inheritance tree is wrong.
so in my case, i should change my code to match the EF requirement, it is sound weird.. because in many case ENTITY is a NO NO to change, we usually create an entity that used in multiple project. event we found our entity wrong we won't update it because updating it will require massive change on the other projects.
so far, i think there is no exact answer for my question. will update it soon after EF support it.
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.