Sparx EA diagram update with API fails with error No such diagram found having diagram GUID - enterprise-architect

I have a C# script performing create/update operations to EA diagrams. It's working good with EA 15. But when I run it with EA 16, it fails with the error - "No such diagram found having diagram GUID:"
Here is the details of example user case.
Script is connecting with one user to EA and creating diagram. Now next time script runs and connects with another user to EA and try to update earlier created diagram.
Based on the new version 16 document, I know that it's required to be reloaded. When I try to update the same diagram from the EA UI, I get the error and it asked to reload that diagram. After reload I am able to update the diagram from the UI.
Same thing I tried from the code to reload the diagram (using Repository.ReloadDiagram (currentDiagram.DiagramID);) and then update (diagram.Update()), but still I am getting same error.
Also tried to login with 2nd user in UI and set the reload changed diagram to true from Design->Diagram->Options->auto reload changed diagram. This also does not reload the diagram and shows pop up to reload before updating entity.
Update:
Here is the code snippet that I am using. It gives error on second diagram.Update() i.e. after connecting to user2 and trying to update diagram from his connection with following error "No such diagram found having diagram GUID: "
{
//connect to user1
EAConnection connection = new EAConnection();
connection.EARepository = new Repository();
connection.EARepository.SuppressSecurityDialog = true;
connection.EARepository.SuppressEADialogs = true;
bool isOpened = connection.EARepository.OpenFile2("path", "user1", "password");
//update diagram with user1
diagram = repository.GetDiagramByGuid(guid);
repository.ReloadDiagram(diagram.DiagramID); //reload diagram object
//update attribute values
diagram.Name = "xyz";
diagram.Update();
//connect to user2
EAConnection connection = new EAConnection();
connection.EARepository = new Repository();
connection.EARepository.SuppressSecurityDialog = true;
connection.EARepository.SuppressEADialogs = true;
bool isOpened = connection.EARepository.OpenFile2("path", "user2", "password");
//update diagram with user2
diagram = repository.GetDiagramByGuid(guid);
repository.ReloadDiagram(diagram.DiagramID); //reload diagram object
diagram.Name = "abc";
diagram.Update();
}

It looks like you have not posted the part of the code where the actual problem is.
This code is tested on version 16.1 (64 bit) and works without any problems.
namespace EA_console_app
{
internal class Program
{
static void Main(string[] args)
{
// connect to user1
var path = #"C:\temp\test project.qea";
var repository = new EA.Repository();
repository.SuppressSecurityDialog = true;
repository.SuppressEADialogs = true;
var isOpened = repository.OpenFile2(path, "user1", "user1");
var guid = "{9585B09C-28C5-485b-AF25-79F1D70EAA18}";
//update diagram with user1
var diagram = repository.GetDiagramByGuid(guid);
//repository.ReloadDiagram(diagram.DiagramID);
diagram.name = "xyz";
var user1Update = diagram.Update();
//connect to user2
var repository2 = new EA.Repository();
repository2.SuppressSecurityDialog = true;
repository2.SuppressEADialogs = true;
isOpened = repository2.OpenFile2(path, "user2", "user2");
//update diagram with user1
diagram = repository.GetDiagramByGuid(guid);
//repository.ReloadDiagram(diagram.DiagramID);
diagram.name = "abc";
var user2Update = diagram.Update();
}
}
}
You might want to check the permissions of your user1 and user2. If they don't have permission to update diagrams, the Update() operation will return false.
What I'm guessing is that you actually try to create a diagram as well (as indicated in the text, but not present in the code), but that your user1 doesn't have the permission to create diagrams. That means that the Update() for user1 fails, and doesn't create the diagram after all. That means the GetDiagramByGuid for user2 will fail.
In that case you would get error like: System.Runtime.InteropServices.COMException: 'Can't find matching ID' instead of the error you mentioned: No such diagram found having diagram GUID:
The method repository.ReloadDiagram does not do what you think it does. It will refresh the visual rendering of the diagram in the GUI, but it will not affect your diagram object in any way. Since you are not even showing the GUI this is completely useless in this context.

