Using C# to Join AutoCAD Entities into a Block Programmatically - c#

I'm trying to write a method that prompts the user to select all the entities they want to combine into a block and then joins them together into a block and returns the block reference. Right now it looks like this.
/// <summary>
/// Returns all entities in an AutoCAD drawing in a list
/// </summary>
public static List<Entity> GetEntitiesInDrawing()
{
List<Entity> entitiesToReturn = new List<Entity>(); //Blocks that will be returned
Transaction tr = _database.TransactionManager.StartTransaction();
DocumentLock docLock = _activeDocument.LockDocument();
using (tr)
using (docLock)
{
BlockTableRecord blockTableRecord = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(_database), OpenMode.ForRead);
foreach (ObjectId id in blockTableRecord)
{
try
{
Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite);
entitiesToReturn.Add(ent);
}
catch (InvalidCastException)
{
continue;
}
}
}
return entitiesToReturn;
}
/// <summary>
/// Prompts the user for a number of entities and then joins them into a block
/// </summary>
public static BlockReference JoinEntities()
{
BlockReference blkToReturn = null;
List<Entity> entitiesToJoin = PromptUserForEntities();
foreach (Entity ent in entitiesToJoin)
{
// ToDo: Join entities into blkToReturn
}
return blkToReturn;
}
My problem is that I have no idea how or if it is possible to take a list of entities and join them into a blockreference.

Kean covered this in his blog: Creating an AutoCAD block using .NET

