c# autocad sideload database binding xrefs - c#

i am trying to bind xrefs in a sideload drawing database. the program is halting at this line ' if(!xNode.Database.Filename.Equals(NewDb.Filename))'. i am also receiving this error 'System.NullReferenceException: Object reference not set to an instance of an object.at XBind.RecursiveFileProcessor.ProcessFile(String path).' i've done some reaserch and found VB.NET code to attach a xref and tried to extrapolate that with no success. i'd appreciate someone pointing me in the right direction on this.
using (Database NewDb = new Database(false, true))
{
NewDb.ReadDwgFile(path, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
NewDb.CloseInput(true);
using (Transaction tr = NewDb.TransactionManager.StartTransaction())
{
ObjectIdCollection xrefCollection = new ObjectIdCollection();
XrefGraph xg = NewDb.GetHostDwgXrefGraph(false);
int numOfNodes = xg.NumNodes;
for (int cnt = 0; cnt < xg.NumNodes; cnt++)
{
XrefGraphNode xNode = xg.GetXrefNode(cnt) as XrefGraphNode;
if (!xNode.Database.Filename.Equals(NewDb.Filename))
{
if (xNode.XrefStatus == XrefStatus.Resolved)
{
xrefCollection.Add(xNode.BlockTableRecordId);
}
}
}
if (xrefCollection.Count != 0)
{
NewDb.BindXrefs(xrefCollection, true);
}
tr.Commit();
}
NewDb.SaveAs(path, DwgVersion.Current);
}

Actually, this will work in-memory. Winslow North missing the following line of code after the CloseInput()...
NewDb.ResolveXrefs(true, false);
But also, you do not need the Transaction for this. It's not necessary. I created my own sample and tested it. It works. If you need me to post that, let me know. The problem was that the xNode had a null database due to the fact that the Xref was not resolved. You have to do that manually with the line above.

Don't believe this will work for in-memory database, you may try this approach, see a pice of it below:
[CommandMethod("CHX")]
public void ChangeXref()
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
var ed = doc.Editor;
var db = doc.Database;
// Get the database associated with each xref in the
// drawing and change all of its circles to be dashed
using (var tr = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
// Loop through the contents of the modelspace
foreach (var id in ms)
{
// We only care about BlockReferences
var br = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (br != null)
{
// Check whether the associated BlockTableRecord is
// an external reference
var bd = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForRead);
if (bd.IsFromExternalReference)
{
// If so, get its Database and call the function
// to change the linetype of its Circles
var xdb = bd.GetXrefDatabase(false);
if (xdb != null)
{
using (var xf = XrefFileLock.LockFile(xdb.XrefBlockId))
{
// Make sure the original symbols are loaded
xdb.RestoreOriginalXrefSymbols();
xdb.RestoreForwardingXrefSymbols();
}
}
}
}
}
tr.Commit();
}
}

Related

What is the most efficient way to validate a SelectImplied against a Filter?

Here is some simple code for asking the user to select some LINE and / or ARC entities:
_AcDb.TypedValue[] dxfs = new _AcDb.TypedValue[]
{
new _AcDb.TypedValue((int)_AcDb.DxfCode.Operator, "<or"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Start, "LINE"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Start, "ARC"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Operator, "or>"),
};
_AcEd.SelectionFilter sFilter = new _AcEd.SelectionFilter(dxfs);
_AcEd.PromptSelectionOptions pso = new _AcEd.PromptSelectionOptions
{
MessageForAdding = "Select LINES and/or ARCS",
MessageForRemoval = "Remove LINES and/or ARCS",
AllowDuplicates = false
};
_AcEd.PromptSelectionResult res = editor.GetSelection(pso, sFilter);
if (res.Status == _AcEd.PromptStatus.OK)
Now, suppose be modify our tool so that it uses CommandFlags.UsePickSet. Now I can test for an existing selection set:
_AcEd.PromptSelectionResult res = editor.SelectImplied();
If the implied selection set result is OK, how can we easily validate that selection set against our filter? Afterall, the user might accidentally pick up a CIRCLE which we would want to ignore.
I can confirm that the answer here (Filter Pickfirst Selectionset) is still correct. To quote:
With CommandFlags.UsePickSet, the selection filter passed to the EditorGetSelection() method is automatically applied to the active selection if any.
I repeat the code snippet in case the link breaks:
[CommandMethod("Test", CommandFlags.UsePickSet)]
public void Test()
{
Document doc = AcAp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
TypedValue[] filter = { new TypedValue(0, "INSERT") };
PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
if (psr.Status != PromptStatus.OK) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
foreach (SelectedObject obj in psr.Value)
{
BlockReference br = (BlockReference)tr.GetObject(obj.ObjectId, OpenMode.ForWrite);
br.Color = Color.FromColorIndex(ColorMethod.ByAci, 30);
}
tr.Commit();
}
}
All we need to do is add the CommandFlags.UsePickSet and the system will take care of the rest (using your filter). Cool.

Getting double data in DB

