Local Query omits/ignores projection with "select" - breeze

Model (exposed mapping table for many-to-many relation):
public class TeamUsers
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long TeamUsersId { get; set; }
public Team Team { get; set; }
public User User { get; set; }
public bool Temporary { get; set; }
}
Two general methods for remote and local query:
getAllOfTypeSelectRemote = function (manager, success, failed, resource, orderBy, predicate, select) {
var query = EntityQuery
.from(resource)
.where(predicate)
.using(manager)
.select(select) //<------
.orderBy(orderBy);
return query.execute()
.then(success)
.fail(failed);
},
getAllOfTypeSelectLocally = function (manager, resource, orderBy, predicate, select) {
var res = null;
try {
res = EntityQuery
.from(resource)
.where(predicate)
.select(select) //<-----
.orderBy(orderBy)
.using(manager)
.executeLocally();
} catch (e) {
logger.error('resource: ' + resource + '<br>select: ' + select + '<br>orderBy:' + orderBy, 'Local query failed!');
}
return res;
};
called this way:
var selectStr = 'User'; //<------
var p1 = new breeze.Predicate("Team.TeamName", Qop.Equals, team);
var p2 = breeze.Predicate("User.UserName", Qop.NotEquals, username);
var predicate = p1.and(p2);
//var res = dsUtils.getAllOfTypeSelectRemote(manager, success, queryFailed, 'TeamUsers', 'User.Nachname, User.Vorname', p1, select);
var res = dsUtils.getAllOfTypeSelectLocally(manager, 'TeamUsers', 'User.Nachname, User.Vorname', p1, selectStr);
The remote query returns an array of User-Entities which is correct i believe. Modifying 'selectStr' to an invalid type like 'bla' throws an exception as expected.
result:
data.results = [{"User": {...}}, {"User": {...}}]
The local query just ignores the select and returns an array of TeamUser
result:
res=[{ Here are the fields of TeamUsers }, { Here are the fields of TeamUsers }]
Modifying 'selectStr' to an invalid type like 'bla' throws NO exception.
Why is select omitted, is this supposed to be like that?

As of v 0.74.3, this should be fixed; i.e. select clauses will now be correctly interpreted by local queries as well as remote queries. Hope this helps.

Related

ApplicationUser is not saving changes to field after there is already a value in that field