In summary:
use Editor.Getselection so the user can select the entities
create a blockTableRecord (BTR) on the BlockTable (from
Database.BlockTableId)
append all entities to the newly created BTR, here you may need to create new entities or move ownership (see BlockTableRecord.AssumeOwnershipOf method)
create a new blockreference that points to the BTR
open the Model Space (or Paper Space) and append the block reference to it
optional: erase all original entities from the model space (avoid
duplicated), if you haven't changed ownership
The post mentioned can help, but it creates new entities (and doesn't move from model to the block definition (step #3)

Related

How to create an AutoCAD object through a Lambda expression and return it

I am new-ish to C# programming (and programming in general) but I'm getting my feet wet with AutoCAD development using the AutoDesk .NET API for projects at work.
There are certain repetitive tasks in AutoCAD dev that I've been creating helper methods for to simplify my code. In order to create an object(lines, polylines, annotation, etc...) in AutoCAD through the .API, the programmer has to write a fairly convoluted statement that accesses the AutoCAD environment, gets the current drawing, gets the database of the current drawing file, starts a transaction with the database, //do work, then append the created entities to the database before finally committing and closing the transaction.
So I wrote the following code to simplify this task:
public static void CreateObjectActionWithinTransaction(Action<Transaction, Database, BlockTable, BlockTableRecord> action)
{
var document = Application.DocumentManager.MdiActiveDocument;
var database = document.Database;
using (var transaction = document.TransactionManager.StartTransaction())
{
BlockTable blocktable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord blockTableRecord = transaction.GetObject(blocktable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
action(transaction, database, blocktable, blockTableRecord);
transaction.Commit();
}
}
Then my Lambda expression that creates a generic MText and sets up some parameters for it:
public static void createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
CreateObjectActionWithinTransaction((transaction, database, blocktable, blocktablerecord) =>
{
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Location = location;
mt.Attachment = attachmentpoint;
mt.Contents = contents;
mt.Height = height;
mt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);
mt.BackgroundFill = usebackgroundmask;
mt.UseBackgroundColor = usebackgroundcolor;
mt.BackgroundScaleFactor = backgroundscale;
blocktablerecord.AppendEntity(mt);
transaction.AddNewlyCreatedDBObject(mt, true);
});
}
And then finally, when I'm actually creating MText somewhere, I can create it in one line, and pass in values for all the parameters without having to write out the huge transaction code for it:
Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);
So this is great and it works when I want to create an MText by itself and put it somewhere. However, there are certain other situations where instead of just creating an MText and placing it in the drawing, I want to create an MText using the same basic premise as above, but return it as a value to be used somewhere else.
AutoCAD has annotation objects called Multileaders which are essentially just an MText just like above, but attached to some lines and an arrow to point at something in the drawing. In the API you need to define an MText and attach it to the Multileader object. However my above code can't be used because it doesn't return anything.
So my question boils down to, how can I create a method like above to create an object, but instead of just creating that object, have it return that object to be used by another piece of code?
Also are there any good resources for beginners on Lambda expressions? Books, websites, YouTube?
Instead of using delegates, I'd rather use extension methods called from within a transaction in the calling method.
static class ExtensionMethods
{
public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead)
{
var tr = db.TransactionManager.TopTransaction;
if (tr == null)
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
return (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), mode);
}
public static void Add(this BlockTableRecord btr, Entity entity)
{
var tr = btr.Database.TransactionManager.TopTransaction;
if (tr == null)
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
btr.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
}
public static MText AddMtext(this BlockTableRecord btr,
Point3d location,
AttachmentPoint attachmentpoint,
string contents,
double height,
short color = 256,
bool usebackgroundmask = false,
bool usebackgroundcolor = false,
double backgroundscale = 1.5)
{
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Location = location;
mt.Attachment = attachmentpoint;
mt.Contents = contents;
mt.Height = height;
mt.ColorIndex = color;
mt.BackgroundFill = usebackgroundmask;
mt.UseBackgroundColor = usebackgroundcolor;
mt.BackgroundScaleFactor = backgroundscale;
btr.Add(mt);
return mt;
}
}
Using example:
public static void Test()
{
var doc = AcAp.DocumentManager.MdiActiveDocument;
var db = doc.Database;
using (var tr = db.TransactionManager.StartTransaction())
{
var ms = db.GetModelSpace(OpenMode.ForWrite);
var mt = ms.AddMtext(Point3d.Origin, AttachmentPoint.TopLeft, "foobar", 2.5);
// do what you want with 'mt'
tr.Commit();
}
}
For the AutoCAD part:
As Miiir stated in the comment, do not return an object, but rather ObjectId. An object instance belongs to a transaction, so if you open the object using some transaction, commit the transaction and try and use that object in another transaction, AutoCAD will basically just crash.
Working with AutoCAD API always follows this basic pattern:
Start transaction
Create new object or use transaction to get an existing object. This is done by either having an ObjectID or by looping over tables and looking for whatever attributes you are interested in (i.e. BlockTable, BlockTableRecord, LayerTable, etc.)
Do stuff to the object.
Commit or Abort transaction.
If you try and bypass steps 1 and 2, it will not work out so well. So, return ObjectID, and then use the id to get the object in another transaction.
As for the C# part:
If you are looking to return a value using a delegate, Action<T> is not your friend. Action does not return a value, it only "acts", thus the name. If you want to use a delegate to return a value you have 2 options:
Define a custom delegate type.
Use the generic delegate supplied by the .NET framework Func<T1,T2,T3,T4,TResult>.
Which one should you use? In your case, I'd probably go with option 1, for the simple reason that your code will just be much cleaner as easier to maintain. I will use that in this example. Using Func would work the exact same way, except your function signatures would look a bit ugly.
Custom delegate:
//somewhere in your code inside a namespace (rather than a class)
public delegate ObjectId MyCreateDelegate(Transaction transaction, Database db,
BlockTable blockTable, BlockTableRecord blockTableRecord);
Then your general method
public static ObjectId CreateObjectActionWithinTransaction(MyCreateDelegate createDel)
{
ObjectId ret;
var document = Application.DocumentManager.MdiActiveDocument;
var database = document.Database;
using (var transaction = document.TransactionManager.StartTransaction())
{
BlockTable blocktable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord blockTableRecord = transaction.GetObject(blocktable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
//here createMtext will get called in this case, and return ObjectID
ret = createDel(transaction, database, blocktable, blockTableRecord);
transaction.Commit();
}
return ret;
}
and the specific method with the lambda:
public ObjectId createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
//here you can return the result the general function
return CreateObjectActionWithinTransaction((transaction, database, blocktable, blocktablerecord) =>
{
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Location = location;
mt.Attachment = attachmentpoint;
mt.Contents = contents;
mt.Height = height;
mt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);
mt.BackgroundFill = usebackgroundmask;
mt.UseBackgroundColor = usebackgroundcolor;
mt.BackgroundScaleFactor = backgroundscale;
blocktablerecord.AppendEntity(mt);
transaction.AddNewlyCreatedDBObject(mt, true);
//make sure to get ObjectId only AFTER adding to db.
return mt.ObjectId;
});
}
And lastly, use it like this
ObjectId mtId = Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);
//now use another transaction to open the object and do stuff to it.
Learning Resources:
And lastly, to understand lambda expressions, you need to start with understanding delegates, if you don't already. All lambdas are is syntactic sugar for instantiating a delegate object that either points to a method or an anonymous method as you've done in your example. This tutorial looks pretty good. And remember, delegates such as Action, Func and Predicate, or no different. So whether you define your own delegate or use the out-of-the box solution, lambda expressions do not care.
For a lambda overview, check out this tutorial.
Do not limit yourself to the two source I provided. Just Google it, and the top 10 hits will all be fairly good information. You can also check out Pluralsight. I do a lot of my learning there.
I am not familiar with AutoCad API, but it appears that "transaction.Commit()" is the line that actually performs the action of placing the MText on your model.
if this is the case; i would do something like the following:
public MText CreateMTextObject({parameters})
{
//code
Return textObject
}
public PlaceTextObject({parameters})
{
CreateTextObject(parameters).Commit()
}
This way, you can choose to keep the textobject for further manipulation, while still allowing the option to apply it in one go. It will also have only one codeblock for making the object, making sure that there are no differences in implementation between the two methods

