I have this model:
namespace Model
{
public class Category
{
public int Id { get; set; }
public string Name { get; private set; }
public Category()
{ }
public Category(string name)
{
Name = name;
}
}
}
When I store a document and retrieve it, the result is a list of documents with zero elements:
using (var session = _documentStore.OpenSession())
{
session.Store(category);
session.SaveChanges();
var categories = session.Query<Model.Category>().ToList();
}
category get the proper id, that is "1". But when I do the Query, then I do not get any elements.
If I Load the document:
var category = session.Load<Model.Document>("categories/1")
Instead of the Query, I get the proper category.
How do I load all documents from Raven?
I figured it out:
I have to wait for non staled results. So if I change my Query out with this:
session.Query<Model.Category>().Customize(cr => cr.WaitForNonStaleResults()).ToList();
it works just fine.
Related
I want to push a ProductPhoto inside the Product using LINQ. Here is my model:
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string Detail { get; set; }
public List<ProductPhoto> ProductPhotos { get; set; }
}
public class ProductPhoto
{
public string Id { get; set; }
public string Url { get; set; }
}
How can I achieve this with the LINQ query? I am expecting the result to be something like this.
{
_id: ObjectId('602d2149e773f2a3990b47f5'),
Name: 'Some name',
Detail: 'Some description',
ProductPhotos: [
{ _id: ObjectId('602d2149e773f2a3990b47f5'), Url: 'Some URL' }
]
}
Next time when I add a new ProductPhoto, then there will be two elements.
Thanks!
Here is the solution to similar question:
How to 'Find And Push' element in an array inside of another array in a document using LINQ to MongoDb
You can achieve this with something like this:
var filterBuilder = Builders<Product>.Filter;
var filter = filterBuilder.Eq(x => x.Id, productId);
var updateBuilder = Builders<Product>.Update;
var update = updateBuilder.Push(doc => doc.ProductPhotos, productPhoto);
Products.UpdateOneAsync(filter, update);
I'm trying to create several documents within a transaction, and one of the documents needs to be inserted as a BsonDocument as I have to be able to mutate a couple of values prior to insertion.
The two documents that I don't need to map .ToBsonDocument() are properly getting a new ID generated, but the main document is not.
Here is a basic class structure that I'm using.
public abstract class BaseDto
{
[JsonProperty("_id")] public string Id { get; init; }
[JsonProperty("_partitionKey")] public string PartitionKey { get; init; } = Constants.MasterDataPartitionKey;
}
public class ProductDto :BaseDto
{
public string Name { get; init; }
public string BrandId { get; init; }
public BrandDto Brand { get; init; }
public string ProducerId { get; init; }
public ProducerDto Producer { get; init; }
}
public class BrandDto :BaseDto
{
public string Name { get; init; }
}
public class ProducerDto :BaseDto
{
public string Name { get; init; }
}
And I'm configuring my mappings without the MongoDb specific attributes.
BsonClassMap.RegisterClassMap<BaseDto>(map =>
{
map.MapIdProperty(c => c.Id)
.SetElementName("_id")
.SetIdGenerator(CannectIdGenerator.Instance)
.SetSerializer(new StringSerializer(BsonType.String));
map.MapProperty(x => x.PartitionKey).SetElementName("_partitionKey");
});
BsonClassMap.RegisterClassMap<ProducerDto>(map => map.AutoMap());
BsonClassMap.RegisterClassMap<BrandDto>(map => map.AutoMap());
BsonClassMap.RegisterClassMap<ProductDto>(map => map.AutoMap());
The trouble I'm running into is that my Product isn't getting an auto-generated ID (the others are). Here's my transaction
[HttpPost]
public async Task<ActionResult<string>> Add(ProductDto product)
{
using var session = await _database.Client.StartSessionAsync();
try
{
session.StartTransaction();
// NOTICE THIS IS A `BsonDocument`
var products = _database.GetCollection<BsonDocument>(MasterCollection.Products.DisplayName);
var brands = _database.GetCollection<BrandDto>(MasterCollection.Brands.DisplayName);
var producers = _database.GetCollection<ProducerDto>(MasterCollection.Producers.DisplayName);
// THIS INSERT GETS A PROPER ID
await producers.InsertOneAsync(session, product.Producer);
// THIS INSERT GETS A PROPER ID
await brands.InsertOneAsync(session, product.Brand);
var insert = product.ToBsonDocument();
insert[nameof(ProductDto.Brand)] = insert[nameof(ProductDto.BrandId)] = product.Brand.Id;
insert[nameof(ProductDto.Producer)] = insert[nameof(ProductDto.ProducerId)] = product.Producer.Id;
// THIS INSERT DOES NOT GET AN ID, INSTEAD IT JUST STAYS `null`
await products.InsertOneAsync(session, insert);
await session.CommitTransactionAsync();
return Ok(insert["_id"]);
}
catch
{
await session.AbortTransactionAsync();
return Problem("Transaction Aborted");
}
}
What I'm trying to avoid is to have to manually initialize the ID field, I'd much prefer it to happen as a part of an insert.
I have a list of elements that contain another list.
public class SiloNode
{
public string Key { get; private set; }
public string Url { get; private set; }
public List<NodeQuery> Queries { get; private set; }
}
public class NodeQuery
{
public string Query { get; private set; }
public int Seq { get; private set; }
}
I need to produce a new list containing all queries, with the corresponding parent url.
An example would be:
https://www.bbc.co.uk, How old is the BBC?
https://www.bbc.co.uk, Where can I find a guide to BBC channels?
https://www.bbc.co.uk, How is the BBC funded?
https://www.channel4.com, More queries about channel 4 ...
The output should be in the form of class LinkMeta.
public class LinkMeta
{
public LinkMeta(string url, string text)
{
Url = url;
Text = text;
}
public string Text { get; private set; }
public string Url { get; private set; }
}
The complete list is contained in: root.Children.
The query below gives me all Query elements but I can't get back to Url.
var filtered = root.Children.SelectMany(x => x.Queries.Select(y => y.Query));
You can achieve it with the below linq.
var filtered = (from snode in root
from qry in snode.Queries
select new LinkMeta(snode.Url, qry.Query)).ToList();
It will return List of LinkMeta. Hope it helps.
Lambda expressions have the same scope as the nested block. This means that the nested lambda expression still has access to the variable 'x'. So, you should be able to do the following:
var filtered = root.SelectMany(x => x.Children.Queries.Select(y => new LinkMeta(x.Url, y.Query));
I have a input field where a user can insert a comma separated list, then on the save method, it tries to split the string and save into tags table.
When I save the post, I get this in my tags table: (GalleryId is the post its related to)
Each separated value should go into its own row, but instead, I get that.
This is what I do on the Save method:
[HttpPost]
public async Task<IActionResult> Save(Gallery gallery) {
var tags = gallery.Tags.ToString();
gallery.Tags = ParseTags(tags);
_context.Galleries.Add(gallery);
await _context.SaveChangesAsync();
return RedirectToAction("UploadForm", "Gallery");
}
public List<Tag> ParseTags(string tags)
{
return tags.Split(",").Select(tag => new Tag
{
Name = tag
}).ToList();
}
And this is my Gallery Model:
namespace SimpleImageGallery.Models
{
public class Gallery
{
public int Id { get; set; }
public string Title { get; set; }
public virtual IEnumerable<Tag> Tags { get; set; }
}
Tag class:
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
}
What can I do to get them to split?
I got it working, I just had to add the string tags into my parameter like this:
public async Task<IActionResult> Save(Gallery gallery, string tags) {
if (tags != null)
{
gallery.Tags = ParseTags(tags);
}
_context.Galleries.Add(gallery);
await _context.SaveChangesAsync();
return RedirectToAction("UploadForm", "Gallery");
}
Also, check to see if any tags passed in before handling it.
I current have an object that has a public property called Id. When I store the object, I would like the Id to be part of the Data and not become the document Id like it currently does. When creating the document store I only set the connection string.
using (var session = documentStore.OpenSession())
{
session.Store(a);
session.SaveChanges();
}
a can be thought of an object as:
public class A
{
public Guid Id { get; set; }
//other properties
}
So either I want it to generate a random Id or have both the data and the documentId being the Id property of class A.
EDIT:
So two possibilities:
1. Document Id = Id and the
Data Section contains:
{
data //sorry if the notation is not correct, doing it off of memory
{
Id: [my guid]
//other properities
}
}
Or the data containing the Id and Document ID = randomly generated by raven
You can use this if you want Id to be a property that you control on your own and have raven use another property as the documents id instead:
public class User
{
public string DocumentId { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public User()
{
Id = Guid.NewGuid();
}
}
documentStore.Conventions.FindIdentityProperty = prop =>
{
if (prop.DeclaringType == typeof(User))
return prop.Name == "DocumentId";
return prop.Name == "Id";
};