You are using the same GUID on two different repositories. That does not make any sense since GUIDs are designed to be different everywhere. So no wonder you don't find the diagram with the GUID from the first repo in the second.
Assuming "path" is different in both Open statements since opening the same repo as two instances would be nonsense as well.

Related

Logic for tracking entity framework property value changes in MVC

I think I am missing something in my understanding of tracking property value changes in entity framework.
I have an application where i store service requests. Whenever a team value in changed in the service request record, I want to create a team history record in a related teamhistory entity.
I have created the app in MVC using the standard scaffolding for controllers and views.
In the (post)edit task in the controller, the standard logic generated has the following code
if (ModelState.IsValid)
{
db.Entry(serviceRequest).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(serviceRequest);
I have modified that to include the creating of the teamhistory record and an individualhistory record for individual assigned within team. The code for creating these related records work, BUT i want these records only created when the values on team or member(individual) change from what they were previously.
So far the conditions i have specified due not trigger this correctly because I havent gotten the condition right. Below is the current code:
//string teamorig = db.Entry(serviceRequest).Property(u => u.Team).OriginalValue.ToString();
//string teamcurr = db.Entry(serviceRequest).Property(u => u.Team).CurrentValue.ToString();
//if (teamorig != teamcurr)
var TeamIsModified = db.Entry(serviceRequest).Property(u => u.Team).IsModified;
if (TeamIsModified)
{
serviceRequest.TeamAssignmentHistories.Add(new TeamAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
Team = serviceRequest.Team
});
}
//=========================================================================================
// if individual assigned has changed add individual history record========================
var IndividualIsModified = db.Entry(serviceRequest).Property(u => u.Member).IsModified;
if (IndividualIsModified)
{
serviceRequest.IndividualAssignmentHistories.Add(new IndividualAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
AssignedTo = serviceRequest.Member.Value,
});
}
//===========================================================================================
The var teamismodified logic doesnt work. When I save the page without making any changes on it- the logic kicks off because in debugging it thinks the field has been modified.
When I comment out that code and uncomment the code above it for original and currentvalues- ie the teamorig and teamcurr logic, teamcurr and teamorig have the same values in debug, even when they have been forced into a change on the save in the MVC view page. Because they have the same values, the if condition is false so the team history record is not created.
The above code has been sandwiched in between
db.Entry(serviceRequest).State = EntityState.Modified;
and
await db.SaveChangesAsync();
statements.
What am I not understanding about entity framework tracking changes in mvc? Why does think its modified when i make not changes to team, and why are teamorig and teamcurr the same when I do make the changes?
Any advice would be welcome. Thanks

Write CRM Plugin that Fires when a Field is updated

