How to Attach Entity in Entity Freamework Core 2.0 Preview? - entity-framework

I want to create a one method using which i can Add/Update entity using Entity Framework 2.0-Preview.
I am using Insert Update Pattern.
For more Ref: https://msdn.microsoft.com/en-us/data/jj592676.aspx (Last example)
Below is a method code:
public string AttachEntity(Book book)
{
_context.Entry(book).State = (book.Id == 0)
? Microsoft.EntityFrameworkCore.EntityState.Added
: Microsoft.EntityFrameworkCore.EntityState.Modified;
string msg = $"Book details {_context.Entry(book).State} Successfully";
_context.Book.Attach(book);
_context.SaveChanges();
return msg;
}
and got below error:
InvalidOperationException: The property 'Id' on entity type 'Book' has
a temporary value while attempting to change the entity's state to
'Unchanged'. Either set a permanent value explicitly or ensure that
the database is configured to generate values for this property.
However Id is auto generated for book table.
and below urls for that action:
[For add] Home/AttachEntity?Id=1&Title=4th book&Description=New book added by attach method&Price=120&AuthorId=2
[For update] Home/AttachEntity?Title=4th book&Description=New book added by attach method&Price=120&AuthorId=2

Related

Get Annotation Id from in a plugin

I have a custom entity A, which contains an annotation column (a built in entity that support upload files). I want to be able to read the annotation id for the entity record i get when a plugin is triggered.
The entity has all attributes i am except the annotation in any form, no referenced entity. It is worth to note that annotation entity is listed in the relationship tab but there is no reference to annotation field in the fields view in Dynamics online.
How can i lookup or get the annotation id in the entity A in a custom plugin.
The plugin triggers on the create message from custom entity A, since it has all the columns i want to process in addition the file uploaded in the annotation entity.
I looked at the sample sdk sample, but it is not useful since i want to get the annotation id first before retrieving it.
https://learn.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/gg328429(v=crm.8)
Any pointers or sample are appreciated it.
There is a one-to-many relationship from your custom entity to the annotation entity, because there can be many notes (and attachments) for each custom entity record.
Your plugin should create a new annotation record and set the objectid and objecttypecode fields on that annotation record to the current custom record that was just created.
Here's an example that uploads a simple text file and relates it to a custom entity record that was just created:
var newId = <new just-created custom entity record id goes here>;
var sampleFileText = "Hello World";
var sampleFileBytes = Encoding.ASCII.GetBytes(sampleText);
var sampleFileBase64 = System.Convert.ToBase64String(fileBytes);
var annotation = new Entity("annotation");
annotation.Attributes["objectid"] = new EntityReference("new_entity", newId); // <- Your custom entity name and new id here
annotation.Attributes["objecttypecode"] = "new_entity"; // <- Your custom entity name here
annotation.Attributes["subject"] = "Uploaded File";
annotation.Attributes["documentbody"] = sampleFileBase64 ;
annotation.Attributes["mimetype"] = #"text/plain";
annotation.Attributes["notetext"] = "Uploaded File";
annotation.Attributes["filename"] = "UploadedFile.txt";
Service.Create(annotation);

How to save changes twice in one method using Entity Framework Core

Using Entity Framework Core i am trying to save changes twice for the same entity collection.
public async Task Save(IEnumerable<Request> request)
{
var entities = request.Select(x => new MyEntity()
{
FileName = x.FileName,
Status = Status.Downloading
});
// save as downloading
await _dbContext.MyFiles.AddRangeAsync(entities).ConfigureAwait(false);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
// start downloading
await _storage.DownloadFilesAsync(request).ConfigureAwait(false);
// save as downloaded
foreach (var entity in entities)
{
entity.Status = Status.Downloaded;
}
// this save DOES NOT update the entities with status Downloaded
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
The first call to SaveChanges() method creates entities in the DB with status Downloading.
Then i am modifying entity's status to Downloaded and call SaveChanges() again. However this time entities does not get updated in DB.
I thought entities are already loaded in the Db context, so i dont have to reload them before second SaveChanges. Just modifying the property would mark them modified. But its not working.
Update 1
I updated code as below and explicitly set State as Modified
// save as downloaded
foreach (var entity in entities)
{
entity.Status = Status.Downloaded;
// The exception occurs at line below on 2nd loop
_dbContext.Entry(entity).State = EntityState.Modified;
}
Now im getting exception
System.InvalidOperationException: The instance of entity type
'MyEntity' cannot be tracked because another instance of this type
with the same key is already being tracked. When adding new entities,
for most key types a unique temporary key value will be created if no
key is set (i.e. if the key property is assigned the default value for
its type). If you are explicitly setting key values for new entities,
ensure they do not collide with existing entities or temporary values
generated for other new entities. When attaching existing entities,
ensure that only one entity instance with a given key value is
attached to the context. at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey
key, InternalEntityEntry entry) at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry
entry) at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState
oldState, EntityState newState, Boolean acceptChanges)
The entity has Id property of type int and The Id property also has [Key] attribute.
public class MyEntity
{
[System.ComponentModel.DataAnnotations.Key]
public int Id {get;set;}
public string FileName {get;set;}
public string Status {get;set;}
}
Update 2
So after doing little more debugging, i found the first SaveChanges() operation is creating the records in the database and also assigning unique Ids to each record as expected. The Id is auto incremented identity column in the database.
However the entities are not refreshed with new Ids. so all the entities still have value 0 after first savechanges.
How do i refresh these entities with newly created Ids?
Inside foreach loop i tried reloading as
_dbContext.Entry<OcrFile>(entity).ReloadAsync().ConfigureAwait(false);
but that didn't refresh.
Resolved!!. I have to eagerly load entities using ToList() or ToArray() before i pass them to AddRange method
var entities = request.Select(x => new MyEntity()
{
FileName = x.FileName,
Status = Status.Downloading
}).ToList();
I am not sure that is a bug in EF but i created a separate SO thread for that question