I'm working on a project that keeps track of the hours employees have worked between different crews. The way the app is supposed to work is that an employee submits a Daysheet form that has their clock-in and clock-out time and the id of their Field Manager(FMId). Each Field Manager has a column in the database that should keep track of the DaysheetIds of forms that are submitted with their FMId. The problem is that this column is only saving the first DaysheetId that gets submitted. Once there's a value in that column, the Field Manager doesn't update to add any other DaysheetIds.
To keep my post concise, I'm trying to post only the relevant code, but let me know if I'm missing something that would be important.
Here's the action in my controller where the Field Manager should be updated. I added the var FM just before the return to see what was happening to the Field Manager with the debugger.
public IActionResult Submit(EmployeeDaySheetViewModel employeeDaySheetViewModel)
{
if (ModelState.IsValid)
{
var newDaySheet = new EmployeeDaySheet
{
Id = employeeDaySheetViewModel.DaysheetId,
EmployeeId = employeeDaySheetViewModel.EmployeeId,
EmployeeName = employeeDaySheetViewModel.EmployeeName,
FMId = employeeDaySheetViewModel.FMId,
Date = employeeDaySheetViewModel.Date,
ClockIn = employeeDaySheetViewModel.ClockIn,
ClockOut = employeeDaySheetViewModel.ClockOut,
};
var success = _employeeDaySheetRepository.AddDaySheet(newDaySheet);
if (success)
{
_applicationUserRepository
.AddCrewDaySheetToFieldManager(employeeDaySheetViewModel.FMId,
employeeDaySheetViewModel.DaysheetId);
TempData["UserMessage"] = "Successfully submitted daysheet for " + DateTime.Now.Day.ToString();
}
else
{
TempData["ErrorMessage"] = "Unable to submit daysheet. Please try again in a few minutes.";
}
}
var FM = _applicationUserRepository.GetFieldManagerById(employeeDaySheetViewModel.FMId);
return Redirect("/home/");
Here is the AddCrewDaySheetToFieldManager method in my user repository that's being called in the controller action:
public bool AddCrewDaySheetToFieldManager(string FMId, string DaySheetId)
{
var fieldManager = _applicationDbContext.ApplicationUsers
.FirstOrDefault(u => u.Id == FMId);
if (fieldManager.CrewDaySheetIds != null)
{
var oldCrewIds = fieldManager.CrewDaySheetIds;
// If the user deletes all their crewIds, the database leaves an empty string in their crewIds column.
// Replacing user.crewIds that has "" at index 0 is the same as starting a new list.
if (oldCrewIds[0] == "")
{
var newCrewIds = new List<string> { DaySheetId };
fieldManager.CrewDaySheetIds = newCrewIds;
}
else
{
fieldManager.CrewDaySheetIds.Add(DaySheetId);
}
}
else { fieldManager.CrewDaySheetIds = new List<string> { DaySheetId }; }
_applicationDbContext.SaveChanges();
return true;
This is what my ApplicationUser looks like:
public class ApplicationUser : IdentityUser
{
public string Tier { get; set; }
public bool IsFieldManager { get; set; }
public double HourRate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<string> PayrollObjectIds { get; set; }
public List<string> CrewDaySheetIds { get; set; }
}
In order to convert the lists attributes of this object to strings, I've got this in my DbContext
protected override void OnModelCreating(ModelBuilder builder)
{
var splitStringConverter = new ValueConverter<List<string>, string>(v => string.Join(";", v), v => v.Split(new[] { ';' }).ToList());
builder.Entity<ApplicationUser>().Property(nameof(ApplicationUser.PayrollObjectIds)).HasConversion(splitStringConverter);
builder.Entity<ApplicationUser>().Property(nameof(ApplicationUser.CrewDaySheetIds)).HasConversion(splitStringConverter);
base.OnModelCreating(builder);
//I also seed some data here//
}
What I can't figure out is the point at which the data is not getting saved. When I run the debugger, I can see the fieldManager is getting updated. In the AddCrewDaySheetToFieldManager method, fieldManager.CrewDaySheetIds has the DaysheetId. Then, back in the action, just before return Redirect('/home/')the FM.CrewDaySheetIds still has the DaysheetId. However, when I look at the database or try to access the CrewDaysheetIds on the FM user, only the first DaysheetId is there.
I suspect that there's something going wrong with the splitStringConversion, but I've used the same code in another project and not had this issue, so I'm stuck for what to do.
I didn't understand your question correctly but if you want update or insert some data in your database you can use _applicationDbContext.ApplicationUsers.add(fieldManager); for insert, and _applicationDbContext.Entry(fieldManager).State = EntityState.Modeified; for update. but you didn't use any of them before _applicationDbContext.SaveChanges().
I think you have to write this:
public bool AddCrewDaySheetToFieldManager(string FMId, string DaySheetId)
{
var fieldManager = _applicationDbContext.ApplicationUsers
.FirstOrDefault(u => u.Id == FMId);
if (fieldManager.CrewDaySheetIds != null)
{
var oldCrewIds = fieldManager.CrewDaySheetIds;
// If the user deletes all their crewIds, the database leaves an empty string in their crewIds column.
// Replacing user.crewIds that has "" at index 0 is the same as starting a new list.
if (oldCrewIds[0] == "")
{
var newCrewIds = new List<string> { DaySheetId };
fieldManager.CrewDaySheetIds = newCrewIds;
}
else
{
fieldManager.CrewDaySheetIds.Add(DaySheetId);
}
}
else { fieldManager.CrewDaySheetIds = new List<string> { DaySheetId }; }
_applicationDbContext.entry(fieldManager).State = EntityState.Modified;
_applicationDbContext.SaveChanges();
return true;

Paging ASynchronously with MongoDB driver 2.9.0

I need to handle the amount of results returned when there are calls to my API (paging). I'm having real trouble achieving this with the new Async stuff that was put in the latest mongo c# drivers.
Mongo Service
public class MongoService
{
private readonly IMongoCollection<BsonDocument> _bsondocs;
public MongoService(IMongoDatabaseSettings settings)
{
//gets mongo connection string and database name from the
//MongoDatabaseSettings class which gets it from appsettings.json
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
//uses the name from MongoCollectionName variable, set by MongoDatabaseSettings.cs, again supplied from appsettings.json
_bsondocs = database.GetCollection<BsonDocument>(settings.MongoCollectionName);
}
internal async Task<SearchResult> SearchAsync(string q, int page)
{
//this part performs the actual search
var indexFilter = Builders<BsonDocument>.Filter.Text(q);
var totalRecords = await _bsondocs.CountDocumentsAsync(indexFilter);
//hard coded page size
var pageSize = 20;
var data = _bsondocs.Find(indexFilter)
.Skip((page - 1) * pageSize)
.Limit(pageSize);
//create a new search result which can track the pages.
var result = new SearchResult()
{
Data = data,
CurrentPage = page,
PageSize = pageSize,
TotalRecords = totalRecords
};
return result;
}
//this is the get method used by the controller to return full list of bson documents in a given DB collection.
public List<BsonDocument> Get()
{
return _bsondocs.Find(bsonDocument => true).ToList();
}
}
}
SearchResult class
public class SearchResult
{
public int CurrentPage { get; set; }
public int PageSize { get; set; }
public long TotalRecords { get; set; }
public ICollection<BsonDocument> Data { get; set; }
}
Call from the controller
[HttpGet("find")]
public async Task<IActionResult> SearchText(string q, int p)
//public ActionResult<List<BsonDocument>> SearchText(string q) =>
{
SearchResult result = await _mongoService.SearchAsync(q, p);
return Ok(result);
}
The error I'm currently getting is:
Error CS0266 Cannot implicitly convert type 'MongoDB.Driver.IFindFluent' to 'System.Collections.Generic.ICollection'. An explicit conversion exists
But I suspect I may have wider problems, I can only find very limited documentation around the ASync methods for the latest mongo driver.
It turns out in this case I was just missing the .ToList() on the find function. Changing my data variable to the following solved my error and got the paging working.
var data = _bsondocs.Find(indexFilter)
.Skip((page - 1) * pageSize)
.Limit(pageSize)
.ToList();

generic ralationship in neo4j c#

I need to establish a relationship between two different node type like this:
public class Fallow<T,U>: Relationship,
IRelationshipAllowingSourceNode<T>,
IRelationshipAllowingTargetNode<U>
{
public Fallow(NodeReference targetNode)
: base(targetNode)
{
}
public const string TypeKey = "FALLOW";
public DateTime relationDate { get; set; }
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
I have an error:
Error 1 'Biber10.Neo4j.Fallow<T,U>' cannot implement both 'Neo4jClient.IRelationshipAllowingParticipantNode<T>' and 'Neo4jClient.IRelationshipAllowingParticipantNode<U>' because they may unify for some type parameter substitutions C:\Users\turgut\Documents\Visual Studio 2013\Projects\Biber10\Biber10.Neo4j\Fallow.cs 10 18 Biber10.Neo4j
How do I fix it?.
Thanks.
We've moved away from the use of Relationship like this, the best example of the thing you're trying to do would be something like this:
public class Fallow
{
public const string TypeKey = "FALLOW";
public DateTime RelationDate { get; set; }
}
Used like so:
//Just using this to create a demo node
public class GeneralNode
{
public string AValue { get; set; }
}
var gc = new GraphClient(new Uri("http://localhost.:7474/db/data/"));
gc.Connect();
//Create
var node1 = new GeneralNode { AValue = "val1"};
var node2 = new GeneralNode { AValue = "val2" };
var fallow = new Fallow { RelationDate = new DateTime(2016, 1, 1)};
gc.Cypher
.Create($"(n:Value {{node1Param}})-[:{Fallow.TypeKey} {{fallowParam}}]->(n1:Value {{node2Param}})")
.WithParams(new
{
node1Param = node1,
node2Param = node2,
fallowParam = fallow
})
.ExecuteWithoutResults();
//Get
var query = gc.Cypher
.Match($"(n:Value)-[r:{Fallow.TypeKey}]->(n1:Value)")
.Return(r => r.As<Fallow>());
var results = query.Results;
foreach (var result in results)
{
Console.WriteLine("Fallow: " + result.RelationDate);
}

uCommerce - add dynamic property to order line

I have hit a problem building a uCommerce site based on top of the demo razor store available http://thesitedoctor.co.uk/portfolio/avenue-clothingcom/
The demo uses servicestack and the ucommerceapi for its basket functions.
I am trying to add a dynamic property to the basket (on an order line) at the point where the user clicks buy. I traced through the productpage.js file and amended the code to add a new property ('message'):
function (data) {
var variant = data.Variant;
$.uCommerce.addToBasket(
{
sku: variant.Sku,
variantSku: variant.VariantSku,
quantity: qty,
message: $('#personalisedMessage').val()
},
function () {
updateCartTotals(addToCartButton);
}
);
});
using firebug, i checked the data that is being posted
addToExistingLine: true
message: "this is a message"
quantity:"1"
sku: "Product (options: none)"
variantSku:""
Posting this does not cause an error, but I cannot tell if it has worked either - I cannot find it in the database, assuming that it would be stored in OrderProperty table. In this scenario, I am 'buying' a product with no variations.
Any help is greatly appreciated with this.
Out of the box you can't add order/line item properties via the API like that. The API payload that you've added to is specified although valid JSON won't get interpreted/used by the API.
Instead what you'll need to do is add your own method to the API. To do this you'll need to implement a service from IUCommerceApiService and then you can do what you need. I've created an example (untested) below and will get it added to the demo store as I think it's a useful bit of functionality to have.
public class AddOrderLineProperty
{
public int? OrderLineId { get; set; }
public string Sku { get; set; }
public string VariantSku { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class AddOrderLinePropertyResponse : IHasResponseStatus
{
public AddOrderLinePropertyResponse() { }
public AddOrderLinePropertyResponse(UCommerce.EntitiesV2.OrderLine line)
{
if (line == null)
{
UpdatedLine = new LineItem();
return;
}
var currency = SiteContext.Current.CatalogContext.CurrentCatalog.PriceGroup.Currency;
var lineTotal = new Money(line.Total.Value, currency);
UpdatedLine = new LineItem()
{
OrderLineId = line.OrderLineId,
Quantity = line.Quantity,
Sku = line.Sku,
VariantSku = line.VariantSku,
Price = line.Price,
ProductName = line.ProductName,
Total = line.Total,
FormattedTotal = lineTotal.ToString(),
UnitDiscount = line.UnitDiscount,
VAT = line.VAT,
VATRate = line.VATRate
};
}
public ResponseStatus ResponseStatus { get; set; }
public LineItem UpdatedLine { get; set; }
}
public class AddOrderLinePropertyService : ServiceBase<AddOrderLineProperty>, IUCommerceApiService
{
protected override object Run(AddOrderLineProperty request)
{
var orderLineId = request.OrderLineId;
var sku = request.Sku;
var variantSku = request.VariantSku;
var orderLine = findOrderLine(orderLineId, sku, variantSku);
addPropertyToOrderLine(orderLine, request.Key, request.Value);
TransactionLibrary.ExecuteBasketPipeline();
var newLine = findOrderLine(orderLineId, sku, variantSku);
return new AddOrderLinePropertyResponse(newLine);
}
private void addPropertyToOrderLine(OrderLine orderLine, string key, string value)
{
if (orderLine == null)
return;
orderLine[key] = value;
orderLine.Save();
}
private static OrderLine findOrderLine(int? orderLineId, string sku, string variantSku)
{
return orderLineId.HasValue
? getOrderLineByOrderLineId(orderLineId)
: getOrderLineBySku(sku, variantSku);
}
private static OrderLine getOrderLineBySku(string sku, string variantSku)
{
return String.IsNullOrWhiteSpace(variantSku)
? getOrderLines().FirstOrDefault(l => (l.Sku == sku))
: getOrderLines().FirstOrDefault(l => (l.Sku == sku && l.VariantSku == variantSku));
}
private static OrderLine getOrderLineByOrderLineId(int? orderLineId)
{
return getOrderLines().FirstOrDefault(l => l.OrderLineId == orderLineId);
}
private static ICollection<OrderLine> getOrderLines()
{
return TransactionLibrary.GetBasket().PurchaseOrder.OrderLines;
}
}
You'll need to add the new method to uCommerce.jQuery.js as well something like this:
addOrderLineProperty: function (options, onSuccess, onError) {
var defaults = {
orderLineId: 0
};
var extendedOptions = $.extend(defaults, options);
callServiceStack({ AddOrderLineProperty: extendedOptions }, onSuccess, onError);
}
Let me know if you have any issues using it.
Tim

Nullable<System.DateTime> and BreezeJS

I have this class:
public partial class VehicleSize
{
public VehicleSize()
{
}
public System.Guid VehicleSizeId { get; set; }
public int Title { get; set; }
public int SizeOrder { get; set; }
public System.DateTime DateCreated { get; set; }
public Nullable<System.DateTime> DateDeleted { get; set; }
}
I'm using with BreezeJS.
Handling of Nullable seems a bit odd. This field is meant to be optional. But it complains until I explicitly set DateDeleted = null from the javascript. If I don't set the field to null I'll get "Problem saving TypeError: entity.dateDeleted is null".
It seems if i inspect the .dateDeleted property in javascript it is a javascript Date object but getTime isNAN. Shouldn't it be null in this case instead and the BreezeJS validator skip validating it as the field is null.
The Meta data from the server defines the field as this:
"name": "DateDeleted",
"type": "Edm.DateTime",
"nullable": "true"
Does anyone have an idea on how I create an optional DateTime field?
Further to my original post. The problem seems to be related to exporting Entities from one entity manager to another. A Date field that starts out as "null" becomes "Invalid Date" after its imported again as demonstrated by this code:
var vehicleSizeId = 'E9DA5803-BB65-4751-AA22-17B54A1EE7C1';
alert('getting vehicle from db');
var query = breeze.EntityQuery
.from("VehicleSizes")
.where("vehicleSizeId", "==", vehicleSizeId);
var em = new breeze.EntityManager("api/Todo");
em.enableSaveQueuing(true);
var sandBoxEm = em.createEmptyCopy();
sandBoxEm.enableSaveQueuing(true);
em.executeQuery(query)
.then(function (data) {
alert('Found the vehicle size in the original entity manager')
var entity = data.results[0];
alert('Org Date Deleted == ' + entity.dateDeleted); // Its null at this point
var bundle = em.exportEntities();
sandBoxEm.importEntities(bundle, { mergeStrategy: breeze.MergeStrategy.OverwriteChanges });
});
sandBoxEm.executeQuery(query)
.then(function (data) {
alert('Found the vehicle size')
var entity = data.results[0];
alert('Date Deleted == ' + entity.dateDeleted); // Now its invalid date
entity.sizeOrder = entity.sizeOrder + 1;
entity.titleTranslation.text = entity.titleTranslation.text + "_x";
//entity.titleTranslation.dateDeleted = null;
//entity.dateDeleted = null;
try {
sandBoxEm.saveChanges().then(
function () {
alert('It saved ok');
}).fail(
function (error) {
var firstItem = error.entitiesWithErrors[0];
var firstError = firstItem.entityAspect.getValidationErrors()[0];
var msg = "prop: " + firstError.property.parentType.name + " = " + firstError.errorMessage;
alert(msg);
}
);
}
catch (ex) {
alert( "Problem saving " + ex );
}
}
).fail(
function () {
alert('Getting the vehicle size failed');
});
Edit: May 8, 2013 - The issue with importing a previously exported null date is now fixed in v 1.3.3 and available on the Breeze website.
Not sure what you are experiencing. I just wrote a unit test to try and confirm what you are seeing and are not able to repro your issue. I am running this against an Employee model where the "birthDate" also defined as a Nullable.
All tests pass on this:
var em = newEm(); // creates a new EntityManager
var emp = em.createEntity("Employee", { firstName: "Joe", lastName: "Smith" });
ok(emp.entityAspect.entityState === breeze.EntityState.Added, "entityState should be 'Added'");
var birthDate = emp.getProperty("birthDate");
ok(birthDate === null, "birthDate should be null");
var q = EntityQuery.from("Employees").where("birthDate", "==", null);
stop();
em.executeQuery(q).then(function(data) {
var empsWithNullBirthDates = data.results;
ok(empsWithNullBirthDates.length > 0, "should be at least 1 employee with a null birthdate");
empsWithNullBirthDates.forEach(function(emp) {
var birthDate = emp.getProperty("birthDate");
ok(birthDate === null, "queried birthDate should be null");
});
}).fail(testFns.handleFail).fin(start);

Resources