We're on CRM 2013 on-premise. I'm writing a plugin that fires when a field on Quote entity is updated.
So I registered my plugin on 'Update' message. Then the event is 'Post-operation'. (I tried Pre-operation but still no luck)
Basically the goal is when the field is updated, create a new entity 'ContractShell' and then create relationship between the Quote and the newly created 'ContractShell'.
However my problem is when the field is updated, my plugin never seems to fire. I just simply put a InvalidPluginExecutionException in my code, but for some reason it never fires.... Any ideas? Thanks.
Here's a screenshot of my plugin step:
Here's my code:
var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// The InputParameters collection contains all the data passed in the message request.
var targetEntity = context.GetParameterCollection<Entity>(context.InputParameters, "Target");
if (targetEntity == null)
throw new InvalidPluginExecutionException(OperationStatus.Failed, "Target Entity cannot be null");
if (!context.OutputParameters.Contains("id"))
return;
Guid QuoteId = (Guid)targetEntity.Attributes["quoteid"];
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
var contractShellEntity = new Entity();
contractShellEntity = new Entity("new_);
//assign the portfolio
if (targetEntity.Attributes.Contains(Schema.Quote.Portfolio))
{
var quotePortfolio = (EntityReference)targetEntity.Attributes[Schema.Quote.Portfolio];
contractShellEntity[Schema.new_ContractShell.PortfolioName] = new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}
var contractShellId = service.Create(contractShellEntity);
throw new InvalidPluginExecutionException(OperationStatus.Failed, "I created New Contract Shell");
//Creating relationship between Contract Shell and the newly created Accounts
var quoteContractReferenceCollection = new EntityReferenceCollection();
var quoteContractRelatedEntity = new EntityReference
{
Id = contractShellId,
LogicalName = contractShellEntity.LogicalName
};
quoteContractReferenceCollection.Add(quoteContractRelatedEntity);
var quoteContractReferenceCollectionRelRelationship = new Relationship
{
SchemaName = Schema.new_ContractShell.ContractQuoteRelationship
};
service.Associate("quote", QuoteId, quoteContractReferenceCollectionRelRelationship, quoteContractReferenceCollection);
You need to register not only the plugin but an SDKMessageProcessingStep. Also, you have to implement the Execute method in your plugin to be able to register it, so either you're missing code in your snippet, or your code is the problem.
Also, your InvalidPluginExecutionException is nested after a number of checks. Theres a good chance you don't have any output parameters if you don't know how to register a plugin, so your code would actually return before you hit the exception.

How to debug a server-side (.rdl) report that does not work in my application?

I have a SQL Server Report Server (SSRS) that hosts a bunch of reports. When I click on the provided URL, I can navigate to the report, drilling down the path, and I can successfully run the report by passing it the required parameters. The Stored Procedures used by the report are hosted in a database that uses Windows Authentication.
However, when I try to call the same report from my application, I cannot see anything.
Here is the code I use. Upon clicking on a button, I execute this code:
var reportViewer = new ReportViewer();
// Set Processing Mode
reportViewer.ProcessingMode = ProcessingMode.Remote;
// Set report server and report path
reportViewer.ServerReport.ReportServerUrl = new Uri("http://MySsrsServer/reportserver");
reportViewer.ServerReport.ReportPath = "/Reports/MyReport";
// set the credentials
reportViewer.ServerReport.ReportServerCredentials.ImpersonationUser = WindowsIdentity.GetCurrent();
// create report parameters and set them in the report
var param1 = new ReportParameter("First_Param", "Some string");
var param2 = new ReportParameter("Second_Param", "Some other string");
reportViewer.ServerReport.SetParameters(new ReportParameter[] { param1, param2 });
using (var reportForm = new ReportForm(reportViewer))
{
reportForm.ShowDialog();
}
The ReportForm is just a regular Form that has a member of type ReportViewer. In the class constructor I assign the member:
public ReportForm(ReportViewer reportViewer)
{
InitializeComponent();
try
{
this.reportViewer = reportViewer;
reportViewer.SetDisplayMode(DisplayMode.PrintLayout);
reportViewer.Dock = DockStyle.Fill;
reportViewer.ZoomMode = ZoomMode.Percent;
reportViewer.ZoomPercent = 100;
reportViewer.RefreshReport();
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
As I said, I can see the report being correctly executed from Server, but not from my application. The content of the ReportForm is empty, but I do not get any exceptions being thrown.
Any idea what is it I am doing wrong?
TIA,
E
Turns out that constructing the ReportViewer object outside the form where the report is to be displayed and then passing it inside the constructor of the form in order to assign it to the inner ReportViewer object is not a good idea. I don't know what's happening behind the scene, perhaps some bindings between the ReportViewr instance and the GUI control that is to show the report are broken.

Returning multiple resultsets with EntityFramework repository

I am working on a code where I need Multiple tables as result of a stored procedure. I am using Entity Framework repository pattern. It returns and bind an IEnumerable object, but I need to bind it with multiple IEnumerables at the same time.
Can anybody help?
This is the code I am using :
db.Database.SqlQuery("procReturnsMultipleResuiltSets")
the ways to achieve your goal are disclosed in this article.
From related article the most common way is:
using (var db = new BloggingContext())
{
// If using Code First we need to make sure the model is built before we open the connection
// This isn't required for models created with the EF Designer
db.Database.Initialize(force: false);
// Create a SQL command to execute the sproc
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[GetAllBlogsAndPosts]";
try
{
db.Database.Connection.Open();
// Run the sproc
var reader = cmd.ExecuteReader();
// Read Blogs from the first result set
var blogs = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Blog>(reader, "Blogs", MergeOption.AppendOnly);
foreach (var item in blogs)
{
Console.WriteLine(item.Name);
}
// Move to second result set and read Posts
reader.NextResult();
var posts = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Post>(reader, "Posts", MergeOption.AppendOnly);
foreach (var item in posts)
{
Console.WriteLine(item.Title);
}
}
finally
{
db.Database.Connection.Close();
}
}
please note the important remark: The first result set must be consumed before moving to the next result set.

EF SaveChanges() throws an exception 'The EntityKey property can only be set when the current value of the property is null'

I have been reading all similar posts I found regarding EF but I can't still manage to found a solution.
I'm very new to the EF and I have been reading some useful info about working with it but I think that I'm missing something.
The scenario is like this:
I want a user to be able to create an account in an ASP.NET webpage. So I have a table named 'Accounts'. The user must agree with the condition terms of the site, that may be updated in the futere, so I have also a table called 'ConditionTerms' that has 1 to many relation with the account (many accounts have an unique condition term).
I wanted to separete the specific personal user data from the data of the account so I also created a table called 'Persons' and I set the relation ship so that a person may have many accounts.
Now, when I want to save an account into the database, I retrieve the last conditionTerm available in the database and I attach it to the account entity. Then when I try to save the data via SaveChanges() I get the exception mentioned in the title of the post. The thing is that if all entities are new, when the associations are created, the EntityState for all the items is 'Detached' and it works, but when I retrieve the existing conditionTerm from the data base and I add it to the account.ConditionTerm, the account changes its state to 'Added' and then it throws the exception.
I read somewhere that when this happens, it means that all the entity tree is considered as already added by the context and I should only need to call SaveChanges() without the AddObject() method since it is already added. I tried this and then I get no exception and the code ends, but then if I check the database (SQL Server 2008 express) the account hasn't been added at all.
This is the code I'm trying and I think it should work but it's clear that I'm missing something:
[TestMethod]
public void TestCreateNewAccount()
{
try
{
AccountRepository accountRepository = new AccountRepository();
Account account = new Account()
{
Username = "TestUsername",
Password = "TestPassword",
Email = "TestEmail",
Nickname = "TestNickName",
Quote = "Test Quote",
AgreedToTermsDate = DateTime.Now,
CreationDate = DateTime.Now,
LastUpdateTime = DateTime.Now
};
// This works (all new entities!)
//ConditionTerm conditionTerm = new ConditionTerm()
//{
// Text = "This is some test condition term.",
// CreationDate = DateTime.Now,
// LastUpdateTime = DateTime.Now
//};
//This does not work (attaching an existing entity to a new one)
ConditionTerm conditionTerm = new ConditionTermsRepository().GetCurrentTerm();
Person person = new Person()
{
FirstName = "TestName",
Surname = "TestSurname",
CreationDate = DateTime.Now,
LastUpdateTime = DateTime.Now
};
account.ConditionTerm = conditionTerm;
account.Person = person;
using (ImproveEntities entities = Connection.GetModel())
{
if (account.ID > 0)
{
Account newAccount = new Account();
newAccount.ID = account.ID;
entities.Accounts.Attach(newAccount);
entities.Accounts.ApplyCurrentValues(account);
}
else
{
entities.Accounts.AddObject(account);
entities.SaveChanges();
}
}
}
catch (Exception)
{
}
}
Any help would be very much apreciated!
EDIT: This is the GetCurrentTerm() method:
public ConditionTerm GetCurrentTerm()
{
using (ImproveEntities entities = Connection.GetModel())
{
ConditionTerm conditionTerm = (from ct in entities.ConditionTerms
orderby ct.CreationDate descending
select ct).FirstOrDefault();
return conditionTerm;
}
}
If I understand correctly you want to insert a new account along with a new user into the database. But you don't want to create a new ConditionTerm but instead assign an existing ConditionTerm to the account.
The problem is that you fetch the existing ConditionTerm in another context (in your GetCurrentTerm) than the context you use for saving the new account. This second context doesn't know anything about the ConditionTerm, so you must EF explicitely tell that this conditionTerm already exists by attaching it to the second context:
// ...
using (ImproveEntities entities = Connection.GetModel())
{
entities.ConditionTerms.Attach(conditionTerm);
account.ConditionTerm = conditionTerm;
account.Person = person;
entities.Accounts.AddObject(account);
entities.SaveChanges();
}
// ...