All entity created by EF is partial class. so it is extendable. Suppose I have entity Person like
partial class Person{FirstName, LastName, .....}
Then I want to add a compute property Name like:
partial class Person{
[DataMember]
public string Name
{
get { return String.Format("{0} {1}", this.FirstName, this.LastName); }
}
partial void OnFirstNameChanged()
{
//.....
this.ReportPropertyChanged("Name");
}
partial void OnLastNameChanged()
{
//.....
this.ReportPropertyChanged("Name");
}
//....
}
Then for data upate operation I got following error:
The property 'Name' does not have a valid entity mapping on the entity object. For more information, see the Entity Framework documentation.
How to fix this solution?
I've just had the same error.
Do not use "ReportPropertyChanged()" but "OnPropertyChanged()" instead. There you go.
ReportPropertyChanged() only works for real entity objects (like FirstName and LastName that are e.g. real database fields), but not those computed ones (like Name, which only exists in your partial class).
The problem is with those ReportPropertyChanged("Name"), you are reporting to ObjectStateManager that the "Name" property has been changed, while this property does not exists in your model metadata (it has just been declared in your partial class, ObjectContext and ObjectStateManager do not know anything about this property).
If you add those OnLastNameChanged and OnFirstNameChanged partial methods, just get rid of them, you don't need them.
Related
I have the following two models:
public class ModelA {
public IList<ModelB> list { get; set; }
// rest properties
}
public class ModelB {
public ModelA navProp { get; set; }
// rest properties
}
When my application loads, I fetch all the data; that is, instances of ModelA include all the values of the collection navigation property list.
In other words, I don't apply the eager loading or loading on demand techniques to fetch the data for the navigation property.
The problem with that is that I get the following error:
A MergeStrategy of 'Disallowed' does not allow you to attach an entity when an entity with the same key is already attached:
Looking at the code, I noticed that the first instance of ModelA is attached twice. It seems that breeze tries to recursively attach the entities into the cache (starting from the navigation property).
I just wonder if I'm following the wrong path. I have the impression that breeze expects us to explicitly load the related entities.
A bug that caused this error message was fixed in breeze 1.5.2.
I have the following Entity:
public class Invoice
{
[Key]
public int Id { get; set; }
public DateTime? ArchiveDate { get; set; }
public DateTime? ClotureDate { get; set; }
...
}
I would like to know whether my invoice is archived or closed by using a kind of flag (boolean). For that purpose I added 2 unmapped properties in my breeze entity like this:
public class Invoice
{
[Key]
public int Id { get; set; }
public DateTime? ArchiveDate { get; set; }
public DateTime? ClotureDate { get; set; }
[NotMapped]
public bool Archived { get { return ArchiveDate.HasValue; } }
[NotMapped]
public bool Clotured { get { return ClotureDate.HasValue; } }
...
}
Now I can query my breeze entity like this:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.toType('Invoice');
The call above will return all properties of my invoice entity (including archived & clotured). It works well.
But I need only a few specific properties (for performance). Then I try:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.select("id, archived, clotured")
.toType('Invoice');
I got the error: The specified type member 'Archived' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Very frustrating. Any idea why do I cannot perform such query?
Or maybe does someone have another solution?
Many thanks.
Short version
What you are seeing is perfectly expected. The ArchivedDate is both a persisted data property and a serialized property. The Archived property is not persisted but it is serialized. That's why you see data values for both ArchivedDate and Archived. However, your remote query ... the LINQ query executed on the server ... may only refer to the persisted properties such as ArchivedDate. EF knows nothing about calculated properties such as Archived; they cannot participate in a LINQ query ... not in a where, select, orderBy or any other query. You can't mention something in a query that EF doesn't know about ... and you told EF (properly) to ignore these Archived and Clotured calculated properties.
Long version
The [Unmapped] attribute hides the properties from EF ... as it must because Archived and Clotured are calculated properties, not persistable data.
The [Unmapped] attribute also hides these properties from the metadata generated from EF. That too is both expected and good.
But this also means that you cannot construct a LINQ query that references these properties. They aren't data properties. They can't be queried by EF. Only data properties and navigation properties can appear in a LINQ query. It is really that simple.
Perhaps you're wondering why the unmapped calculated property values are actually communicated to the JavaScript client, why those values appear in the JSON payload and would populate the like-named Breeze entity properties if you add such properties to the client metadata for Invoice as "unmapped properties".
To understand why, you must understand the difference between properties that you query with EF and the properties that you serialize with Json.NET. After the EF query completes, the materialized entities have both the data properties (e.g., ArchivedDate) and the calculated properties (Archived). The [NotMapped] attribute doesn't hide a property from Json.NET. Json.NET serializes ALL properties of the materialized object - both data and calculated properties - unless you tell it not to. For example you could hide the Archived property from Json.NET serialization with the [Ignore] attribute.
The toType is a red herring and has no bearing on the matter.
Remove the ".toType('Invoice')' line from your query. Just go with:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.select("id, archived, clotured");
This forces breeze to coerce your projection into an Invoice entity type. If you leave it off you will get a true projection, i.e. a plain javascript object with just the properties you have specified, i.e. not an entity.
I am starting an MVC project and designing my DB in EF, which means I design the tables, and VS creates the classes I need to access them.
The problem is, I want to make use of attributes like DisplayName, Required and generating validation error messages ( including specifying rules to validate ).
As far as I can see, the classes are recreated every time I change my DB, so I can't really add them to the classes. Is there another way to do this once and have it persist ?
So you would use the MetadataType attribute and link your entity to a type where you'll set the validation attributes.
Something like this for an Entity Person:
[MetadataType(typeof(Person_Validation))]//<<link to metadata class
public partial class Person//<<<Your real entity class
{//this is in a separate file.
//note =>partial. There's nothing in this class
}
public class Person_Validation//the validations go here.
{
[StringLength(255, ErrorMessage="Name is required"), Required]
[DisplayName("Name")]
public string Name { get; set; }
}
My original question is here.
Below is my updated code.
Public Function StockTransferItemRemove(removeRequest As StockTransferItemRequest) As StockTransferItemResponse Implements IStockTransferService.StockTransferItemRemove
' create your objects
Dim removeResponse = New StockTransferItemResponse
Dim stockTransfer As New StockTransfer
Dim stockTransferItem As New StockTransferItem
Try
' get the aggregate root
stockTransfer = _stockTransferRepository.FindBy(removeRequest.StockTransferID).FirstOrDefault
stockTransfer.RemoveItem(removeRequest.StockTransferItemView.Id)
_stockTransferRepository.Save(stockTransfer)
Dim count As Integer = _uow.WMSCommit()
If (count > 0) Then
' the object was saved succesfully
removeResponse.Success = True
Else
' the object was not saved successfully
removeResponse.BrokenRules.Add(New BusinessRule(String.Empty, String.Empty, Tags.Messages.Commit_Failed))
End If
Catch ex As Exception
' an unexpected error occured
removeResponse.BrokenRules.Add(New BusinessRule(String.Empty, String.Empty, ex.Message))
End Try
Return removeResponse
End Function
When the unit of work tries to commit it produces the following error message.
The operation failed: The relationship could not be changed because one or more of
the foreign-key properties is non-nullable. When a change is made to a relationship,
the related foreign-key property is set to a null value. If the foreign-key does not
support null values, a new relationship must be defined, the foreign-key property must
be assigned another non-null value, or the unrelated object must be deleted.
I know that when I use StockTransfer.RemoveItem() that it removes the item from the collection but it keeps the record in the database, which is why I am receiving the error.
Is there a way of removing the child object from an aggregate Root and persisting the aggregate root?
Im sorry for the unclear code but im a C# guy so trying to find my way in VB Code. You should use the .Clear() option on the entities link which you want to clear.
Example:
Company <> Employees
Company.Emplyees.Clear() removes all the records in the relation
table.
Thats an issue im having too. I dont know the pure solution, but i always have to delete it in the ef context manualy before saving changes. In your repository method for save You should check for entities which are in the ef context but not in aggregates collection and remove them from the dbset on the context.
Did you find a good solution? I have created a solution using a ParentAttribute and extending the DbContext SaveChanges or ValidateEntity. You can find my solution here.
The answer might be a little late but, this extension method on my data context called DataContext (which inherits from DbContext) worked for me using EF4.3.
public static void Delete<TEntity>(this DataContext context, IEnumerable<TEntity> entities) where TEntity : class, new()
{
foreach (var entity in entities)
{
context.Delete(entity);
}
}
public static void Delete<TEntity>(this DataContext context, TEntity entity) where TEntity : class, new()
{
var obj = context.Entry(entity);
if (obj.State == System.Data.EntityState.Detached)
{
context.Set(typeof(TEntity)).Attach(obj.Entity);
}
context.Set(typeof(TEntity)).Remove(obj.Entity);
}
And the data context class just for completeness.
public class DataContext : DbContext
{
public DbSet<MyPOCO> POCOs { get; set; }
...
}
I have an asp.net MVC application that was recently upgraded from 1.0 to 2.0. I use a Linq-to-Sql data model and in a lot of cases, I have been using these as my model objects, as it was simple and seemed to work...
I have a class that has foreign key relationships with two child tables - these child tables will not always be populated (i.e. the foreign key is nullable).
My code (a little simplified) looks something like this:
/// This would be the generated linq-to-sql class
public class ModelObject
{
//Bunch of properties
public ChildObject { get; set; }
public ChildObject2 { get; set; }
}
public ActionResult Edit(int ID)
{
//Get the current saved object
ModelObject test = _service.GetModelObject(ID);
UpdateModel(test);
}
Since the upgrade to 2.0, I've found that the updateModel call has been instantiated the two child objects - my save then fails, as some of these have empty fields which are not nullable. This wasn't happening previous to the upgrade.
Is there a way to stop this from happening (or does anybody have a pointer as to why this has started to happen since the upgrade)?
You can stop this from happening by specifying the properties you want to exclude from binding as a parameter in your UpdateModel() call:
UpdateModel(test, null, null, new [] { "ChildObject", "ChildObject2"});
You can get more information from MSDN.