Learning C# should this be fixed or left alone? I can't seem to find an answer that works every action in VS didn't resolve the issue answer's I found online I either didn't understand or failed as well. Why I am asking here.
`
public class AdventureService : IAdventureService
{
public Adventure GetInitialAdventure()
{
var basePath = $" {AppDomain.CurrentDomain.BaseDirectory}Adventures";
var initialAdventure = new Adventure();
if (File.Exists($"{basePath}\\initial.json"))
{
var directory = new DirectoryInfo(basePath);
var initialJsonFile = directory.GetFiles("initial.json");
using (StreamReader fi = File.OpenText(initialJsonFile[0].FullName))
{
initialAdventure = JsonConvert.DeserializeObject<Adventure>(fi.ReadToEnd());
}
}
return initialAdventure;
}
`
You need to decide what the method will do if the deserialize call returns null.
One option is for the method to return null, in which case you just need to change the return type:
public Adventure? GetInitialAdventure()
{
//Etc.
If you prefer that the method never return null, you could change the return statement so that it replaces a null with a new Adventure.
//Rest of method up here
return initialAdventure ?? new Adventure();
}
There are maybe other options as well but these are the basics.
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
On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.
I am trying to get records from an AS400 system into Dynamics CRM programatically. To achieve this i have pushed the AS400 records into a SQL table and am able to push those records to CRM by referencing the CRM 4 web service endpoints in a SSIS 2008 C# script.
The problem is one of the fields is in Y2K date string format. In order to get it into a date field (D.O.B) in CRM i believe i will need to convert it to a date format then reference resulting value in a variable.
I do not know how to do this.
This question/answer (http://stackoverflow.com/a/4880021/1326443) may help with part of the question but i do not know how to use this into my script to get a value (haven't done any scripting for a number of years and new to C#)
Script snippet:
public class ScriptMain : UserComponent
{
private CrmService service = null;
public override void PreExecute()
{
base.PreExecute();
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = "DevOrg";
service = new CrmService();
service.Url = "http://crm/mscrmservices/2007/crmservice.asmx";
service.CrmAuthenticationTokenValue = token;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
public override void PostExecute()
{
base.PostExecute();
}
public override void LeadInput_ProcessInputRow(LeadInputBuffer Row)
{
lead cont = new lead();
if (!Row.TITL20_IsNull)
{
cont.salutation = Row.TITL20;
}
if (!Row.DOBI20_IsNull)
{
cont.new_birthdate = Row.DOBI20;
}
....
....
service.Create(cont);
}
}
}
{ cont.new_birthdate = Row.DOBI20; } throws:
cannot implicitly convert type 'string' to .....CrmSdk.CRMDateTime
Just had a look at the documentation for CRMDateTime (http://msdn.microsoft.com/en-us/library/bb928935.aspx)
This states that you can set this using the Value property (http://msdn.microsoft.com/en-us/library/bb928944.aspx)
So you might like to try:
cont.new_birthdate.Value = Row.DOBI20
Edit
In response to your comments, try the following
string ConvertDate(string dateToConvert)
{
dateToConvert= dateToConvert.PadLeft(7, '0');
int c;
int.TryParse(dateToConvert.Substring(0,1), out c);
c = (c * 100) + 1900;
int y;
int.TryParse(dateToConvert.Substring(1,2), out y);
return (c+y).ToString() + dateToConvert.Substring(3,4);
}
I'm creating a custom workflow activity in VS2010 targeting .NET 3.5. The DLL is actually being used in a Microsoft System Center Service Manager custom workflow, but I don't think that is my issue.
I have a public string property, that the user types in the string of what the activity should use. However, when the WF runs, it errors out 'value cannot be null'. I want to target if it is my code or something else.
When we drag my custom activity onto the designer, I'm able to type in the text of the string on the designer for that property.
public static DependencyProperty ChangeRequestStageProperty = DependencyProperty.Register("ChangeRequestStage", typeof(String), typeof(UpdateChangeRequestStage));
[DescriptionAttribute("The value to set the ChangeRequestStage Property in the ChangeRequest Extension class.")]
[CategoryAttribute("Change Request Extension")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public String Stage
{
get { return ((String)(base.GetValue(UpdateChangeRequestStage.ChangeRequestStageProperty))); }
set { base.SetValue(UpdateChangeRequestStage.ChangeRequestStageProperty, value); }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
EnterpriseManagementGroup emg = CreateEMG();
//System.WorkItem.ChangeRequest Extension - ClassExtension_928bec0a_cac4_4a0a_bd89_7146c9052fbe
ManagementPackClass mpcChangeRequest = emg.EntityTypes.GetClass(new Guid("8c6c6057-56ad-3862-47ec-dc0dde80a071"));
//System.WorkItemContainsActivity Relationship Class
ManagementPackRelationship workItemContainsActivityRelationship = emg.EntityTypes.GetRelationshipClass(new Guid("2DA498BE-0485-B2B2-D520-6EBD1698E61B"));
EnterpriseManagementObject changeRequest = null;
//Loop thru each emo (Change Request in this case), and assign it. There will never be more than 1 emo returned
foreach (EnterpriseManagementObject obj in emg.EntityObjects.GetRelatedObjects<EnterpriseManagementObject>(executionContext.ContextGuid, workItemContainsActivityRelationship, TraversalDepth.OneLevel, ObjectQueryOptions.Default))
{ changeRequest = obj; }
EnterpriseManagementObjectProjection emop = new EnterpriseManagementObjectProjection(changeRequest);
if (emop != null)
{ emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage; }
emop.Commit();
return base.Execute(executionContext);
}
Since it is getting a 'value cannot be null' error, I'm guessing it's on this line:
emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage;
I'm going to test and see if hardcoding a value works or not. Any ideas?
enter code here
try this
if (emop != null && emop.Object[mpcChangeRequest, "ChangeRequestStage"] != null)
emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage
I didn't want to leave this question wide open, so I'm updating it as to how I resolved this (a long time ago).
Rather than working with an EnterpriseManagementObjectProjection (emop), I worked with a standard EnterpriseManagementObject (emo). From there, I was able to follow a similar format from above:
ManagementPackClass mpcChangeRequest = emg.EntityTypes.GetClass(new Guid("8c246fc5-4e5e-0605-dc23-91f7a362615b"));
changeRequest[mpcChangeRequest, "ChangeRequestStage"].Value = this.Stage;
changeRequest.Commit();