I pull student data from 2 databases. 1 from an online SOAP API which can handle async calls and 1 from a local DB with an older services that doesnt support async.
I compare these databases and write the differences in a local sqlDB through EF.
Problem:
I get double entries in my EF DB. He puts the correct data and amount in arrays inside the method, but it looks like once he hits the db.savechanges() he jumps back up a few line and saves again.
I don't even know where this extra thread comes from.
Some code might be still there from numerous tries to solve it. For instance I tried with addrange but I get an error when he tries to add the FullVarianceList.
public async Task<bool> FullStudentCompare(string date) //format DD/MM/YYYY
{
try
{
//DB context
using (var db = new SchoolDbContext())
{
//GET DATA
//SMT (async)
List<SmtStudent> smtStdudentList = await GetAllSmartschoolStudents();
//Wisa (sync)
//on date, or if emty on current systemdate
List<WisaStudent> wisaList;
if (date == "")
{
wisaList = GetWisaStudentData(DateTime.Now.ToShortDateString());
}
else
{
wisaList = GetWisaStudentData(date);
}
//Flags and props needed for DB entry after compare
bool existsInLocalDb = false;
List<Variance> vList = new List<Variance>();
//Full list to add to DB outside foreach
List<Variance> fullVarianceList = new List<Variance>();
//Full List of new Students to write to DB outside foreach
List<DbStudent> fullStudentList = new List<DbStudent>();
//Compare lists
foreach (WisaStudent wstd in wisaList)
{
//determine correct classCode
string klasCode;
if (wstd.klasgroep.Trim() == "Klasgroep 00")
{
klasCode = wstd.klas.Trim();
}
else
{
klasCode = wstd.klasgroep.Trim();
}
//Create SmtStudent object for compare
SmtStudent tempStd = new SmtStudent(true,
wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
//Find matching SmtStudent
SmtStudent smtStd = smtStdudentList.Find(i => i.Internnummer == wstd.stamboeknummer);
//Find matching Std in local DB
DbStudent dbStd = await db.Students.Where(i => i.Stamboeknummer == wstd.stamboeknummer).FirstOrDefaultAsync();
//if none exists in the local DB create an entity to update and write to DB
if (dbStd == null)
{
dbStd = new DbStudent(wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.loopbaanDatum,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
db.Students.Add(dbStd);
fullStudentList.Add(dbStd);
}
else
{
existsInLocalDb = true;
}
if (smtStd == null)
{
//Std doesn't exist in Smt -> New student
dbStd.IsNewStudent = true;
dbStd.ClassMovement = true;
//remove from wisaList
wisaList.Remove(wstd);
}
else
{
//clear vlist from previous iterations
vList.Clear();
//get all properties on the obj, cycle through them and find differences
PropertyInfo[] props = smtStd.GetType().GetProperties();
vList.AddRange(props.Select(f => new Variance
{
Property = f.Name,
ValueA = f.GetValue(smtStd),
ValueB = f.GetValue(tempStd),
Checked = false
})
.Where(v => !v.ValueA.Equals(v.ValueB) && v.ValueB != null)
.ToList());
//If the users allrdy exists in LocalDb delete all previously recorded variances
if (existsInLocalDb)
{
if (db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).FirstOrDefault() != null)
{ //if the student allready exists we will recreate the variancelist, hence deleting all current items first
List<Variance> existingList = db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).ToList();
foreach (Variance v in existingList)
{
db.Variances.Remove(v);
}
}
}
//Add new variances if vList is not empty
if (vList.Count > 0)
{
//Check if KlasCode is a variance -> set classmovement to true
if (vList.Where(i => i.Property == "KlasCode").FirstOrDefault() != null)
{
dbStd.ClassMovement = true;
}
else
{
dbStd.ClassMovement = false;
}
//add the StudentObject to the variance to link them 1-many
foreach (Variance v in vList)
{
v.Student = dbStd;
fullVarianceList.Add(v);
db.Variances.Add(v);
}
}
}
}
//add the full lists of variances and new students to DB
//db.Variances.AddRange(fullVarianceList);
//db.Students.AddRange(fullStudentList);
db.SaveChanges();
return true;
}
}
catch(Exception ex)
{
return false;
}
}
A couple of things:
It is important to understand that EF uses a unit of work pattern where none of the changes to the entities are persisted until SaveChanges is called which explains the "once he hits the db.Savechanges() he jumps back up" phenomenon.
When you have a 1 to many relationsship and you assign a collection of entities to a navigation property on another entity and then add that parent entity to the DbContext, EF marks those child entities to be added too. In your case dbStd is added at the line "db.Students.Add(dbStd);" and at the line "v.Student = dbStd;". This is most likely what is causing your duplicates.

Autocad C# delete layout viewports

