Update Azure search document schema - c#

I'm encountering an error when trying to update an Azure search document schema through a code-first approach.
Our current entity has the schema:
public class SearchDocument
{
[DataAnnotations.Key]
public string ID;
[IsSearchable]
public string Title;
[IsSearchable]
public string Content;
}
but I want to add a field so it becomes this:
public class SearchDocument
{
[DataAnnotations.Key]
public string ID;
[IsSearchable]
public string Title;
[IsSearchable]
public string Content;
[IsSortable]
public bool Prioritize;
}
and when running a re-indexing query, I get the error:
"The request is invalid. Details: parameters : The property 'Prioritize' does not exist on type 'search.documentFields'. Make sure to only use property names that are defined by the type."
Which makes sense... but is there a way check beforehand if the schema matches, and update the Azure schema? I know other cloud databases, like Parse (dead) and Backendless (may have changed since I last used it), had automatic entity updating based on the entity schema POSTed, so is there any way to do this in Azure?
I've found a couple articles on MSDN about updating the indexers, but haven't been able to test (due to a closed dev environment... I know, I'm sorry).
One thing in particular that concerns me is the warning in the first link:
Important
Currently, there is limited support for index schema updates. Any schema
updates that would require re-indexing such as changing field types are not
currently supported.
So is this even possible? Or do I have to update the schema locally and then log into the Azure portal and update the indexer schema manually? Any help is very appreciated!

Azure Search does support incremental changes to an Azure Search index. What this means is that you are able to add new fields (as you are doing here with your Prioritize field). Based on what you have stated, it looks like you are using the .NET SDK. For that reason, I wonder if you have tried the CreateOrUpdate index operation? For example:
serviceClient.Indexes.CreateOrUpdate

Related

Dynamic LINQ with Skip and Take causes Exception: Unknown LINQ expression of type 'Dynamic'

I'm building a report builder/runner using System.Linq.Dynamic.Core (1.2.20) in an ASP.NET MVC (5.2.9) app and I mostly have it working, except for one annoying issue. I can't get Skip and Take to work. Basically my code is doing this:
_context.SetDynamic("ENTITY_NAME")
.Where(_parsingConfig, WHERE_EXPRESSION)
.OrderBy(_parsingConfig, ORDER_BY_EXPRESSION)
.Skip(???)// exception
.Take(???)// exception
.Select(_parsingConfig, SELECT_EXPRESSION)
.ToDynamicListAsync();
Running that causes this exception:
Unknown LINQ expression of type 'Dynamic'.
When I remove Skip and Take then it works correctly and I see the results, but I lose out on the paging capabilities.
From what I can tell, it has to do with me starting out with SetDynamic which returns an IQueryable<object>. Elsewhere in the app I do the same query, but start out from a Set<T> and there's no problems with it.
What should I do to get Skip and Take to work?
After some more trial and error I got it to work. I manually tested switching to Set(Type) to see if it fixed Skip and Take and it did.
From there I decided to change the model for Report to contain a string Object property. It already had an ObjectType property which was an enum, but I decided to replace it with a string, and I'll probably do that everywhere else I use it and just remove it.
After that, using reflection I got all objects in the DbContext assembly that implement a marker interface called IEntity and projected them into an IList<Entity>:
public sealed class Entity {
public string DisplayName { get; set; }
public string Name { get; set; }
public Type Type { get; set; }
}
... and then stored them as a singleton for dependency injection. In the report runner class since I already know the report Id I just pull it out of the database and then pull out the Entity where Report.Object == Entity.Name from the list and pass the type off to Set(Type).
Kind of long winded, but it works. I had been planning to have something like that list for a little while so I can present it as drop down list when a new report is being created so the report runner knows where to start from when building the query.

How to use same entity in POST and PATCH action with required property? [duplicate]

Say I have a controller CatController with actions for GET, POST and PUT. They all use the same Cat resource which could look like this:
public class CatDto {
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsFriendly {get; set; }
}
However, the Name and IsFriendly properties should only be required when creating a new cat (POST), but optional when updating it (PUT) to allow updating only a single property.
The way I've handled this until now is simply having two classes, a CreateCat and UpdateCat which have the same properties but different data annotations. However I don't want to have to maintain two almost identical classes.
I could of course validate the model manually in each action, but data annotations are very useful for things like global model validators and automatic generation of Swagger schemas.
I'm also using the Swagger schema to automatically generate SDK's (using ApiMatic), and that results in having two duplicate classes generated (CreateCat and UpdateCat) for what really should only be a single resource (Cat).
Is there an alternative way to achieve what I'm trying to do with only a single class?
I prefer to keep separate models to be honest.
You could have a base abstract ( or not ) model with all the common properties although this is not required and simply adds a third class. Is there a need for it? I'd say no.
There are slight differences between POST and PUT.Neither POST nor PUT require the Id property if you already have that in the PUT endpoint. This negates the need of checking if that Id in URL matches the Id in the model.
Your example does not make the difference visible, but in many cases there are fields you don't really want to update. For example let's say you have a Created and Updated date fields, you would not want to change your Created date via a PUT for example. The more data you have that you do not want to update via a PUT, the more obvious and worthwhile the differences between the models become.
In your case even with those 2 properties I would still create 2 different models, even if they are virtually the same, this sets the expectation on how the API works and creates a clear design in the mind of everyone else who is working on it.
I would recommend against the design you are asking for. According to RFC [RFC7231] you can find here it is advised not to have partial content update in PUT methods.
"An origin server that allows PUT on a given target resource MUST send
a 400 (Bad Request) response to a PUT request that contains a
Content-Range header field (Section 4.2 of [RFC7233]), since the
payload is likely to be partial content that has been mistakenly PUT
as a full representation. Partial content updates are possible by
targeting a separately identified resource with state that overlaps a
portion of the larger resource, or by using a different method that
has been specifically defined for partial updates (for example, the
PATCH method defined in [RFC5789])."
The preferred solution is to use PATCH method instead of PUT. Patch method is described in the RFC in the this link. PATCH method were introduced for partial resource modifications
So look up PATCH methods or if you want to use PUT maybe have a separate endpoint that only takes one of the two values.
More information about PATCH method can be found here
So either go for a PATCH method or create different models and end point to cater for partial update using PUT.

MongoDB collection error with type inheritance (FileFormatException)

This is my first few days with Mongo (and NoSQL in general), so I may be trying something that is only possible in my head.
Setup:
DB: MongoDB 2.2.1
Interface: latest 10gen c# driver from nuget
I've got a general class like this
public class User { public string userName {get;set;} }
and then an application specific implementation like this
public class App1User : User { public int appSpecificProperty { get;set; } }
Issue:
In my application I can load a record that was saved as type User as either User or App1User. Unfortunately once I save a record using the App1User type, I can no longer load it as a User (FileFormatException: Element 'appSpecificProperty' does not match any field or property of class User).
I load items like this:
var collection = mongDB.GetCollection<User>("users");
var query = new QueryDocument("username" : userName);
var user = collection.Find(query).SingleOrDefault(); // error here if record was type App1User
This is a problem as I want to use a user authentication library across multiple projects and then just extend the user object to add application specific settings.
I discovered that you need to mark your Parent class with this attribute
[BsonDiscriminator("baseuser", RootClass = true)]
Unfortunately this will only fix new records, I haven't figured out how to apply this fix to existing records that cannot load.

How do I get the string length for a field on the server side?

I have a edmx model created from a database and a metadata.cs for it.
In the client, the .g.cs includes [StringLength(X)] attributes alongside my attributes from my metadata.
I am doing some serverside validation for a flat file import that is seperate to the client side editors of these entities.
I am able to apply my range and regular expression validations but I am unable to find the StringLength attribute on the server. Does anyone know how to do this without duplicating the StringLength attributes manually on the metadata properties.
Edit:
Here is some code:
Server side file ProductService.metadata.cs:
internal sealed class PRODUCTMetadata
{
[Required]
[RegularExpression("[A-Z]+")]
[Display(Name = "Product Code", Order = 10)]
public string Product_code { get; set; }
}
Client side Generated_Code\NameSpace.Web.g.cs:
public sealed class PRODUCT
{
[DataMember()]
[Display(Name="Product Code", Order=10)]
[RegularExpression("[A-Z]+")]
[Required()]
[StringLength(8)] //This is what I want to know, but server side
public string Product_code
{...etc
}
}
I've investigated a bit around this problem, and couldn't find any good information about the topic on the Internet. So what' I'll say here is only assumption.
As you have seen, the auto-generated client proxy code is much more decorated with attributes than the server-side code. Your entities for instance have the nice [StringLength(8)] attribute that comes from the Entity Model. On the server side, the auto-generated .metadata.cs file doesn't have those attributes on entities. I think it's all about code-generation templates.
I suspect that the code generation template of RIA Services (that creates the .g.cs file) is much more complete than the template that creates the .metadata.cs file on server side.
The fact that the attribute that is missing in your case is 95% of time used for UI validation on client-side might explain why the template for the .metadata.cs file doesn't produce those validation attributes.
I see 2 work-arounds for your problem:
1. Write your own metadata class on server side
Some example:
[MetadataTypeAttribute(typeof(PRODUCT.PRODUCTMetadata))]
public partial class PRODUCT
{
internal sealed class PRODUCTMetadata
{
// Metadata classes are not meant to be instantiated.
private PRODUCTMetadata()
{
}
[StringLength(8)]
public string Product_code { get; set; }
}
}
You can manually add any attributes to the properties of your entities, as entities are partial classes.
Unfortunately, you'll have to maintain those metadatas each time you modify your model: if (for example), your DB table column changes from varchar(8) to varchar(10), you'll be able to automatically update your EDMX model from your database, but you'll have to manually check that your metadatas are still OK (in this example, you would have to manually replace [StringLength(8)] by [StringLength(9)]).
Here's a nice link about metadata.
2. Modify the T4 templates
Second option is probably the best one, but I didn't experienced myself the code generation template modification, so I don't known what can effectively be done or not.
Code generation templates are known as T4 templates (Text Template Transformation Toolkit). It is possible to modify those templates to include anything you want in the code generation process. You could modify the default EF template so it generates the missing attributes just as the RIA Services template does.
Here's some nice articles about T4 code generation:
http://www.scip.be/index.php?Page=ArticlesNET36#T4CodeGenerationClasses
http://msdn.microsoft.com/en-us/data/gg558520
http://msdn.microsoft.com/en-us/library/cc982041.aspx
I write this as an answer (it wouldn't fit as a comment), but remember it's all assumptions.

Retrieve class name hierarchy as string

Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH

Categories