I'm working with MFiles API...
I want to pass a propertyDef to a propertyValue...
This code is working... but I have to create the MFiles object first.
ObjectVersionAndProperties objVersion =
mFilesStructure.MFileVault.ObjectOperations.CreateNewObject(objTypeID,
propValues);
var testPropValues = new PropertyValues();
testPropValues = FilesStructure.MFileVault.ObjectPropertyOperations.GetProperties(objVersion.ObjVer);
var testPropValue = new PropertyValue();
testPropValue = testPropValues.SearchForProperty(typeClientID);
it work fine "testPropValue" has all the property set correctly espacially the DataType... but don't want to create the MFiles at first...
This should do the same, in my opinion but doesn't
var test = new PropertyDef();
test = mFilesStructure.MFileVault.PropertyDefOperations.GetPropertyDef(typeClientID);
var testPropValue = new PropertyValue();
testPropValue.PropertyDef = test.ID;
the properties doesn't setup correctly...
Any one can help
Best regards,
Steph
I just stumbled across this looking for something else and thought I might help.
You actually have it a little backwards. The creation of the new object is actually the last step in the process. You need to create a collection of PropertyValues() by creating each individual PropertyValue() and then adding them to the collection.
So something like this:
public static PropertyValue GetPropertyValue(int propertyDefId, object value)
{
//resolve property def by ID
PropertyDef propertyDef = Vault.PropertyDefOperations.GetPropertyDef(propertyDefId);
//create the property value with prop def ID and value
return GetPropertyValue(propertyDefId, propertyDef.DataType, value);
}
public static PropertyValue GetPropertyValue(int propertyDefId, MFDataType dataType, object value)
{
PropertyValue propertyValue = new PropertyValue();
propertyValue.PropertyDef = propertyDefId;
propertyValue.TypedValue.SetValue(dataType, value);
return propertyValue;
}
public static ObjectVersionAndProperties CreateDocument(PropertyValues propertyValues, string filepath, Vault vault)
{
// Create the Source File object from the filepath.
SourceObjectFile sourceFile = new SourceObjectFile();
sourceFile.SourceFilePath = filepath;
sourceFile.Extension = Path.GetExtension(filepath).TrimStart('.');
sourceFile.Title = Path.GetFileNameWithoutExtension(filepath).TrimEnd('.');
// Create the document object.
return vault.ObjectOperations.CreateNewSFDObject((int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument,
propertyValues, sourceFile, true);
}
Once you set up the above functions you could call them like:
//If the document doesn't exist, go ahead and create a new one
//creat and add all the properties
PropertyValues props = new PropertyValues();
//class
props.Add(-1, HelperMF.GetClassPropertyValue(classId, env.Vault));
//job
int jobId = env.Vault.ValueListItemOperations.GetValueListItemByDisplayID(Structure.ObjType.Job.ID, jobDisplayId).ID;
props.Add(-1, HelperMF.GetPropertyValue(Properties.Job.ID, jobId, env.Vault));
//estimates
props.Add(-1, HelperMF.GetPropertyValueFromListOfDisplayIds(env.Vault, Properties.Estimate.ID,
MFDataType.MFDatatypeMultiSelectLookup, Structure.ObjType.Estimate.ID, estimateDisplayIds));
//Add the relationship to the return doc that was uploaded
props.Add(-1, HelperMF.GetPropertyValue(Properties.Document.ID, movingDocId, env.Vault));
//create the new object in the vault
ObjectVersionAndProperties newDoc = HelperMF.CreateDocument(props, docDownloadPath, env.Vault);
I use a lot of help functions and classes but you should get the gist from my samples. Also, I would highly recommend you use the M-Files community website for research as they have a lot of code samples there geared specifically for M-Files.
https://community.m-files.com/
Also, if you don't already, use the API documentation as it also includes code samples.
http://www.m-files.com/api/documentation/2015.2/
Hopefully this helps,
Mike
Related
I'm currently looking for a way to dynamically create a FormDialog from values predefined in the database. In other words, my field types, prompts and settings are all stored in a database, and what I'm trying to achieve is reading those settings and building the appropriate form dynamically.
What I tried so far is something similar to the following. Suppose I have a form with a Name (string) and an Age (int) field (FieldDefinition is a class I created to store the parameters of a field, assuming they are fetched from the database) (The code is stripped just to illustrate the idea):
public static IForm<dynamic> BuildForm()
{
string FormMessage = "Welcome to demo contact form!";
string CompletionMessage = "Thank your for your info. Our team will contact you as soon as possible.";
var fields = new List<FieldDefinition>()
{
new FieldDefinition()
{
Name = "Name",
FieldType = typeof(string),
Prompts = new string[] { "What's your name?", "Please input your name" }
},
new FieldDefinition()
{
Name = "Age",
FieldType = typeof(int),
Prompts = new string[] { "What's your age?", "How old are you?" }
}
};
var builder = new FormBuilder<dynamic>();
builder.Message(FormMessage);
foreach (var f in fields)
{
builder.Field(
new FieldReflector<dynamic>(f.Name)
.SetType(f.FieldType)
);
}
builder.AddRemainingFields()
.OnCompletion(async (context, order) => {
var message = context.MakeMessage();
message.Text = CompletionMessage;
await context.PostAsync(message);
});
return builder.Build();
}
So here's the problems:
I thought I could use a dynamic type. But a method cannot return a dynamic object as it is determined at run-time. Therefore, I got an error when I tried building the form using the following:
dynamic values; var form = new FormDialog<dynamic>(values, ContactForm.BuildForm, FormOptions.PromptInStart, null);`
I need to create the properties of the object dynamically, therefore I looked for a way to create a Type on runtime. I ended up with something called TypeBuilder but I was a bit skeptical if it could solve my problem or not.
Therefore, I guess the ultimate start is by using the FieldReflector but I have no idea how to achieve this. I'm looking for something similar to the above but that does actually work.
Have you looked at FormBuilderJson? You could dynamically construct the .json string, and build the form at runtime:
public static IForm<JObject> BuildJsonForm()
{
string fromFlowJson = GetFormFlowJson();
return new FormBuilderJson(schema)
.AddRemainingFields()
.Build();
}
See here for more information: https://learn.microsoft.com/en-us/azure/bot-service/dotnet/bot-builder-dotnet-formflow-json-schema?view=azure-bot-service-3.0
I have a situation where a user can upload a word document which contains placeholders for certain properties (i.e. in MS Word the user has gone to Insert > Quick Parts > Document Properties and chosen a property). The specific properties I want to support are Title, Author, Company and Publish Date.
Title and Author are set as Package Properties, and Company is set as an Extended File Property. These are set with the below code, which works correctly:
private static void SetDocumentProperties(ReportTemplateModel model, WordprocessingDocument wordDocument)
{
//these properties always exist
wordDocument.PackageProperties.Title = model.Title;
wordDocument.PackageProperties.Creator = model.Author;
wordDocument.PackageProperties.Created = DateTime.Now;
wordDocument.PackageProperties.Modified = DateTime.Now;
//these properties can be null
if (wordDocument.ExtendedFilePropertiesPart == null)
{
wordDocument.AddNewPart<ExtendedFilePropertiesPart>();
}
if (wordDocument.ExtendedFilePropertiesPart.Properties == null)
{
wordDocument.ExtendedFilePropertiesPart.Properties = new Properties(new Company(model.SiteName));
}
else
{
wordDocument.ExtendedFilePropertiesPart.Properties.Company = new Company(model.SiteName);
}
}
My problem is that I can't work out how the set the Publish Date property. I have tried adding it as a Custom File Property using the below code (which is adapted from https://www.snip2code.com/Snippet/292005/WDSetCustomProperty), but this does not work. I've read a few things about setting custom properties, but I'm confused how they're meant to work. I'm also unsure if the Publish Date should actually be set as a custom property, or some other type of property.
var customProps = wordDocument.CustomFilePropertiesPart;
if (customProps == null)
{
customProps = wordDocument.AddCustomFilePropertiesPart();
customProps.Properties = new DocumentFormat.OpenXml.CustomProperties.Properties();
}
var properties1 = new DocumentFormat.OpenXml.CustomProperties.Properties();
//I have tried both of these lines, neither worked.
//properties1.AddNamespaceDeclaration("op", "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties");
properties1.AddNamespaceDeclaration("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
var customDocumentProperty1 = new DocumentFormat.OpenXml.CustomProperties.CustomDocumentProperty()
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = 2,
Name = "Publish Date"
};
customDocumentProperty1.Append(new DocumentFormat.OpenXml.VariantTypes.VTLPWSTR { Text = DateTime.Today.ToShortDateString() });
properties1.Append(customDocumentProperty1);
part.Properties = properties1;
What type of property should the Publish Date be set as, and what is the right syntax for setting this?
Update: I have found that Publish Date is a CoverPageProperty which can be created using the below snippet, but I still haven't found how to set it correctly within the document.
var coverPageProps = new DocumentFormat.OpenXml.Office.CoverPageProps.CoverPageProperties
{
PublishDate = new PublishDate(DateTime.Today.ToShortDateString())
};
Adding the below code to my SetDocumentProperties method seems to work. I must admit I don't fully understand the below code, so any explanation would still be welcome. Additionally, if anyone has a solution which doesn't include writing XML as a string inside C# I would much prefer to avoid that.
const string publishDatePartId = "publishDatePart";
var publishDateXmlPart = wordDocument.MainDocumentPart.AddNewPart<CustomXmlPart>("application/xml", publishDatePartId);
var writer = new XmlTextWriter(publishDateXmlPart.GetStream(FileMode.Create), System.Text.Encoding.UTF8);
writer.WriteRaw($"<CoverPageProperties xmlns=\"http://schemas.microsoft.com/office/2006/coverPageProps\">" +
$"<PublishDate>{DateTime.Today.ToShortDateString()}</PublishDate>" +
$"</CoverPageProperties>");
writer.Flush();
writer.Close();
var customXmlPropertiesPart = publishDateXmlPart.AddNewPart<CustomXmlPropertiesPart>(publishDatePartId);
customXmlPropertiesPart.DataStoreItem = new DocumentFormat.OpenXml.CustomXmlDataProperties.DataStoreItem()
{
//I don't know what this ID is, but it seems to somehow relate to the Publish Date
ItemId = "{55AF091B-3C7A-41E3-B477-F2FDAA23CFDA}"
};
Currently, I'm sending some data to Parse.com. All works well, however, I would like to add a row if it's a new user or update the current table if it's an old user.
So what I need to do is check if the current Facebook ID (the key I'm using) shows up anywhere in the fbid column, then update it if case may be.
How can I check if the key exists in the column?
Also, I'm using C#/Unity.
static void sendToParse()
{
ParseObject currentUser = new ParseObject("Game");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("Sent to Parse");
}
Okay, I figured it out.
First, I check which if there is any Facebook ID in the table that matches the current ID, then get the number of matches.
public static void getObjectID()
{
var query = ParseObject.GetQuery("IdealStunts")
.WhereEqualTo("fbid", FB.UserId);
query.FirstAsync().ContinueWith(t =>
{
ParseObject obj = t.Result;
objectID = obj.ObjectId;
Debug.LogError(objectID);
});
}
If there is any key matching the current Facebook ID, don't do anything. If there aren't, just add a new user.
public static void sendToParse()
{
if (count != 0)
{
Debug.LogError("Already exists");
}
else
{
ParseObject currentUser = new ParseObject("IdealStunts");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("New User");
}
}
You will have to do a StartCoroutine for sendToParse, so getObjectID has time to look through the table.
It may be a crappy implementation, but it works.
What you need to do is create a query for the fbid. If the query returns an object, you update it. If not, you create a new.
I'm not proficient with C#, but here is an example in Objective-C:
PFQuery *query = [PFQuery queryWithClassName:#"Yourclass]; // Name of your class in Parse
query.cachePolicy = kPFCachePolicyNetworkOnly;
[query whereKey:#"fbid" equalTo:theFBid]; // Variable containing the fb id
NSArray *users = [query findObjects];
self.currentFacebookUser = [users lastObject]; // Array should contain only 1 object
if (self.currentFacebookUser) { // Might have to test for NULL, but probably not
// Update the object and save it
} else {
// Create a new object
}
We are having an issue with searching a custom record through SuiteTalk. Below is a sample of what we are calling. The issue we are having is in trying to set up the search using the internalId of the record. The issue here lies in in our initial development account the internal id of this custom record is 482 but when we deployed it through the our bundle the record was assigned with the internal Id of 314. It would stand to reason that this internal id is not static in a site per site install so we wondered what property to set up to reference the custom record. When we made the record we assigned its “scriptId’ to be 'customrecord_myCustomRecord' but through suitetalk we do not have a “scriptId”. What is the best way for us to allow for this code to work in all environments and not a specific one? And if so, could you give an example of how it might be used.
Code (C#) that we are attempting to make the call from. We are using the 2013.2 endpoints at this time.
private SearchResult NetSuite_getPackageContentsCustomRecord(string sParentRef)
{
List<object> PackageSearchResults = new List<object>();
CustomRecord custRec = new CustomRecord();
CustomRecordSearch customRecordSearch = new CustomRecordSearch();
SearchMultiSelectCustomField searchFilter1 = new SearchMultiSelectCustomField();
searchFilter1.internalId = "customrecord_myCustomRecord_sublist";
searchFilter1.#operator = SearchMultiSelectFieldOperator.anyOf;
searchFilter1.operatorSpecified = true;
ListOrRecordRef lRecordRef = new ListOrRecordRef();
lRecordRef.internalId = sParentRef;
searchFilter1.searchValue = new ListOrRecordRef[] { lRecordRef };
CustomRecordSearchBasic customRecordBasic = new CustomRecordSearchBasic();
customRecordBasic.recType = new RecordRef();
customRecordBasic.recType.internalId = "314"; // "482"; //THIS LINE IS GIVING US THE TROUBLE
//customRecordBasic.recType.name = "customrecord_myCustomRecord";
customRecordBasic.customFieldList = new SearchCustomField[] { searchFilter1 };
customRecordSearch.basic = customRecordBasic;
// Search for the customer entity
SearchResult results = _service.search(customRecordSearch);
return results;
}
I searched all over for a solution to avoid hardcoding internalId's. Even NetSuite support failed to give me a solution. Finally I stumbled upon a solution in NetSuite's knowledgebase, getCustomizationId.
This returns the internalId, scriptId and name for all customRecord's (or customRecordType's in NetSuite terms! Which is what made it hard to find.)
public string GetCustomizationId(string scriptId)
{
// Perform getCustomizationId on custom record type
CustomizationType ct = new CustomizationType();
ct.getCustomizationTypeSpecified = true;
ct.getCustomizationType = GetCustomizationType.customRecordType;
// Retrieve active custom record type IDs. The includeInactives param is set to false.
GetCustomizationIdResult getCustIdResult = _service.getCustomizationId(ct, false);
foreach (var customizationRef in getCustIdResult.customizationRefList)
{
if (customizationRef.scriptId == scriptId) return customizationRef.internalId;
}
return null;
}
you can make the internalid as an external property so that you can change it according to environment.
The internalId will be changed only when you install first time into an environment. when you deploy it into that environment, the internalid will not change with the future deployments unless you choose Add/Rename option during deployment.
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.