Can anyone help me to create a C# code to delete the viewport in layouts.
I've tried a code to delete the viewport, it compiles no problem, but it doesn't delete the viewport, I am not sure what I am doing wrong here.
Thanks
public class Class1
{
[CommandMethod("haha")]
public static void CreateModelViewports()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
var viewportTable = (ViewportTable)trans.GetObject(db.ViewportTableId, OpenMode.ForWrite);
foreach (ObjectId id in viewportTable)
{
var viewport = (ViewportTableRecord)trans.GetObject(id, OpenMode.ForRead);
// Delete the active viewport
viewport.UpgradeOpen();
viewport.Erase();
}
trans.Commit();
}
}
}
You are erasing a ViewportTableRecord not a Viewport.
You need to get BlockTableRecordId from the Layout you want to erase its viewports. Then iterate through every ObjectId in that BlockTableRecord to see if it is a Viewport. Only then you can delete them
So if you want to delete all Viewports in the current layout you do something like this (You must be in paper space for this to work, because it doesn't check)
[CommandMethod("GOO")]
public static void test()
{
Database DB = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction trans = DB.TransactionManager.StartTransaction())
{
LayoutManager LM = LayoutManager.Current;
string currentLo = LM.CurrentLayout;
DBDictionary LayoutDict = trans.GetObject(DB.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
Layout CurrentLo = trans.GetObject((ObjectId)LayoutDict[currentLo], OpenMode.ForRead) as Layout;
BlockTableRecord BlkTblRec = trans.GetObject(CurrentLo.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
foreach (ObjectId ID in BlkTblRec)
{
Viewport VP = trans.GetObject(ID, OpenMode.ForRead) as Viewport;
if (VP != null)
{
VP.UpgradeOpen();
VP.Erase();
}
}
trans.Commit();
}
}

How can I pass a function with one parameter to an ICommand?

Here's my ICommand:
public ICommand ConfirmLotSavedCommand {
get
{
return new RelayCommand(ConfirmLotSaved);
}
}
The problem is I have deserialized data that I want to store into database after a user clicks confirm button. If the user does not click on confirm or the lot number already exists, then I don't want to save the deserialized string in db.
I had trouble calling a function with one parameter inside my ConfirmLotSaved() method because of scope.
So I created a set the deserialized lot as a field and put the code to save to db inside of ConfirmLotSaved(). However, the field is null for some strange reason... I'm not sure why.
Here's my attempt:
private LotInformation lot; //field that is supposed to contain all the deserialized info
private void ConfirmLotSaved()
{
using (var db = new DDataContext())
{
bool lotNumDbExists = db.LotInformation.Any(r => r.lot_number == DeserialLotNumber);
if (lotNumDbExists == false)
{
successWindow.Message = "Successfully Saved Lot";
dialogService.ShowDialog(successWindow.Message, successWindow);
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = Lot.lot_number;
ExpirationDate = Lot.exp_date.ToString();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
else if (lotNumDbExists == true)
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
}
Deserialization function to see where lot is grabbing data:
public void DeserializedStream(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
LotInformation lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
LotInformation newList = new LotInformation();
using (var db = new DDataContext())
{
bool isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (newList != null && isDuplicate == false)
{
newList.Id = lot.Id;
newList.lot_number = lot.lot_number;
newList.exp_date = lot.exp_date;
DeserialLotNumber = newList.lot_number;
DeserialExpirationDate = newList.exp_date.ToString();
foreach (Component comp in lot.Components)
{
newList.Components.Add(comp);
}
DeserialComponentsList = newList.Components;
foreach (Families fam in lot.Families)
{
newList.Families.Add(fam);
}
DeserialFamiliesList = newList.Families;
}
else if (isDuplicate == true)
{
DeserialAnalytesList = null;
DeserialFamiliesList = null;
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
I figured out what was wrong:
After setting private LotInformation lot; field before constructor, I redeclared locally my mistake:
LotInformation lot = new LotInformation();
Changed it to:
lot = new LotInformation();
and it works.
I suggest you to use RelayCommand's generic edition http://www.kellydun.com/wpf-relaycommand-with-parameter/
It will allow you to pass lot to your command from view, all you need to store lot in current DataContext.

SQL Server CE EF Optimization

I have the following code which updates / adds data to a SQL Server CE database with EF6, which is working fine for small number of records. However when the volume of records exceeds 1000~2000 the transaction become very slow (10~15sec). Is there any way to optimize it?
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", MyProject.ConnectionString);
ProjectContext context = new ProjectContext();
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
using (var db = new ProjectContext())
{
foreach (var item in MyProject.Brands)
{
if (!db.Brands.Any(i => i.Name == item.Name))
{
// Add
db.Brands.Add(item);
}
else
{
// Update
var found = db.Brands.First(i => i.Name == item.Name);
found = item;
}
}
db.SaveChangesAsync();
This way you save 50% on your Linq queries, and your update might actually work (your MyProject.Brands are not attached in any way to your new ProjectContext that you are using, the found that you extract from there is attached, but in your original code, you overwrote it with yuor item, meaning it will be ignored.
context I have left out completely, as you were not doing anything with it, as your using creates a new ProjectContext anyway.
using (var db = new ProjectContext())
{
foreach (var item in MyProject.Brands)
{
var found = db.Brands.FirstOrdefault(i => i.Name == item.Name);
if (found == null)
{
// Add
db.Brands.Add(item);
}
else
{
// Update
found.prop1 = item.prop1;
found.prop2 = item.prop2;
// ... etc, for all and any updatable properties
}
}
db.SaveChangesAsync();
}

Categories