A reasonably common pattern in REST APIs is to support conditional inclusion (or sometimes exclusions) of attributes in a response using a query parameter. For example:
GET /customers?include=notes
In this case, the Notes attribute is optional and won't be included in the response unless specifically requested.
The naive solution leaves a lot to be desired:
var query = dbContext.Query<Customer>();
if (include.Contains('notes'))
{
return query.Select(x => new Customer
{
Id = x.Id,
Name = x.Name,
Notes = x.Notes,
});
}
else
{
return query.Select(x => new Customer
{
Id = x.Id,
Name = x.Name
});
}
Is there a clean, elegant, DRY way to implement this feature with a combination of Entity Framework and AutoMapper in ASP.NET?
Related
I've a JSON model that contains strings instead of dates (the model is generated via T4TS, so I cannot change that).
The code is currently using an expanded model extending the original json, where the dates are recalculated on new fields.
I was wondering if it would be possible to apply the filters on the fields being string without adding that additional step of extending the model.
private makeNumeric(label: string, property: string) {
return {
label: label,
key: property,
prepareDimension: (crossfilter) => (CrossfilterUtils.makeNumeric(crossfilter, property)),
prepareGroup: (dimension) => {
if (!this.values[property]) {
var group = CrossfilterUtils.makeNumericGroup(dimension);
this.values[property] = group;
}
return this.values[property];
},
valuesAreOrdinal: false
};
}
I haven't used the crossfilter library much before and by looking at the documentation I can't seem to reconcile it with the code (heritage code, to put it that way).
The incoming date format looks like this: "2020-10-22T07:26:00Z"
The typescript model I'm working with is like this:
interface MyModel {
...
CreatedDate?: string;
}
Any idea?
The usual pattern in JavaScript is to loop through the data and do any conversions you need:
data.forEach(function(d) {
d.date = new Date(d.date);
d.number = +d.number;
});
const cf = crossfilter(data);
However, if this is not allowed due to TS, you can also make the conversions when creating your dimensions and groups:
const cf = crossfilter(data);
const dateDim = cf.dimension(d => new Date(d.date));
const monthGroup = dateDim.group(date => d3.timeMonth(date))
.reduceSum(d => +d.number);
I find this a little less robust because you have to remember to do this everywhere. It's a little harder to reason about the efficiency since you have to trust that crossfilter uses the accessors sparingly, but I don't recall seeing this be a problem in practice.
I try to get some basic data from Facebook API, by using the .NET Middleware (I'm using ASP.NET 5, but I think it's similar in older versions) for authentication by facebook.
I have no problems with first name, last name and so on, but I'm stuck to get the middle name, here is my code snippet:
app.UseFacebookAuthentication(options =>
{
options.AuthenticationType = "Facebook";
options.SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType;
options.AppId = facebookAppId;
options.AppSecret = facebookAppSecret;
options.Scope.Add("user_birthday");
options.Scope.Add("email");
options.Notifications = new Microsoft.AspNet.Security.Facebook.FacebookAuthenticationNotifications()
{
OnAuthenticated = async (ctx) =>
{
var token = ctx.AccessToken;
var firstName = ctx.User["first_name"];
var middleName = ctx.User["middle_name"];
var lastName = ctx.User["last_name"];
}
}
}
I tried to add "middle_name" to the scope, but it throw a error from FB:
Invalid Scope: middle_name
You should add middle name in scope. Like this.
options.Scope.Add("middle_name");
First name and last name might be the implicit values you are getting, for middle name you have to add it to scope to get the value.
And you should add this to scope to corresponding facebook App, that you have created ( I mean you are using corresponding app key/secret, that facebook App).
I have a basic POCO (No database) structure implementing an OData Service with the latest WebAPI update. Unfortunately, the latest update broke the HasNavigationPropertiesLink code that I had to generate links which can be used for $expand operations. Here is my old code:
var jobs = modelBuilder.EntitySet<Job>("Jobs");
jobs.EntityType.NavigationProperties,
(entityContext, navigationProperty) => new
Uri(entityContext.UrlHelper.Link(ODataRouteNames.PropertyNavigation,
new
{
Controller = "Jobs",
parentId = entityContext.EntityInstance.ID,
NavigationProperty = navigationProperty.Name
})));
And here is my new code (that doesn't work):
var jobs = modelBuilder.EntitySet<Job>("Jobs");
jobs.EntityType.NavigationProperties,
(entityContext, navigationProperty) => new
Uri(entityContext.Url.Link(<??WHAT GOES HERE??>,
new
{
Controller = "Jobs",
parentId = entityContext.EdmObject,
NavigationProperty = navigationProperty.Name
})),
true);
Any help is much appreciated - this doesn't seem to have been documented in the updates.
looks like the version of the OData bits you are using is very old. In our current version, you can use the ODataConventionsModelBuilder to create a model that defines navigation properties and links following conventions, so unless you need to generate custom links, it's a better way to go. However, if you want to generate a custom navigation link, the link generation code looks something similar to this:
var jobs = builder.EntitySet<Job>("Jobs");
jobs.HasNavigationPropertiesLink(customers.EntityType.NavigationProperties,
(context, navigationProperty) =>
{
var result = "http://mydomain.com/prefix/odataPath";
//In order to generate this link you can use context.Url.ODataLink(new EntityPathSegment("Jobs"), ...);
return new Uri(result);
}, followsConventions: true);
it is better to use ODataConventionsModelBuilder as Javier has suggested. But if you still want to set up your own odata model you can do it:
var jobs = builder.EntitySet<Job>("Jobs");
jobs.HasNavigationPropertiesLink(customers.EntityType.NavigationProperties,
(context, navigationProperty) => context.GenerateNavigationPropertyLink(navigationProperty, false)
, followsConventions: true);
Normally I use stored procedures / work in SQL so apologies if I get the terminology slightly off here..
I have a database, with 3 seperate tables, and I need to search multiple fields in each of the 3 tables.
Im sure that I am not doing this the mose effective way, initially I am trying to do it in simple seteps to understand it.
I have the following;
var foo1 = entities.table1.Where(a => a.bodyText.Contains(searchString) || a.pageTitle.Contains(searchString));
var foo2 = entities.table2.Where(b => b.newsArticle.Contains(searchString) || b.newsArticle.Contains(searchString));
var foo3 = entities.table3.Where(c => c.ImageDescriptionContains(searchString));
I need to combine all these results into a single repeater for display.
At this point all 3 sets of data are in seperate, unique collections of anonymous data. So whats the best way of converting these into a single coherent bindable source?
I was thinking of itereating through each list in turn, pulling out the fields I need to display and putting them in a new class, then binding a lsit of these classes to the repeater.
But it all seems a bit clunky to me.
Is there a way of doing the search across all 3 tables in one go, and returning just the fields I need from each table, with a common name (i.e. in SQL I could write
select b.newsArticle as myText,
or
select newsArticle, ''
to return the news article and an empty string).
This would combine:
var foos = foo1.ToList();
foos.AddRange(foo2);
foos.AddRange(foo3);
To get just what you want:
var myExtractedValues = foos.Select(x => new {
Article = !string.IsNullOrEmpty(x.newsArticle))
? x.newsArticle
: string.Empty});
I have used an anonymous type here but you could swap the new {} with a type of your own.
I reverse the operator on the IsNullOrEmpty but that is just a personal preference (I prefer how is reads.)
To get all the results in one go you'll need to define a common class that will be used by all three queries to store the result. This class may be as well anonymous but I'll name it just for clarity.
class Data
{
public string Text{ get; set;}
}
Now, in your code you'll fetch instances of Data from database and you can use Union:
using( var entities = new YourDataContext)
{
var foo1 = entities.table1
.Where(a => a.bodyText.Contains(searchString) ||
a.pageTitle.Contains(searchString))
.Select(a => new Data{ Text = a.bodyText});
var foo2 = entities.table2
.Where(b => b.newsArticle.Contains(searchString) ||
b.newsArticle.Contains(searchString))
.Select(b => new Data{ Text = b.newsArticle});
var foo3 = entities.table3
.Where(c => c.ImageDescription.Contains(searchString))
.Select(c => new Data{ Text = c.ImageDescription});
return foo1.Union(foo2).Union(foo3);
}
My Code for a controller:
public ViewResult Index(int? ProjectID)
{
var user = HttpContext.User;
User profile = db.Users.Where(d => d.Email == user.Identity.Name).Single();
var contracts = db.Contracts.Include(c => c.Project);
if (!user.IsInRole("Admin"))
{
contracts = contracts.Where(p => p.Project.Client == profile.Client );
}
if (ProjectID != null)
{
contracts = contracts.Where(u => u.ID == ProjectID);
}
return View(contracts.ToList());
}
This is suppose to pull up all of the contracts whose parent project has the same client fk as the user currently signed in unless they are an admin. This isn't working.
I am getting the following error when the non-admin's look at the page:
Unable to create a constant value of type
'MembershipExt.Models.Client'. Only primitive types ('such as Int32,
String, and Guid') are supported in this context.
Do I need to use a second include or something?
What is the data type for p.Project.Client? I'm guessing that it is a complex type (it has properties off of it). Perhaps what you want is something like this...
contracts = contracts.Where(p => p.Project.Client.ClientID == profile.Client.ClientID );
Obviously I don't know what the object looks like, but perhaps this helps.
contracts = contracts.Where(p => p.Project.Client == profile.Client );
i am guessing the problem is in the above line the right hand side of lambda needs to be of simple type that is int32, string etc but you are having a complex type i.e. p.Project.Client
This looks wrong
db.Users.Where(d => d.Email == user.Identity.Name)
looks like you are comparing email with name