How do I get all the entities on a specific layer in a .DWG file without opening the file (C#.NET/AutoCAD)?

I am writing a C#.NET program that interacts with AutoCAD through the AutoCAD .NET API. The program loops through DWG files in a directory and checks every text entity on the "testLayer" layer to see if it matches "testText". I got this to work by opening up every file and making a Selectionfilter to get all of the entities on the "testLayer" layer.
Application.DocumentManager.Open(curfile.FullName, false);
....
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction acTrans = doc.TransactionManager.StartTransaction())
{
ObjectIdCollection ents = new ObjectIdCollection();
// Set up filter and filter on layer name
TypedValue[] tvs = new TypedValue[1] { new TypedValue((int)DxfCode.LayerName, "testLayer")};
SelectionFilter sf = new SelectionFilter(tvs);
PromptSelectionResult psr = ed.SelectAll(sf);
if (psr.Status == PromptStatus.OK)
{
// Get the object ids for all of the entities for the filtered layer
ents = new ObjectIdCollection(psr.Value.GetObjectIds());
foreach (ObjectId objid in ents)
{
DBText dbText = acTrans.GetObject(objid, OpenMode.ForRead) as DBText;
if (dbText.TextString.Contains("testText")
{
return dbText.TextString;
}
}
return "";
}
else
{
return "";
}
}
}
But now I am converting my program to side-load the underlying databases because it was taking too long for the program to open and close every .DWG file. The problem is that now I am using
db.ReadDwgFile(currentDWG, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
to read files without actually opening them so I can't use
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor
and
ed.SelectAll(sf) for the selection filter strategy I was using earlier because the document isn't actually open. So how can I can get all of the text entities on each layer named "testLayer" without actually opening the DWG file?
In a 'side database', to mimic SelectAll, you have to iterate through all entities in all the layouts and check the entity layer.
EDIT: In a 'side database', to mimic SelectAll, you have to iterate through all entities in all the layouts and check the entity type and layer.
private IEnumerable<ObjectId> GetTextEntitiesOnLayer(Database db, string layerName)
{
using (var tr = db.TransactionManager.StartOpenCloseTransaction())
{
var blockTable = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
foreach (ObjectId btrId in blockTable)
{
var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
var textClass = RXObject.GetClass(typeof(DBText));
if (btr.IsLayout)
{
foreach (ObjectId id in btr)
{
if (id.ObjectClass == textClass)
{
var text = (DBText)tr.GetObject(id, OpenMode.ForRead);
if (text.Layer.Equals(layerName, System.StringComparison.CurrentCultureIgnoreCase))
{
yield return id;
}
}
}
}
}
}
}

Autocad API detect shapes inside layer

I am using Autocad 2012 with the API provided. I am developing in c#.
What I am trying to do is select a certain layer, and "detect" all rectangles / squares in that layer. Ultimateley, I would like to be able to draw inside of all of those rectangles that I have "detected" (using their coordinates).
So far, I am using the LayerTable class along with GetObjects to associate layers with objects, like so:
LayerTable layers;
layers = acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForRead) as LayerTable;
String layerNames = "";
foreach (ObjectId layer in layers)
{
LayerTableRecord layerTableRec;
layerTableRec = acTrans.GetObject(layer, OpenMode.ForRead) as LayerTableRecord;
layerNames += layerTableRec.Name+"\n";
}
I can't seem to figure out where to go from here though. How to select just one layer, and then detect shapes inside of it. Can someone point me in the correct direction, in terms of what classes / methods to look into? Thanks.
Ultimately, you need to take another look at the AutoCAD object model. The BlockTableRecord "ModelSpace" is what will contain all* of the AutoCAD entities that have layer assignments. Once you have the BlockTableRecord open for read, you can filter down to entities matching whatever layers you're interested in. LINQ can come in handy here.
You don't actually care about the layer's objectID in this instance, just the name. You only really open up the LayerTableRecord when you want to change a layer. If you'll be changing entity properties, you really need to familiarize yourself with the Transaction class. There's also a faster alternative to using 'As' in AutoCAD by leveraging RXObject.GetClass().
*Entities can also live in other BlockTableRecords (any additional layouts for example) but for now you'll likely be fine with just modelspace.
Here's a little snippet to get you started:
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
if (!id.ObjectClass.IsDerivedFrom(entClass)) // For entity this is a little redundant, but it works well with derived classes
continue;
var ent = (Entity)tr.GetObject(id, OpenMode.ForRead)
// Check for the entity's layer
// You'll need to upgrade the entity to OpenMode.ForWrite if you want to change anything
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
Entity acEnt = (Entity)tr.GetObject(id, OpenMode.ForRead);
string layerName = acEnt.Name;
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}