How to Detect Duplicate record in CRM 2013 on Create (Pre-Validate ) Plugin

I am writing a plugin which will detect duplicate ID on Create and will restrict the user to enter a new ID instead. NOTE : I CANT USE DEFAULT DUPLICATION METHODS PROVIDED BY MICROSOFT DYNAMICS 2013 or 2015.
THIS IS A SPECIAL CASE.
Following is the code of my plugin :
enter code here
if (entity.LogicalName == "new_studentinformation")
{
// An accountnumber attribute should not already exist because
// it is system generated.
if (entity.Attributes.Contains("new_studentid") == false)
{
// Create a new accountnumber attribute, set its value, and add
// the attribute to the entity's attribute collection.
Random rndgen = new Random();
entity.Attributes.Add("new_studentid", rndgen.Next().ToString());
}
Now the problem i am facing is in this line
if (entity.Attributes.Contains("new_studentid") == "Something")
how i can get the value entered by user in crm and compare it to my existing records ?
You need to retrieve the entity from the plugin context as described in the documentation:
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
// Your code here...
var desiredValue = entity.GetAttributeValue<desiredtype>("desiredfield");
}

greenDao generator is not generating certain variables in auto generated classes

Below is my schema generation code, Two variables (storeIdForSurvey & questionIdForAnswer) are not being auto generated in the model class( Survey & Question), though they are present the in auto generated dao classes (SurveyDao & QuestionDao).
Object Oriented description of the domain is as : User has Store, Store has Survey,Survey has FollowupItems, Survey has Category, Category has Question, Question has History, Question has Answer.
private static void addUser(Schema schema) {
//User
Entity user = schema.addEntity("User");
user.addIdProperty();
user.addStringProperty("districtId");
user.addStringProperty("employeeId");
user.addStringProperty("name");
user.addStringProperty("sessionToken");
user.addStringProperty("userId");
//Store
Entity store = schema.addEntity("Store");
// foreign key
Property userIdForStore = store.addLongProperty("userIdForStore").getProperty();
store.addToOne(user, userIdForStore);
user.addToMany(store, userIdForStore);
store.addIdProperty();
store.addStringProperty("storeId");
store.addStringProperty("address");
store.addStringProperty("city");
store.addStringProperty("storeName");
store.addStringProperty("state");
store.addStringProperty("zip");
store.addStringProperty("storeManagerName");
store.addBooleanProperty("isSurveyHistoryAvailable");
//Survey
Entity survey = schema.addEntity("Survey");
//foreign key
Property storeIdForSurvey = survey.addLongProperty("storeIdForSurvey").getProperty();
survey.addToOne(store, storeIdForSurvey); // one store can have one survey at a time
store.addToOne(survey, storeIdForSurvey);
survey.addIdProperty();
survey.addStringProperty("surveyId");
survey.addStringProperty("dmSignImagePath");
survey.addStringProperty("dmSignImageName");
survey.addStringProperty("smSignImagePath");
survey.addStringProperty("smSignImageName");
survey.addStringProperty("startlatitude");
survey.addStringProperty("startlongitude");
survey.addStringProperty("submitLatitude");
survey.addStringProperty("submitLongitude");
survey.addStringProperty("acknowledgedBy");
survey.addStringProperty("deliveredBy");
survey.addStringProperty("name");
survey.addStringProperty("createdBy");
survey.addStringProperty("description");
survey.addStringProperty("storeId");
survey.addStringProperty("districtManager");
survey.addDateProperty("startDate");
survey.addDateProperty("submitDate");
survey.addDateProperty("syncDate");
survey.addDateProperty("createdDate");
survey.addDateProperty("actionItemAssignDate");
survey.addDateProperty("actionItemDueDate");
survey.addDoubleProperty("score");
//FolloupItems
Entity followupItem = schema.addEntity("FollowupItem");
//foreign key
Property surveyIdForFollowupItem = followupItem.addLongProperty("surveyIdForFollowupItem").getProperty();
followupItem.addToOne(survey, surveyIdForFollowupItem);
survey.addToMany(followupItem, surveyIdForFollowupItem);
followupItem.addIdProperty();
followupItem.addStringProperty("assignedTo");
followupItem.addStringProperty("comment");
followupItem.addStringProperty("photoName");
followupItem.addStringProperty("photoURL");
followupItem.addDateProperty("assignedDate");
followupItem.addDateProperty("dueDate");
followupItem.addDateProperty("expeireDate");
//Category
Entity category = schema.addEntity("Category");
//foreign key
Property surveyIdForCategory = category.addLongProperty("surveyIdForCategory").getProperty();
category.addToOne(survey, surveyIdForCategory);
survey.addToMany(category, surveyIdForCategory);
category.addIdProperty();
category.addStringProperty("categoryId");
category.addStringProperty("name");
category.addStringProperty("weight");
category.addStringProperty("surveyId");
category.addDoubleProperty("totalScore");
category.addIntProperty("sortOrder");
category.addBooleanProperty("completionStatus");
category.addBooleanProperty("hasActionItem");
//Question
Entity question = schema.addEntity("Question");
//foreign key
Property categoryIdForQuestion = question.addLongProperty("categoryIdForQuestion").getProperty();
question.addToOne(category, categoryIdForQuestion);
category.addToMany(question, categoryIdForQuestion);
question.addIdProperty();
question.addStringProperty("questionId");
question.addDateProperty("startDate");
question.addDateProperty("endDate");
question.addStringProperty("statement");
question.addStringProperty("type");
question.addStringProperty("weight");
question.addStringProperty("surveyCategoryName");
question.addIntProperty("displayOrder");
question.addBooleanProperty("naFlag");
question.addBooleanProperty("isRequired");
//Question History
Entity questionHistory = schema.addEntity("questionHistory");
//foreign key
Property questionIdForQuestionHistory = questionHistory.addLongProperty("questionIdForQuestionHistory").getProperty();
questionHistory.addToOne(store, questionIdForQuestionHistory);
question.addToMany(questionHistory, questionIdForQuestionHistory);
questionHistory.addIdProperty();
questionHistory.addStringProperty("questionId");
questionHistory.addStringProperty("secondLastHistory");
questionHistory.addStringProperty("lastHistory");
//Answer
Entity answer = schema.addEntity("Answer");
//foreign key
Property questionIdForAnswer = answer.addLongProperty("questionIdForAnswer").getProperty();
question.addToOne(answer, questionIdForAnswer);
answer.addToOne(question, questionIdForAnswer);
answer.addIdProperty();
answer.addStringProperty("projectType");
answer.addStringProperty("assignedTo");
answer.addStringProperty("comment");
answer.addStringProperty("photoUrl");
answer.addStringProperty("photoNmae");
answer.addStringProperty("selectedOption");
answer.addDateProperty("assignedDate");
answer.addDateProperty("dueDate");
answer.addDateProperty("expireDate");
answer.addDoubleProperty("score");
}
please read the documentation carefully:
public ToOne addToOne(Entity target, Property fkProperty)
Adds a to-one relationship to the given target entity using the given given
foreign key property (which belongs to this entity).
This means the following statement is correct:
Property storeIdForSurvey = survey.addLongProperty("storeIdForSurvey").getProperty();
survey.addToOne(store, storeIdForSurvey);
but the next statement is incorrect since the Property storeIdForSurvey is not member of the Entity store:
store.addToOne(survey, storeIdForSurvey);
Try to use this statement instead:
store.addToOneWithoutProperty("Survey", survey, "storeIdForSurvey");

ObjectDisposedException when getting foreign key field

I created my model using Db Context Generator, using EF 4.
My model is like this:
Program Table:
ID
Name
Group Table:
ID
Name
ProgramID (Associated to Program.ID)
I want to display these columns in my grid:
Program.Name - Group.Name
But grdGroups.DataSource = db.Groups.ToList()
doesn't return Program.Name
When I try to this I get ObjectDisposedException.
Partial Public Class Group
Public ReadOnly Property ProgramName() As String
Get
Return Program.Name
End Get
End Property
End Class
What's the best way to return the Program.Name to include it in the grid datasource?
When I try to this I get
ObjectDisposedException
The problem is lazy loading - EF did not materialize the related Program entity, hence when you try to access Program.Name it will try and re-query the DB, but the context has been disposed at this point, so you get an exception.
You can use an Include() query when you retrieve your Group entity, to specify that you also want to load the related Program entity, i.e. :
var groups = context.Groups.Include(x => x.Program);