Well, in your original post you did not show exactly the command method that put the 2 code snippets together, wrapped in one transaction, that fails, as you claimed. The code in the your latest post you also did not show the situation of how the code fails. But I guessed I knew what you meant, so I went ahead for a test run with exactly with your 2 code snippets and put them in ONE TRANSACTION in a commandmethod, as following:
public class OpenLayoutTest
{
[CommandMethod("CreateMyLayout")]
public static void CreateLayouts()
{
var dwg = CadApp.DocumentManager.MdiActiveDocument;
var ed = dwg.Editor;
using (var tran = dwg.TransactionManager.StartTransaction())
{
var lNames = dwg.Database.GetLayoutNames();
var newLayoutId = CreateAndMakeLayoutCurrent(
LayoutManager.Current, "New Layout");
tran.Commit();
}
}
public static ObjectId CreateAndMakeLayoutCurrent(
LayoutManager lm, string name, bool select = true)
{
//try and get Layout
ObjectId id = lm.GetLayoutId(name);
try
{
if (!id.IsValid)
{
id = lm.CreateLayout(name);
}
lm.CurrentLayout = name;
}
catch (System.Exception ex)
{
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager
.MdiActiveDocument.Editor.WriteMessage("\n" + ex.Message);
}
return id;
}
}
public static class DBExtensions
{
public static List<string> GetLayoutNames(this Database db)
{
var existSheetNames = new List<string>();
var layoutDict = db.LayoutDictionaryId.GetObject(OpenMode.ForRead) as DBDictionary;
foreach (var layout in layoutDict)
{
existSheetNames.Add(layout.Key);
}
return existSheetNames;
}
}
As you can see, in the CommandMethod class, I simply wrapped your 2 pieces of code in a transaction. The code runs without error: the first run creates a new layout ("New Layout"); the following run, make the "New Layout" current, if it wasn't. No error. I tested with Acad2018, but would not think it would be otherwise with 2017, 2019, or whatever Acad version.
So, since you did not show full code that led to error, I suspect there could be other reasons that causes error. For example, LayoutManager.Current.CurrentLayout = "xxxx" would raise exception if called in ApplicationContext without current document being locked.
There are also a few things:
1. In your latest code, you have
using (var tr = db.TransactionManager.StartTransaction())
{
existLayoutNames = db.GetLayoutNames();
}
It is not a good practice to start a transaction (even for reading only) and not commit it.
2. If you create an extension method to get data from whatever object (in your case, it is Database), it would be better to make it self-contained, and no dependency to outside environment. And if there is dependency, then you add argument to the method so that the dependency could be injected when the method is calling. Here I am talking about the need to using a Transaction to wrap your extension method call, which is not good design, IMO. you should:
public static List<string> GetLayoutNames(this Database db)
{
var existSheetNames = new List<string>();
using (var tran = db.TransactionManager.StartTransaction())
{
var layoutDict = db.LayoutDictionaryId.GetObject(OpenMode.ForRead) as DBDictionary;
foreach (var layout in layoutDict)
{
existSheetNames.Add(layout.Key);
}
tran.Commit();
}
return existSheetNames;
}
3. When using LayoutManager, there is no need to wrap its method calls with Transaction, it methods internally starts transaction and commit it as needed
With the suggestion I made in 2 and 3, the command method I showed on top could be simplified to:
[CommandMethod("CreateMyLayout")]
public static void CreateLayouts()
{
var dwg = CadApp.DocumentManager.MdiActiveDocument;
var ed = dwg.Editor;
var lNames = dwg.Database.GetLayoutNames();
var newLayoutId = CreateAndMakeLayoutCurrent(
LayoutManager.Current, "New Layout");
}
That is, no need to wrap the Database extension method GetLayoutNames() and CreateAndMakeLayoutCurrent(). This, of course works with my test (Acad2018).
Again, I have no idea where the error you saw came from.