When I run this code:
var result = _client.Index<EntityType>(item, i => i.Index(n));
I'm getting this error:
Exception has occurred: CLR/System.StackOverflowException An unhandled
exception of type 'System.StackOverflowException' occurred in
Elasticsearch.Net.dll
The full method:
public bool Index<EntityType>(EntityType item, int attempt = 0) where EntityType : class, IDomainEntity<int>
{
const int maxRetries = 5;
if (item == null)
{
return false;
}
var type = item.GetType();
var attributes = type.CustomAttributes;
string n = "";
foreach (var attribute in attributes)
{
foreach (var arg in attribute.NamedArguments)
{
if (arg.MemberName == "RelationName")
{
n = arg.TypedValue.Value.ToString().ToLower();
}
}
}
var result = _client.Index<EntityType>(item, i => i.Index(n));
if (!CheckResponse(result) && attempt < maxRetries)
{
RefreshClient<EntityType>();
attempt++;
return Index(item, attempt);
}
RefreshClient<EntityType>();
return result.IsValid;
}
I added [PropertyName("propertyToIgnoreInElasticsearch", Ignore = true)] from NEST to my POCO fields which were causing an infinite loop while Indexing. It ignores a field from the Elasticsearch Index so it is not indexed.
for example:
[Serializable]
public abstract class VeganItem<VeganItemEstablishmentType>
{
[Required]
public string Name { get; set; }
[PropertyName("veganItemEstablishments", Ignore = true)]
public virtual ICollection<VeganItemEstablishmentType> VeganItemEstablishments { get; set; }
}
Related
I'm working on a project in AutoCAD using c#, my application data is stored in complex objects
(String, double, objectId, arrays, list...) and I would like to save data for later using (serialize or saved in AutoCAD drawing) and if I re-open AutoCAD and reload my project, I can find all data in my object
Sorry for my English
So You need to use XData.
Details and sample You can find here:
https://www.keanw.com/2007/04/adding_xdata_to.html
You could serialize your class into a binary stream and then you can save it in the drawing as a bunch of binary chunks (see this topic)
But most of the time you should directly store data in Xrecords of a DBDictionary.
public abstract class RecordableObject
{
protected ObjectId dictionaryId;
protected Database database;
public string Key { get; }
protected RecordableObject(string key, Database db = null)
{
database = db ?? HostApplicationServices.WorkingDatabase;
Key = key;
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var NOD = (DBDictionary)tr.GetObject(database.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary dictionary;
if (NOD.Contains(Key))
{
dictionaryId = NOD.GetAt(Key);
}
else
{
NOD.UpgradeOpen();
dictionary = new DBDictionary();
dictionaryId = NOD.SetAt(Key, dictionary);
tr.AddNewlyCreatedDBObject(dictionary, true);
}
tr.Commit();
}
}
public abstract void SavePropertiesToDictionary();
public abstract void SetPropertiesFromDictionary();
protected void SaveData(string key, params TypedValue[] values)
{
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
Xrecord xrecord;
if (dictionary.Contains(key))
{
xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForWrite);
}
else
{
xrecord = new Xrecord();
dictionary.UpgradeOpen();
dictionary.SetAt(key, xrecord);
tr.AddNewlyCreatedDBObject(xrecord, true);
}
xrecord.Data = new ResultBuffer(values);
tr.Commit();
}
}
protected T GetData<T>(string key)
{
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
if (dictionary.Contains(key))
{
var xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForRead);
if (xrecord.Data != null)
return (T)xrecord.Data.AsArray()[0].Value;
}
return default;
}
}
protected T[] GetDataArray<T>(string key)
{
using(var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
if (dictionary.Contains(key))
{
var xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForRead);
if (xrecord.Data != null)
return xrecord.Data.AsArray().Select(tv => (T)tv.Value).ToArray();
}
return default;
}
}
}
Derived class example:
public class RecordableExample : RecordableObject
{
public double Size { get; set; }
public ObjectId ObjectId { get; set; }
public int[] Ints { get; set; }
public RecordableExample(string key, Database db = null) : base(key, db) { }
public override void SavePropertiesToDictionary()
{
SaveData(nameof(Size), new TypedValue((int)DxfCode.Real, Size));
SaveData(nameof(ObjectId), new TypedValue((int)DxfCode.Handle, ObjectId.Handle));
if (Ints != null)
SaveData(nameof(Ints), Ints.Select(i => new TypedValue((int)DxfCode.Int32, i)).ToArray());
}
public override void SetPropertiesFromDictionary()
{
Size = GetData<double>(nameof(Size));
Ints = GetDataArray<int>(nameof(Ints));
var handle = new Handle(Convert.ToInt64(GetData<string>(nameof(ObjectId))));
if (database.TryGetObjectId(handle, out var id))
ObjectId = id;
}
}
I'm trying to implement a contains specification for nested properties. I am currently working off a contains specification below that only supports a simple property.
For an object structure like below and applying the specification to the Manager Entity.
public class Manager
{
public string Name { get; set; }
public Department Department { get; set; }
public IList<Employee> Employees { get; set; }
}
public class Department
{
public string Name { get; set; }
}
public class Employee
{
public string Name { get; set; }
}
It is able to support a filter like below.
[
{
"Name",
"John"
}
]
The problem comes when I need to support a more complicated query like below.
[
{
"Employees.Name",
"Jake"
}
]
I think the specification will also have to behave different for a filter like this because it is not looking into a collection and just a single object.
[
{
"Department.Name",
"Accounting",
}
]
The contains specification that I currently have now
private static Expression<Func<T, bool>> CreateExpression(IDictionary<string, string> filter)
{
var type = typeof(T);
var parameterExpression = Expression.Parameter(typeof(T));
var aggregatedExpression = filter.Aggregate((Expression)null, (agg, pair) =>
{
var propertyInfo = type
.GetProperties()
.FirstOrDefault(t => t.Name.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase));
if (propertyInfo == null)
{
throw new Exception("PropertyInfo was not found.");
}
// Call to string on value if property is of the listed property types
var propertyExpression = (Expression)Expression.Property(parameterExpression, propertyInfo);
if (propertyInfo.PropertyType == typeof(long) ||
propertyInfo.PropertyType == typeof(long?) ||
propertyInfo.PropertyType == typeof(int) ||
propertyInfo.PropertyType == typeof(int?) ||
propertyInfo.PropertyType == typeof(DateTime) ||
propertyInfo.PropertyType == typeof(DateTime?) ||
propertyInfo.PropertyType == typeof(Guid) ||
propertyInfo.PropertyType == typeof(Guid?))
{
var toStringMethod = propertyExpression.Type.GetMethod(nameof(ToString), new Type[] { });
if (toStringMethod != null)
{
propertyExpression = Expression.Call(propertyExpression, toStringMethod);
}
}
var containsMethod = propertyExpression.Type.GetMethod(nameof(Queryable.Contains), new[] { propertyExpression.Type });
if (containsMethod == null)
{
throw new Exception("Contains method is not found.");
}
// Call contains on the property (i.e. Name.Contains("John")
var expression = Expression.Call(propertyExpression, containsMethod, Expression.Constant(pair.Value));
// Multiple Contains to be aggregated into OrElse
agg = agg == null ? expression : (Expression)Expression.OrElse(agg, expression);
return agg;
});
return Expression.Lambda<Func<T, bool>>(aggregatedExpression, parameterExpression);
}
I have a class that should represent a controller's action parameter and I'd like its properties to be "required" (meaning, you get a status code 400 or something in case it's passed as null). I managed to get it done using System.ComponentModel.DataAnnotations, but the ErrorMessage that I pass to the constructor of the Required attribute is never shown.
[XmlRoot(ElementName = "root")]
public class Request
{
[XmlElement(ElementName = "prop")]
[Required(ErrorMessage = "The property is required.")]
public string Property { get; set; }
[XmlElement(ElementName = "another")]
[Required(ErrorMessage = "The property is required.")]
public string Another { get; set; }
}
Action:
[HttpPost]
public IActionResult Post([FromBody] Request value)
{
return Ok(value); //ignore this, it's just for testing purposes...
}
However, if I don't pass the Property value, I get a 400 that doesn't contain the ErrorMessage I passed earlier. Am I missing something here?
<ValidationProblemDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>One or more validation errors occurred.</Title>
<Status>400</Status>
</ValidationProblemDetails>
My Startup has Xml formatters added to it:
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true;
options.InputFormatters.Insert(0, new XmlSerializerInputFormatter(options));
options.OutputFormatters.Insert(0, new XmlSerializerOutputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
The body of the request looks like this, and it doesn't have "Property":
<root>
<another>Test</another>
<!-- Property "Property" is missing here -->
</root>
Kudos to Code Rethinked for the huge help - Customizing automatic HTTP 400 error response in ASP.NET Core Web APIs.
An approach that I managed to figure out eventually includes the use of services.Configure in my Startup.ConfigureServices method.
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
return new OkObjectResult(new CustomResponse(someStatusCode, context))
{
ContentTypes = { "application/xml" }
};
};
});
So, I made a class named CustomResponse that holds the status code I want to retrieve and all the validation errors (including the ones where my Required property was not passed to the API).
[XmlRoot(ElementName = "rcemsTrxSubReqAck")]
public class CustomResponse
{
[XmlElement(ElementName = "Status")]
public string Status { get; set; }
[XmlArray(ElementName = "Errors"), XmlArrayItem(ElementName = "Error")]
public string[] Errors { get; set; }
public CustomResponse(int status, ActionContext context)
{
Status = status;
Errors = ConstructErrorMessages(context);
}
private string[] ConstructErrorMessages(ActionContext context)
{
if (context == null)
{
return null;
}
string[] arr = new string[context.ModelState.ErrorCount];
int i = 0;
foreach (var keyModelStatePair in context.ModelState)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
if (errors.Count == 1)
{
var errorMessage = GetErrorMessage(errors[0]);
arr[i] = $"{key}: {errorMessage}";
}
else
{
var errorMessages = new string[errors.Count];
for (var j = 0; j < errors.Count; j++)
{
errorMessages[j] = GetErrorMessage(errors[j]);
}
arr[i] = $"{key}: {errorMessages.ToString()}";
}
i++;
}
}
return arr;
}
private string GetErrorMessage(ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ? "The input was not valid." : error.ErrorMessage;
}
}
I'm developing a simple web app where I need to bind all types implementing and interface of a specific type. My interface has one single property like this
public interface IContent {
string Id { get;set; }
}
a common class using this interface would look like this
public class Article : IContent {
public string Id { get;set; }
public string Heading { get;set; }
}
to be clean here the article class is just one of many different classes implementing IContent so therefor I need a generic way of storing and updating these types.
So in my controller I have the put method like this
public void Put(string id, [System.Web.Http.ModelBinding.ModelBinder(typeof(ContentModelBinder))] IContent value)
{
// Store the updated object in ravendb
}
and the ContentBinder
public class ContentModelBinder : System.Web.Http.ModelBinding.IModelBinder {
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) {
actionContext.ControllerContext.Request.Content.ReadAsAsync<Article>().ContinueWith(task =>
{
Article model = task.Result;
bindingContext.Model = model;
});
return true;
}
}
The code above does not work because it does not seem to get hold of the Heading property even though if I use the default model binder it binds the Heading correctly.
So, in the BindModel method I guess I need to load the correct object from ravendb based on the Id and then update the complex object using some kind of default model binder or so? This is where I need some help.
Marcus, following is an example which would work fine for both Json and Xml formatter.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace Service
{
class Service
{
private static HttpSelfHostServer server = null;
private static string baseAddress = string.Format("http://{0}:9095/", Environment.MachineName);
static void Main(string[] args)
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
config.Routes.MapHttpRoute("Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
try
{
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Service listenting at: {0} ...", baseAddress);
TestWithHttpClient("application/xml");
TestWithHttpClient("application/json");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception Details:\n{0}", ex.ToString());
}
finally
{
if (server != null)
{
server.CloseAsync().Wait();
}
}
}
private static void TestWithHttpClient(string mediaType)
{
HttpClient client = new HttpClient();
MediaTypeFormatter formatter = null;
// NOTE: following any settings on the following formatters should match
// to the settings that the service's formatters have.
if (mediaType == "application/xml")
{
formatter = new XmlMediaTypeFormatter();
}
else if (mediaType == "application/json")
{
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
formatter = jsonFormatter;
}
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Get;
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
HttpResponseMessage response = client.SendAsync(request).Result;
Student std = response.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("GET data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std))
{
Console.WriteLine("both are equal");
}
client = new HttpClient();
request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Post;
request.Content = new ObjectContent<Person>(StudentsController.CONSTANT_STUDENT, formatter);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
Student std1 = client.SendAsync(request).Result.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("POST and receive data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std1))
{
Console.WriteLine("both are equal");
}
}
}
public class StudentsController : ApiController
{
public static readonly Student CONSTANT_STUDENT = new Student() { Id = 1, Name = "John", EnrolledCourses = new List<string>() { "maths", "physics" } };
public Person Get()
{
return CONSTANT_STUDENT;
}
// NOTE: specifying FromBody here is not required. By default complextypes are bound
// by formatters which read the body
public Person Post([FromBody] Person person)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
}
return person;
}
}
[DataContract]
[KnownType(typeof(Student))]
public abstract class Person : IEquatable<Person>
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
// this is ignored
public DateTime DateOfBirth { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (this.Id != other.Id)
return false;
if (this.Name != other.Name)
return false;
return true;
}
}
[DataContract]
public class Student : Person, IEquatable<Student>
{
[DataMember]
public List<string> EnrolledCourses { get; set; }
public bool Equals(Student other)
{
if (!base.Equals(other))
{
return false;
}
if (this.EnrolledCourses == null && other.EnrolledCourses == null)
{
return true;
}
if ((this.EnrolledCourses == null && other.EnrolledCourses != null) ||
(this.EnrolledCourses != null && other.EnrolledCourses == null))
return false;
if (this.EnrolledCourses.Count != other.EnrolledCourses.Count)
return false;
for (int i = 0; i < this.EnrolledCourses.Count; i++)
{
if (this.EnrolledCourses[i] != other.EnrolledCourses[i])
return false;
}
return true;
}
}
}
I used #kiran-challa solution and added TypeNameHandling on Json media type formatter's SerializerSettings.
I am trying to run lazy queries against raven db and get the counts on total matching results. I am finding when I query against a static index, a lazy search does not initialize the statistics when the query is materialized, but otherwise it comes back all right.
Below is the test to prove this behaviour.
[TestFixture]
public class CanSearchLazily
{
private const int ServerPort = 8085;
private readonly string _serverAddress = #"http://localhost:{0}".For(ServerPort);
[Test]
public void CanGetTotalResultsFromStatisticsOnLazySearchAgainstDynamicIndex()
{
CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex();
}
[Test]
public void CanGetTotalResultsFromStatisticsOnLazySearchAgainstStaticIndex()
{
CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex("UserByFirstName");
}
private void CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex(string indexName = "")
{
BuilderSetup.DisablePropertyNamingFor<User, string>(x => x.Id);
var users = Builder<User>.CreateListOfSize(2000).All()
.With(x => x.FirstName = GetRandom.FirstName())
.With(x => x.LastName = GetRandom.LastName())
.Build();
using (GetNewServer())
using (var store = new DocumentStore { Url = _serverAddress }.Initialize())
{
using (var session = store.OpenSession())
{
users.ForEach(session.Store);
session.SaveChanges();
IndexCreation.CreateIndexes(typeof(UserByFirstName).Assembly, store);
session.Query<User, UserByFirstName>().Customize(x => x.WaitForNonStaleResults()).ToList();
}
using (var session = store.OpenSession())
{
var names = session.Query<User>().Select(u => u.FirstName).Distinct().Take(15).ToList();
RavenQueryStatistics stats;
var query = string.IsNullOrEmpty(indexName)
? session.Query<User>().Statistics(out stats).Where(x => x.FirstName.In(names))
: session.Query<User>(indexName).Statistics(out stats).Where(x => x.FirstName.In(names));
var results = query.Take(8).Lazily();
Assert.AreEqual(8, results.Value.ToList().Count);
Assert.AreEqual(DateTime.Now.Year, stats.IndexTimestamp.Year, "the index should have the current year on its timestamp");
Assert.IsTrue(stats.TotalResults > 0, "The stats should return total results");
}
}
}
protected RavenDbServer GetNewServer(bool initializeDocumentsByEntitiyName = true)
{
var ravenConfiguration = new RavenConfiguration
{
Port = ServerPort,
RunInMemory = true,
DataDirectory = "Data",
AnonymousUserAccessMode = AnonymousUserAccessMode.All
};
if (ravenConfiguration.RunInMemory == false)
IOExtensions.DeleteDirectory(ravenConfiguration.DataDirectory);
var ravenDbServer = new RavenDbServer(ravenConfiguration);
if (initializeDocumentsByEntitiyName)
{
using (var documentStore = new DocumentStore
{
Url = _serverAddress
}.Initialize())
{
new RavenDocumentsByEntityName().Execute(documentStore);
}
}
return ravenDbServer;
}
}
[Serializable]
public class User
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserByFirstName : AbstractIndexCreationTask<User>
{
public UserByFirstName()
{
Map = users => from user in users
select new {user.FirstName};
}
}