What is the best approach to saving/updating entities using Entity Framework retrieved from a XML feed?

Background:
Simply put, I am currently developing an app using ASP.NET MVC and Entity Framework that pulls data from an XML feed on a periodic basis and saves the data to a database, adding new records and/or updating existing ones.
My current approach is to retrieve the XML feed (using XmlReader to deserialize the XML data into classes created from the xsd.exe tool). Then I iterate through the collection of XML data retrieved, create and hydrate EF classes/entities (created via EF Power Tools and the Reverse Engineer Code First approach) and save each of these new/updated entities to the database.
Example
In this example I am dealing with retreiving locations. The DB has a Location table, and a LocationType table, with a one-to-many relationship between LocationType and Location. Location has a foreign key constraint on Location.LocationTypeId = LocationType.LocationTypeId.
I need to verify whether the XML location exists in the database so I first retrieve it using the XML feed location ID: if it's null the I'm dealing with a new location; if it's not null then I'm dealing with an existing location and I need to update it.
// LOCATION SERVICE
private void LoadLocations()
{
// retreive XML location data
List<locationsLocation> locationsFeed = _xmlFeedRepository.GetLocations().ToList();
// iterate through each location and save to DB
foreach (var fl in locationsFeed)
{
// get location from DB using XML location feedId
var location = _locationRepository.GetLocationByFeedId(fl.id);
if (location == null)
{
// add location
HydrateLocation(ref location, fl);
_locationRepository.AddLocation(location);
}
else
{
// update location
HydrateLocation(ref location, fl);
_locationRepository.UpdateLocation(location);
}
}
}
private void HydrateLocation(ref Location location, locationsLocation fl)
{
if (location == null)
{
// create new location
location = new Location();
}
// get location type
var locationType = _locationRepository.GetLocationTypeByName(fl.type);
location.Name = fl.name;
location.FeedId = fl.id;
// add existing locationType or create new locationType
location.LocationType = locationType ?? new LocationType { Name = fl.type };
}
// LOCATION REPOSITORY
public void AddLocation(Location location)
{
if (location != null)
{
using (var context = new MyDBContext())
{
context.Locations.Add(location);
context.SaveChanges();
}
}
}
public void UpdateLocation(Location location)
{
if (location != null)
{
using (var context = new MyDBContext())
{
context.Locations.Attach(location);
context.Entry(location).State = EntityState.Modified;
context.SaveChanges();
}
}
}
public Location GetLocationByFeedId(int feedId)
{
Location location = null;
if (feedId > 0)
{
using (var context = new MyDBContext())
{
location = context.Locations.FirstOrDefault(l => l.FeedId == feedId);
}
}
return location;
}
Question/Concern
Is this the correct way to add/update an entity that has related entities, e.g., adding/update a location and its locationType? Can anyone suggest a preferred way of doing this?
There were a few problems with this solution, which I managed to track down after some guidance from #JulieLerman, and some help from the superb Entity Framework Profiler by Hibernating Rhinos (highly recommend):
Given the number of records with the initial bulk import (approx. 20K rows), executing _locationRepository.GetLocationByFeedId(fl.id); for each XML entity was way overkill. I have since reworked the solution completely, but a better solution would be to do one call to the DB and pull out all locations and store them in memory and use the in memory collection to check whether the location already exists. The same applies for _locationRepository.GetLocationTypeByName(fl.type);
Setting the locationType using location.LocationType = locationType ?? new LocationType { Name = fl.type }; could and probably would result in duplicate records (since EF would thing the locationType was a new locationType). It is safer to only set the locationType foreign key of the location entity instead, e.g. location.LocationTypeId = locationType.locationTypeId
Instead of wrapping each call to the context in a using block, a better approach would be to declare a private instance of the context in the repository, and declaring a separate SaveChanges() method.

