The Client is retrieving the metadata from a service. That call is succeeding.
But the call entityManager.CreateEntity(); is failing.
The error is:
"There are no KeyProperties yet defined on EntityType:
'Customer:#MyCommerceServer.Models'. Please insure that the metadata
for this type is complete either by calling FetchMetadata or by
explicitly updating the KeyProperties before creating an EntityKey for
this type."}
But the following passes with an exception says the customer is detached.
var customerType = entityManager.MetadataStore.GetEntityType(typeof(Customer));
var customer = customerType .CreateEntity();
Here is my set up. The Customer entity has a key named Id. The Customer entity on the client also has the same key. The entities on client and server exist in the same namespace.
Is there any setup I have to add to have the Customer entity KeyProperties? I see the same problem in the ToDo sample project also.
******** Update on 8/12/2014
On the server:
namespace MyCommerceServer.Models
{
public class Customer
{
public int Id { get; set; }
}
}
On the client:
namespace MyCommerceServer.Models
{
public class Customer : BaseEntity
{
public int Id
{
get { return GetValue<int>(); }
set { SetValue(value); }
}
}
}
The difference between the Breeze.sharp EntityManager.CreateEntity method and the EntityType.CreateEntity method is that the first, by default, adds the newly created entity to the EntityManager whereas the 2nd does not. The error you are getting occurs when an entity is added to an EntityManager and that entity either has no key defined or the key is set to just the default values for all of the key properties. The key is needed before the entity can be attached because the entity is cached in the EntityManager by its key.
So you have several options,
You can set the key properties in the EntityManager.createEntity call using an anon object like so:
var newCust = (Customer) myEntityManager.CreateEntity(typeof(Customer),
new { Id = 999 }));
or you can use the EntityType.CreateEntity method and set the Id before adding the entity to the entityManager
var customerType = myEntityManager.MetadataStore.GetEntityType(typeof(Customer));
var customer = customerType.CreateEntity();
customer.Id = 999;
myEntityManager.AddEntity(customer);
or you can change your metadata for the customer type to use Identity keys. This will mean that the AutoGeneratedKeyType property of the customer is set to either Identity or KeyGenerator. Note that either of these will require changes to your server side model to accomodate the change.
var customerType = entityManager.MetadataStore.GetEntityType(typeof(Customer));
Assert.IsTrue(customerType.AutoGeneratedKeyType == AutoGeneratedKeyType.Identity);
Related
I am wanting to pass down some pseudo information about an Entity to the client and have that available with my entity. From what I can tell only mapped properties of the entity are available after query execution. Is there a hook I am missing or some other way I can get at the raw results of the API call that fetched a given entity from the server?
e.g:
server:
class Patient
{
[NotMapped]
public string Name => First + " " + Last;
public string First {get;set;}
public string Last {get;set;}
}
client:
this._executeQuery(breeze.EntityQuery.from('api/Patient/1'))
.then(function (data) {
var data = data[0];
// data = PatientCtor with first and last properties only
});
I would like data.name to be available in the client. It is sent down with the query to the client but does not come out on the other end of the query.
You can add the properties to the client-side definition of the entity, and Breeze will populate them for you. You can do this by registering a custom constructor function for the entity:
function Patient() {
this.name = "";
}
var em = new breeze.EntityManager();
em.metadataStore.registerEntityTypeCtor("Patient", Patient);
Note that you need to do this before your first query.
Then, when breeze creates Patient entities as a result of a query, it will populate the unmapped name property.
See the Breeze documentation on Extending Entities.
I'm trying to add several entities to the db but I get this error:
"Saving or accepting changes failed because more than one entity of type 'Schema.Domain.DataModels.ActivitySummery' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration."
public void CreateNewGeneratedSchema(List<WeekDayViewModel> weekDays, int userId)
{
List<ActivitySummery> savedActivities = _schemaRepository.GetAllActivitySummeries(userId).ToList();
foreach(ActivitySummery activitySummery in savedActivities)
{
_schemaRepository.DeleteActivitySummery(activitySummery.ActivitySummeryId);
}
foreach (WeekDayViewModel weekDay in weekDays)
{
foreach (ActivitySummeryViewModel activitySummeryViewModel in weekDay.ActivitiySummeries)
{
try
{
ActivitySummery activitySummery = new ActivitySummery()
{
ActivityId = activitySummeryViewModel.ActivityId,
WeekDayId = activitySummeryViewModel.WeekDayId,
//Change userId
UserId = 1,
StartTime = activitySummeryViewModel.StartTime,
EndTime = activitySummeryViewModel.EndTime,
ActivityDescription = activitySummeryViewModel.Description,
Activity = _schemaRepository.GetSpecificActivity(activitySummeryViewModel.ActivityId),
WeekDay = _schemaRepository.GetSpecificWeekDay(activitySummeryViewModel.WeekDayId)
};
_schemaRepository.CreateActivitySummery(activitySummery);
}
catch (Exception)
{
throw new Exception("Something went wrong when trying to save the activity summery");
}
}
}
_schemaRepository.Save();
_schemaRepository.Dispose();
}
I know one solution that might work and it's to save and dispose the _schemaRepository after every time i added one model to the db and create a new instace of _schemaRepository. But im not sure if that is the right way. I know that every model that im trying to save has the pk of 0 and i think that might be the problem. However it still works and the db accepts the new entities but I still get the exception.
If your model (which you didn't show) has the primary key as ActivityId (which you also didn't indicate) and it is setup to auto-generate the primary key:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ActivityId { get; set; }
Then you must not include the primary key in the CreateActivitySummery method (also which you haven't provided).
// Remove this line...
// ActivityId = activitySummary.ActivityId;
Do note that a (reasonable) alternative to having the database generate the primary key automatically is to use Guid/UniqueIdentifier without using DatabaseGenerated. Then your application code can use Guid.NewGuid to generate new (presumably unique) primary keys that it can insert with no problem and track related entities easily.
In my MVC application, I have been using Repository pattern for DAL.
Now, when I do select one entity record and and update the entity field value and do Update operation then getting below error.
Attaching an entity of type 'DAL.User' failed because another entity
of the same type already has the same primary key value. This can
happen when using the 'Attach' method or setting the state of an
entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate."} System.Exception
Below is repository stuff:
public void Update(TEntity entity)
{
if (_context.Entry(entity).State != EntityState.Modified)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
Calling as follow:
In Bussines layer library:
Manager class :
private readonly IUnitOfWork _unitOfWork;
private IRepository <User , int> UserRepository
{
get
{
return _unitOfWork.GetRepository<AccountUser, int>();
}
}
public void UpdateUserEntity(UserDTO u)
{
try
{
User model = new User ();
UserRepository.Update(Mapper.Map(u, model));
_unitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
Please guide me how I could resolve above error.
The exception says that there is another entity with the same key that has been attached, but different reference.
The exception could be caused by previous attached entity.
db.Set<Entity>().Attach(new Entity { Id = 123 });
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
Or could be also caused by tracked entity that automatically attached.
db.Set<Entity>().FirstOrDefault(e => e.Id == 123); // automatically attached
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
The second cause can be solved by mentioning AsNoTracking when retrieving item.
db.Set<Entity>().AsNoTracking().FirstOrDefault(e => e.Id == 123);
Or to be safe you can use this extension to always detach any attached entity.
public static class DbSetExtension
{
public static void SafeAttach<T>(
this DbContext context,
T entity,
Func<T, object> keyFn) where T : class
{
var existing = context.Set<T>().Local
.FirstOrDefault(x => Equals(keyFn(x), keyFn(entity)));
if (existing != null)
context.Entry(existing).State = EntityState.Detached;
context.Set<T>().Attach(entity);
}
}
Usage.
db.SafeAttach(entity, e => e.Id);
It's because of the reason,
"TEntity entity as a new object instead of the one which already exists".
Means,Entity framework treats each new object as new entry.(eventhough with same existing old data,PK & all).
Solution is,
First retrieve the object from database
Do/assign the changes to the same object (preferably without changing Primary key)
Then do state as Modified ,Update,SaveChange()
I have a large object graph that I want to return to the client (an overview of the entire model) so I decided to send it back as one big object (I'm returning it as the object in question.)
In Breeze however I'm only getting the first object in each dependent object. So for example I have a 'policy' object with two 'vehicle' objects. I only see one vehicle (even when I put a breakpoint at var p = data.results[0]. The json coming back from the call shows two vehicles but breeze is catching only one.
What can I do to get the other ones? Here's the call I'm making:
var getPolicyByPolicyNumber = function (lob, policynumber, policyObservable) {
var params = {};
params['lOBCd'] = lob;
params['policyNumber'] = policynumber;
var query = EntityQuery.from('PolicyInquiry')
.withParameters(params);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var p = data.results[0];
p.completeTree(true);
return policyObservable(p);
}
};
And in my breeze controller I have the following:
[System.Web.Http.HttpGet]
public Policy PolicyInquiry(string lOBCd, string policyNumber)
{
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return policy;
}
And here's an abbreviated model showing policy and vehicle:
public class Policy
{
public int Id { get; set; }
public string PolicyNumber { get; set; }
[InverseProperty("Policy")]
public ICollection<Vehicle> Vehicles { get; set; }
// other fields removed
}
public class Vehicle
{
public int Id {get; set}
public string ItemId { get; set; }
// other fields removed
//Foreign Keys
public int PolicyId { get; set; }
[IgnoreDataMember]
[ForeignKey("PolicyId")]
[Required]
public virtual Policy Policy { get; set; }
}
Now that I see your model I think I see the issue.
Breeze does not automatically resolve the entity graph on a query. i.e. if you retrieve a Policy, Breeze only returns the Policy instance itself without any navigation properties resolved. This is by design, so that a single query doesn't bring down the entire entity graph.
If you want the values of any navigation properties you have three options, the third of which is probably your best bet. I've taken some liberties in simplifying your model for the purposes of explanation. These all assume that the "Policy" type is actually defined as a Breeze entity type, i.e. has a metadata definition in the Breeze metadataStore.
1) Use an client side EntityQuery.expand clause
var query = EntityQuery.from('Policy')
.expand("Vehicles")
.withParameters(params);
2) Use a server side Include clause
[System.Web.Http.HttpGet]
public IEnumberable<Policy>(string lOBCd, string policyNumber) {
return _contextProvider.Context.Policies.Where(....).Include("Vehicles");
}
3) Use a anonymous result, that contains two known entity types.
[System.Web.Http.HttpGet]
public Object PolicyInquiry(string lOBCd, string policyNumber) {
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return new {
Policy = policy,
Vehicles = policy.GetVehicles() // insure that they are actually resolved on the server)
}
return policy;
}
More info here: Navigation Properties
I hope this is clear enough.
Sorry, but without seeing the underlying implementation of "policy" and its metadata, it's hard to tell what's going on. But I can make a general suggestion.
1) If you want to return an aggregate object and have Breeze recognize it's constituent parts, the recommended mechanism is to create a projection and return that. i.e. something like
public IQueryable<Object> CompanyInfoAndOrders() {
return ContextProvider.Context.Customers.Select(c => new { Customer = c, Orders = c.Orders });
}
In this example, providing that Breeze has metadata for the Customer and Order types,
Breeze will deconstruct the result and add the Customer and its orders to the EntityManager, and return a collection of json objects each with a "Customer" and Orders property. The Customer and individual Orders will each have been "adapted" to the current model library as well (i.e. knockout, backbone, or backingStore - for Angular).
I'm new in MVC2 and Entity Framework and I tried get a list of products with the respective category name, but it's returned me the error
"Object reference not set to an instance of an object."
I have a table Product with a foreign key Category.
I'm using MVC2 and Entity Framework 4.0.
public class Repository
{
public IQueryable<Produto> ListAllProducts()
{
return entities.Produtos;
}
}
public class AdminProdutoController : Controller
{
TudoDeMassinhaRepository repository = new TudoDeMassinhaRepository();
public ActionResult Index()
{
var produtos = repository.ListAllProducts().ToList();
return View(produtos);
}
}
code in view where the error is generated: <%: item.CatProduto.cat_produto_nome%>
You're only selecting the products - you're not currently including the categories. This means: you'll get back your product objects, but any related objects they refer to are not loaded automatically - that's why the .CatProduto property will be NULL and thus you're getting the error.
You need to explicitly specify which additional entities you want to have loaded - something like:
public IQueryable<Produto> ListAllProductsWithCategories()
{
return entities.Produtos.Include("CatProduto");
}
This way, you should get back your Produto objects, and their CatProduto property should have been loaded and populated, too.
So if you change your index method to be:
public ActionResult Index()
{
var produtos = repository.ListAllProductsWithCategories().ToList();
return View(produtos);
}
it should work.