I'm trying to initialize an AttributeCollection but I seem to get no good suggestions from Google with this search.
I'm also open to create the object using the specialized constructor that takes an array of type Attribute as described here on MSDN but I can't find any examples for that either.
I can resolve the issue by adding each attribute on its own as shown below but that's not as nice as initializing it directly.
bzzz["prop1"] = "val_1";
bzzz["prop2"] = "val_2";
...
bzzz["prop8"] = "val_8";
bzzz["prop9"] = "val_9";
It's the class in System.ComponentModel.
This is perfectly legal:
var attributes = new AttributeCollection();
attributes.Add("key1", "value1");
attributes.Add("key2", 2);
attributes.Add("key3", new EntityReference("contact", Guid.NewGuid()));
var entity = new Entity(){ Attributes = attributes };
Edit
You can do it all in one C# statement, At one time I saw it as being less readable, but now I see it being more readable so it really comes down to either picking an existing standard, or personal choice:
var entity = new Entity()
{
Attributes = new AttributeCollection() {
{ "key1", "value1" },
{ "key2", 2 },
{ "key3", new EntityReference("contact", Guid.NewGuid())}
}
};
Let's say I have a simple attribute
public class SimpleAttribute : Attribute
{
public SimpleAttribute(String val)
{
value = val;
}
protected String value;
}
I would add Attributes to the AttributeCollection like this.
AttributeCollection attrColl = new AttributeCollection(
new SimpleAttribute("value1"), new SimpleAttribute("value2"));
The only constructor parameter is declared as params, so you can pass multiple attributes (separated by commas), while you are constructing the collection.
Related
Due to the filter I'm applying to my changestream (discussed at SO: How do you filter updates to specific fields from ChangeStream in MongoDB), I am getting a BsonDocument back instead of a ChangeStreamDocument object. The only thing different about this BsonDocument from a ChangeStreamDocument is that it contains an extra element called "tmpfields".
In my scenario, I still need the ResumeToken and other elements in the document, so I'd like to convert this BsonDocument to a ChangeStreamDocument object. My first attempt was to use BsonSerializer.Deserialize<ChangeStreamDocument<BsonDocument>>( doc) where doc was the BsonDocument I got back. However, since it had the extra tmpfields element, this isn't allowed.
I attempted to register a BsonClassMap since the ChangeStreamDocument class is part of the C# driver and I couldn't add the [BsonIgnoreExtraElements] attribute to the class, but I wasn't successful:
BsonClassMap.RegisterClassMap<ChangeStreamDocument<BsonDocument>>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
The AutoMap() didn't work though and I got an exception about "no matching creator found". I tried to cm.MapCreator(...), but wasn't succesffuly there either. I took the AutoMap() call out (only leaving the SetIgnoreExtraElements line) and got errors about it not being able to match the properties (_id, etc). So I tried lines like cm.MapProperty(c => c.DocumentKey).SetElementName("documentKey") for each of the properties, but they were never set when I used the Deserialize() method - they were left as null.
For now, I've reverted to using doc["field"].AsXYZ method to get the values that I need from the BsonDocument, but I'd like to learn a better way to do this.
Is using the RegisterClassMap the correct approach? If so, what did I miss?
I couldn't add the [BsonIgnoreExtraElements] attribute to the class
If you just want to ignore the extra field. You could just add an extra aggregation pipeline $project to remove the field.
For example
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var addFields = new BsonDocument { { "$addFields", new BsonDocument { { "tmpfields", new BsonDocument { { "$objectToArray", "$updateDescription.updatedFields" } } } } } };
var match = new BsonDocument { { "$match", new BsonDocument { { "tmpfields.k", new BsonDocument { { "$nin", new BsonArray{"a", "b"} } } } } } };
// Remove the unwanted field.
var project = new BsonDocument { {"$project", new BsonDocument { {"tmpfields", 0 } } } };
var pipeline = new[] { addFields, match, project };
var cursor = collection.Watch<ChangeStreamDocument<BsonDocument>>(pipeline, options);
var enumerator = cursor.ToEnumerable().GetEnumerator();
while(enumerator.MoveNext())
{
ChangeStreamDocument<BsonDocument> doc = enumerator.Current;
Console.WriteLine(doc.DocumentKey);
}
I want to store all my enums in form of strings in my Elasticsearch index. Since the Model I am using needs to be independent from any specific technology, I can't use attribute mappings.
I know that you can pass a SerializerFactory when creating the ConnectionSettings, but I do not know what option will change the serialization of enums.
Here is the code I use to connect with NEST:
var serializers = new SerializerFactory(<what to put here?>);
var settings = new ConnectionSettings(myUri, serializers)
.DefaultIndex(myIndex)
.InferMappingFor<MyModel>(m => m
.IdProperty(s => s.MyId)
);
var client = new ElasticClient(settings);
Can I use the SerializerFactory to save all enums as strings? Is there any other option without using the attribute mappings?
Elasticsearch version:
"version" : {
"number" : "5.6.0",
"build_hash" : "781a835",
"build_date" : "2017-09-07T03:09:58.087Z",
"build_snapshot" : false,
"lucene_version" : "6.6.0"
}
NEST Version: 5.5.0 (most recent nuget)
Thanks to #RussCam I could find the solution. Here is the code I am using now:
var connectionPool = new SingleNodeConnectionPool(new Uri(myUri));
var connection = new HttpConnection();
var serializers = new SerializerFactory((s, v) => s.Converters.Add(new StringEnumConverter()) );
var settings = new ConnectionSettings(connectionPool, connection, serializers)
.DefaultIndex(StatusIndex)
.InferMappingFor<MyModel>(m => m
.IdProperty(s => s.MyId)
);
var client = new ElasticClient(settings);
Line 3 is the important one. The StringEnumConverter that is added to the Converters of the SerializerFactory will make every enum be serialized to a string and deserialized from a string (using this ElasticClient).
I used a similar approach to #Leifb is suggesting (using StringEnumConverter on the property):
[JsonConverter(typeof(StringEnumConverter))]
public MyEnum Status {get;set;}
However, this gives an issue if you use automapping for the types, so I created a generic Enum to String property visitor for that:
var result = await _client.PutIndexTemplateAsync(
p.TemplateName, s=>s
.Template(p.Template)
.Mappings(m=>m
.Map(p.TemplateName, mm=>mm
.AutoMap<MyType>(new EnumAsStringPropertyVisitor())
)
));
public class EnumAsStringPropertyVisitor : NoopPropertyVisitor
{
public override void Visit(
INumberProperty type,
PropertyInfo propertyInfo,
ElasticsearchPropertyAttributeBase attribute)
{
if(propertyInfo.PropertyType.IsEnum)
{
type.Type = "keyword";
}
}
}
I know that in C# you can nowadays do:
var a = new MyObject
{
Property1 = 1,
Property2 = 2
};
Is there something like that in PHP too? Or should I just do it through a constructor or through multiple statements;
$a = new MyObject(1, 2);
$a = new MyObject();
$a->property1 = 1;
$a->property2 = 2;
If it is possible but everyone thinks it's a terrible idea, I would also like to know.
PS: the object is nothing more than a bunch of properties.
As of PHP7, we have Anonymous Classes which would allow you to extend a class at runtime, including setting of additional properties:
$a = new class() extends MyObject {
public $property1 = 1;
public $property2 = 2;
};
echo $a->property1; // prints 1
Before PHP7, there is no such thing. If the idea is to instantiate the object with arbitrary properties, you can do
public function __construct(array $properties)
{
foreach ($properties as $property => $value)
{
$this->$property = $value
}
}
$foo = new Foo(array('prop1' => 1, 'prop2' => 2));
Add variations as you see fit. For instance, add checks to property_exists to only allow setting of defined members. I find throwing random properties at objects a design flaw.
If you do not need a specific class instance, but you just want a random object bag, you can also do
$a = (object) [
'property1' => 1,
'property2' => 2
];
which would then give you an instance of StdClass and which you could access as
echo $a->property1; // prints 1
I suggest you use a constructor and set the variables you wish when initialising the object.
I went from c# to PHP too, so I got this working in PHP:
$this->candycane = new CandyCane(['Flavor' => 'Peppermint', 'Size' => 'Large']);
My objects have a base class that checks to see if there's one argument and if it's an array. If so it calls this:
public function LoadFromRow($row){
foreach ($row as $columnname=>$columnvalue)
$this->__set($columnname, $columnvalue);
}
It also works for loading an object from a database row. Hence the name.
Another way, which is not the proper way but for some cases okay:
class Dog
{
private $name;
private $age;
public function setAge($age) {
$this->age = $age;
return $this;
}
public function getAge() {
return $this->age;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
}
$dogs = [
1 => (new Dog())->setAge(2)->setName('Max'),
2 => (new Dog())->setAge(7)->setName('Woofer')
];
For my work I need to create a page that gets all the data for that page from a database using the kellerman framework.
The problem is that I can't seem to create a certain part.
List<TextBlockDataObject> t = new List<TextBlockDataObject>();
t.Add(new TextBlockDataObject() { Translations = (List<content new List<ContentTranslationDataObject>().Add(new ContentTranslationDataObject() { LangueageCode = "Eng", TextValue = "TestingText" } ) });
Error: Cannot implicitly convert type 'void' to
When I try to build the object trough a other way I get other errors related to bad object creation.
Other Code I used for testing
var tmpTextDataObjs = new List<TextBlockDataObject>();
tmpTextDataObjs.Add(new TextBlockDataObject());
List<ContentTranslationDataObject> ContentTransDataObject = new List<ContentTranslationDataObject>();
ContentTransDataObject.Add(new ContentTranslationDataObject() { LangueageCode = "Eng", TextValue = "this is a test.." });
tmpTextDataObjs[0].Translations = ContentTransDataObject.ToArray();
tmpPage.TextBlockDataObjects = tmpTextDataObjs.ToArray();
var titleObj = new List<ContentTranslationDataObject>();
titleObj.Add(new ContentTranslationDataObject(){LangueageCode = "Eng", TextValue = "444"});
tmpPage.TitleTranslations = titleObj.ToArray();
the code above works for a few elements but in the end the page doesn't get created.
Hope anyone can help me out!
The problem is that you are calling Add() on a newly created List object that you are trying to add to another List property. Add() returns void, not the new list.
Split up your declarations and assignments and you should be fine. Also, I recommend using the var keyword to lessen code duplication/boilerplate as well as using IList instead of List (always use a more generic type/interface when possible).
If you want to initialize the objects inline you can use the anonymous array initializer syntax.
IList<myType> myList = new[] { new myType(), new myType() };
Info myPath = new Info()
{
path = oFile.FileName
};
...
class Info
{
public string path;
public string Path
{
get { return path; }
set { path = value; }
}
}
Above is the C# code from some program and it can work normally. But I don't understand it well. The first question is that why path = oFile.FileName is not written as path = oFile.FileName; ? Why the semicolon can be removed?
The second question is that why I cannot write it like this: myPath.path = oFile.FileName ? There will give error message by Visual Studio 2012.
That construct is an object initializer. It's not a list of arbitrary statements - it's only initialization of fields and properties, and they're comma-separated:
Foo x = new Foo // Implicitly calls the parameterless constructor
{
Property1 = value1,
Property2 = value2
};
That's shorthand for:
Foo tmp = new Foo();
tmp.Property1 = value1;
tmp.Property2 = value2;
Foo x = tmp;
Object initializers were introduced in C# 3, along with collection initializers which are effectively syntactic sugar for repeated calls to Add. So:
List<string> names = new List<string>
{
"Foo", "Bar"
};
is equivalent to:
List<string> tmp = new List<string>();
tmp.Add("Foo");
tmp.Add("Bar");
List<string> names = tmp;
You have many ways of initializing an object in C#.
Here you can do what you have written, which will be an equivalent to this:
Info myPath = new Info();
myPath.Path = oFile.FileName;
this syntax
Info myPath = new Info()
{
path = oFile.FileName
};
is just a shortcut, and can be more readable, but will do the same thing. Actually it seems that it was kind of taken from VisualBasic (the With statement).
To explain the above syntax:
YourClassName <your_variable> = new YourClassName()
{
<property_name> = value,
<anotherProperty> = value,
...
<last_property> = value
};
the last way is to have a constructor that takes the path as an argument and initializes it. This is actually the way where there is the less operations done by the cpu (but it's not significant).
In C# 3.0, they added a new & helpful feature for initializing objects as a single statement/expression.
Whereas before, you'd have to issue separate statements:
Info myPath = new Info();
myPath.Filename = ...
myPath.AnotherProperty = ...
myPath.AnotherAnotherProperty = ...
You can now perform the same assignments in one step:
Info myPath = new Info
{
Filename = ...
AnotherProperty = ...
AnotherAnotherProperty = ...
};
This is especially useful, for constructing objects in Linq queries (without having to custom-code object constructors).
For example:
someList.Select(x => new SomethingElse{ SomeProperty = x.Something });
Info myPath = new Info()
{
path = oFile.FileName
};
means:
Initialize a new Info Class and add to property path the value oFile.FileName
it is the short version of:
Info myPath = new Info();
myPath.path = oFile.FileName;
you do not need ';' because you can stack more properties in the brackets like this:
Person p = new Person()
{
Name = "John",
Age = 25
};
C# allows you to initialize properties of an object at the same time that you're constructing it.
These are all equivalent:
var foo = new Foo();
foo.Property = "Bar";
foo.AnotherProperty = 12;
var foo = new Foo()
{
Property = "Bar",
AnotherProperty = 12
};
// The parentheses are not really necessary for parameterless constructors
var foo = new Foo
{
Property = "Bar",
AnotherProperty = 12
};
It is the new way of initializing variables in C#. You can also skip '()' signs.
You can initialize like that also lists, arrays, etc. in brackets you must insert elements which you want to initialize in your object.
Info myPath = new Info()
{
path = oFile.FileName
};
is equivalent to
Info myPath = new Info();
myPath.path = oFile.FileName;
When initializing with the object initializing structure, you create a list of property assignments as you noted. This way you can create objects and assign variables in one call without having an explicit constructor. The above could easily have been written in one line.
More info can be found on the MSDN website.