Entity framework 4 CRUD creating errors

I have 3 related tables in my database.
Farm ----> FarmCrops <----- Crops
I'm trying to update the a farm entity with a collection of crops but am running into problems. I've been working on this with no success for hours now so any help would be greatly appreciated.
The error I'm receiving is this:
The object cannot be attached because
it is already in the object context.
An object can only be reattached when
it is in an unchanged state.
My update logic is as follows (my apologies for the large amount of code. I'd just like to be as clear as possible):
bool isNew = false;
Farm farm;
// Insert or update logic.
if (viewModel.Farm.FarmId.Equals(Guid.Empty))
{
farm = new Farm
{
FarmId = Guid.NewGuid(),
RatingSum = 3,
RatingVotes = 1
};
isNew = true;
}
else
{
farm = this.ReadWriteSession
.Single<Farm>(x => x.FarmId == viewModel.Farm.FarmId);
}
// Edit/Add the properties.
farm.Name = viewModel.Farm.Name;
farm.Owner = viewModel.Farm.Owner;
farm.Address = viewModel.Farm.Address;
farm.City = viewModel.Farm.City;
farm.Zip = viewModel.Farm.Zip;
farm.WebAddress = viewModel.Farm.WebAddress;
farm.PhoneNumber = viewModel.Farm.PhoneNumber;
farm.Hostel = viewModel.Farm.Hostel;
farm.Details = viewModel.Farm.Details;
farm.Latitude = viewModel.Farm.Latitude;
farm.Longitude = viewModel.Farm.Longitude;
farm.Weather = viewModel.Farm.Weather;
// Add or update the crops.
string[] cropIds = Request.Form["crop-token-input"].Split(',');
List<Crop> allCrops = this.ReadWriteSession.All<Crop>().ToList();
if (!isNew)
{
// Remove all previous crop/farm relationships.
farm.Crops.Clear();
}
// Loop through and add any crops.
foreach (Crop crop in allCrops)
{
foreach (string id in cropIds)
{
Guid guid = Guid.Parse(id);
if (crop.CropId == guid)
{
farm.Crops.Add(crop);
}
}
}
if (isNew)
{
this.ReadWriteSession.Add<Farm>(farm);
}
else
{
this.ReadWriteSession.Update<Farm>(farm);
}
this.ReadWriteSession.CommitChanges();
My update code within the ReadWriteSession is simple enough (GetSetName<T> just returns the types name from it's PropertyInfo.):
/// <summary>
/// Updates an instance of the specified type.
/// </summary>
/// <param name="item">The instance of the given type to add.</param>
/// <typeparam name="T">The type of entity for which to provide the method.</typeparam>
public void Update<T>(T item) where T : class, new()
{
this.context.AttachTo(this.GetSetName<T>(), item);
this.context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
}
You are adding existing Crop objects (from the allCrops list) to the new Farm. When you connect a new entity to an existing one, the new entity automatically gets attached to the context. Therefore you get the error when you try to attach the Farm to the context the second time.
The Add<Farm>(farm) statement in your code is not even necessary to connect the Farm to the context, and if you have an existing Farm that is loaded from the context, it is already attached to the context.
The whole of your if (isNew) statement is unnecessary. Entity framework tracks object state itself, so you don't need to set the modified state.
you don't have to attach the "farm" object at the end, because it's already attached as modified when you change one of its properties. try removing the else statement at the end:
if (isNew)
{
this.ReadWriteSession.Add<Farm>(farm);
}
Hope this helps :)
The problem is in your update method. You can't attach the Farm instance because you have loaded it from the same context so it is already attached and you don't need to call your Update at all because changes to attached objects are tracked automatically.

Categories

Resources