I know the include-feature of RavenDB. It allows me to fetch a referenced document right away in one roundtrip to the database. But my problem is: The document i fetch in the first place is not including a reference to the "other" documents. But the "other" documents have references to the current document.
Imagine a setup where we have sites across the world. Each site may trigger various alarms. Each alarm has a reference to the site via siteId.
Now i would like to get a list of all the sites including all alarms. But it looks like, this is not possible with RavenDB? Since include only accepts a "path" in the site-Document which holds an id (or an array of ids) to the referenced document.
This could be solved by providing an array of alarmIds within the site'-document and referencing this array in include. But in contrast to a lot of examples featuring stuff like an orderwithlineItemswhere the order is a self contained thing, mysite` will be running for years, collecting alarms anywhere between 0 and a million. Which seems to be a bad idea to me.
Of course i could go the other way round: Query all alarms and include the sites via sitesId. But this would not return a site that has zero alarms.
So is this just a design error on my side? To i misunderstand something? Or is it just not possible to do this in one query and prevent a "n+1 query"?
public class A
{
public string Id { get; set; }
}
public class B
{
public string Id { get; set; }
public string A { get; set; }
}
public class MultiMapIndex : AbstractMultiMapIndexCreationTask<MultiMapIndex.Result>
{
public class Result
{
public string Id { get; set; }
public IEnumerable<string> Bs { get; set; }
}
public MultiMapIndex()
{
AddMap<A>(items => from a in items
select new Result {Id = a.Id, Bs = new string[0]});
AddMap<B>(items => from b in items
select new Result {Id = b.A, Bs = new[] {b.Id}});
Reduce = results => from result in results
group result by result.Id
into g
select new Result {Id = g.Key, Bs = g.SelectMany(r => r.Bs)};
}
}
[Fact]
public async Task TestCase()
{
using var store = GetDocumentStore();
await new MultiMapIndex().ExecuteAsync(store);
using (var session = store.OpenAsyncSession())
{
await session.StoreAsync(new B {A = "a/1"}, "b/0");
await session.StoreAsync(new A(), "a/1");
await session.StoreAsync(new A(), "a/2");
await session.SaveChangesAsync();
}
WaitForIndexing(store);
using (var session = store.OpenAsyncSession())
{
var results = await session.Query<MultiMapIndex.Result, MultiMapIndex>()
.Include(r => r.Bs)
.ToArrayAsync();
var before = session.Advanced.NumberOfRequests;
var bs = session.LoadAsync<B>(results[0].Bs);
Assert.Equal(before, session.Advanced.NumberOfRequests);
}
}
If you do choose to query all Alarms, as you mention,
then you can create a Map-Reduce index on the Alarms collection which will group-by the Sites.
Then you can query this Map-Reduce index and know per Site the count of Alarms it has or doesn't have...
https://demo.ravendb.net/demos/csharp/static-indexes/map-reduce-index
Related
We have an Department model (domain-driven design). Each department has its child departments, so domain model looks like
public class Department
{
int Id { get; set; }
...
ICollection<Department> Children { get; set; }
}
At the API domain models of the same hierarchy path, coming from repository, it will transforms to DTO trough AutoMapper and does not include children by default.
public class DepartmentDto
{
int Id { get; set; }
...
ICollection<DepartmentDto> Children { get; set; } // Empty set.
}
Does it a good way to add [NotMapped] bool HasChildren property to the Department domain model to show or hide expand arrows at the client? For lazy load.
This field smells strange: can be filled or can be not (depends on query).
Repository returns a collection of departments, belongs to parent Id (may become Null to root nodes):
ICollection<Department> GetDepartments(int? parentId = null);
So, based on Lucian Bargaoanu comments, I've found the solution:
IDepartmentRepository.cs
IQueryable<Department> GetDepartmentsQuery(int? parentId = null);
DepartmentsController.cs (API):
[HttpGet]
public async Task<ActionResult<ICollection<DepartmentDto>>> GetRootDepartments()
{
var dtoItems = await _repository.GetDepartmentsQuery()
.ProjectTo<DepartmentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
return Ok(dtoItems);
}
AutoMapper configuration:
CreateMap<Department, DepartmentDto>()
.ForMember(x => x.HasChildren,
opts => opts.MapFrom(x => x.Children.Any()))
.ForMember(x => x.Children,
opts => opts.Ignore());
First of all there are several similar questions and I have tried looking at them every where but none of them worked .
I have this mapping code
var updateEntities = await _dbContext.AnswerAnchors.Where(x => x.SurveyResultId == surveyResultId).ToListAsync();
updateEntities = _mapper.Map<IEnumerable<AnswerAnchorsDomainModel>, List<AnswerAnchors>>(anchors, updateEntities);
_dbContext.UpdateRange(updateEntities);
And here is my destination Object structure .
public class AnswerAnchors : BaseEntity<long>
{
public long SurveyResultId { get; set; }
public DateTimeOffset AnchorDate { get; set; }
public string AnchorDescription { get; set; }
}
public class BaseEntity<T> : IBaseEntity
where T : struct
{
[Key]
public T Id { get; set; }
}
What is happening is the existing Id needs to be mapped and not overwritten by the destination under any circumstances because entity framework needs to update the entity. However whenever the mapping happens the Id property gets overwritten to zero(0). I tried using opt.Ignore() and opt.UseDestinationValue() but none of them retains the destination Id value.
Here is the profile -
CreateMap<AnswerAnchorsDomainModel, AnswerAnchors>()
.ForMember(anchor => anchor.Id, opt => opt.UseDestinationValue())
.AfterMap((src, dest) =>
{
dest.CreatedBy = "";
dest.LastUpdateBy = "";
dest.CreateOn = DateTimeOffset.Now;
dest.UpdateOn = DateTimeOffset.Now;
});
My work around to this, fetching all data from repo, do a deep clone, then _context.Entry(entity here).State = EntityState.Detached; mapping all retaliated object _mapper.Map(source, destination) assigning the id's back using the clone object .... witch change the state back to modified, then await _repo.SaveAllAsync() ....I do this for each related object. Not the best of ways, but it works for now.
Another workaround and better way is the get updateEntities from repo witch contains the id properties in detached state ...
then foreach loop the AnswerAnchorsDomainModel assighn the id's with query in updateEntities to AnswerAnchorsDomainModel
_mapper.Map, List>(anchors,
updateEntities);
Change the updateEntities state back to modified.
Save Changes...
Done..
This way you map the correct id's to the update enity's ...
now EF and in my case EFcore, know's what to update and what to add.
I have table structures that look like below:
table Tenant: Id[PK], etc
table Contact: Id[PK], FirstName, LastName etc
table Sale: Id[PK], TenantId[FK], SellerId[FK], BuyerId[FK], etc
SellerId is a FK to Contact.Id
BuyerId is a FK to Contact.Id
TenantId is a FK to Tenant.Id
I want to use OrmLite to generate SQL similar to below:
select sale.*
, buyer.FirstName 'BuyerFirstName'
, buyer.LastName 'BuyerLastName'
, seller.FirstName 'SellerFirstName'
, seller.LastName 'SellerLastName'
from sale
left join
contact seller
on sale.SellerId = seller.Id
left join
contact buyer
on sale.BuyerId = buyer.Id
where tenantId = 'guid' -- this is being filtered at a global level
Because I want to have a strongly typed global filter to filter out result by tenantId (on database side) I have code looks like below
public List<TOut> Exec<TIn, TOut>(SqlExpression<TIn> exp) where TIn : IHaveTenantId
{
exp.Where(x => x.TenantId == _tenantId);
return _conn.Select<TOut>(exp);
}
The poco of Sale looks like below:
public class Sale : IHaveTenantId
{
public Guid Id { get; set; }
[ForeignKey(typeof(Contact), OnDelete = "CASCADE")]
public Guid BuyerId { get; set; }
[ForeignKey(typeof(Contact), OnDelete = "CASCADE")]
public Guid SellerId { get; set; }
//etc
}
And I'm trying to use strongly typed LeftJoin syntax like below:
public class SaleView
{
public Guid Id { get; set; }
public string BuyerFirstName { get; set; }
public string SellerLastName { get; set; }
//etc
}
var result = Exec<SaleView, Sale>(_conn
.From<Sale>()
.LeftJoin<Contact>((sale, seller) => sale.SellerId == seller.Id)
.LeftJoin<Contact>((sale, buyer) => sale.BuyerId == buyer.Id));
I couldn't figure out how to join the same table multiple times and have an alias per join (e.g. left join contact as 'seller', hence I can select seller.FirstName, buyer.FirstName) and I don't want to use parameterised raw sql.
Is this possible at all with OrmLite?
Support for typed JOIN aliases were added in v4.0.62, e.g:
var q = db.From<Sale>()
.LeftJoin<ContactIssue>((s,c) => s.SellerId == c.Id, db.JoinAlias("seller"))
.LeftJoin<ContactIssue>((s,c) => s.BuyerId == c.Id, db.JoinAlias("buyer"))
.Select<Sale, ContactIssue>((s,c) => new {
s,
BuyerFirstName = Sql.JoinAlias(c.FirstName, "buyer"),
BuyerLastName = Sql.JoinAlias(c.LastName, "buyer"),
SellerFirstName = Sql.JoinAlias(c.FirstName, "seller"),
SellerLastName = Sql.JoinAlias(c.LastName, "seller"),
});
Prior to v4.0.62 you can continue to use a Typed SqlExpression with custom SQL for this, e.g:
var q = db.From<Sale>()
.CustomJoin("LEFT JOIN Contact seller ON (Sale.SellerId = seller.Id)")
.CustomJoin("LEFT JOIN Contact buyer ON (Sale.BuyerId = buyer.Id)")
.Select(#"Sale.*
, buyer.FirstName AS BuyerFirstName
, buyer.LastName AS BuyerLastName
, seller.FirstName AS SellerFirstName
, seller.LastName AS SellerLastName");
The benefit of which is that it still leaves a Typed API where you can add additional filters like a global TenantId filter, e.g:
q.Where(x => x.TenantId == tenantId);
And then project it into your Custom POCO with:
var sales = db.Select<SaleView>(q);
The new CustomJoin API is available from v4.0.37+ that's now available on MyGet.
I have a custom content type built through the UI (e.g. not via a module) that has a couple of fields on it, one of which is a ContentItemPicker. I managed to get everything with the front-end working for this with the exception of finding the friendly URL off of the ContentItem from the Model's collection of items. I'm seeing some examples where I'm supposed to use Url.ImageDisplayUrl([ContentItem]), but that gives me this error: 'System.Web.Mvc.UrlHelper' has no applicable method named 'ItemDisplayUrl' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
My using statements at the top are as follows:
#using Orchard.ContentPicker.Fields
#using Orchard.Utility.Extensions;
#using System.Linq
#using Orchard.ContentManagement;
#using Orchard.Mvc.Html;
#using Orchard.Utility.Extensions;
I'd assume I'm missing something with those, but can't seem to figure out what. The way I'm building out my view is below, and the URL I am trying to get is off of tab.ContentItem.HomepageTab.NavigationItem:
/** #Model.Items is a collection of my custom content types that were created through the UI **/
foreach (var tab in #Model.Items)
{
var t = new SliderTab
{
DisplayOrder = tab.ContentItem.HomepageTab.DisplayOrder.Value,
ButtonText = tab.ContentItem.HomepageTab.ButtonText.Value,
Description = tab.ContentItem.HomepageTab.Description.Value,
ImageUrl = tab.ContentItem.HomepageTab.Image.Url,
Title = tab.ContentItem.TitlePart.Title,
ContentItem = tab.ContentItem,
TabText = tab.ContentItem.HomepageTab.TabText.Value
};
/** HomepageTab is the custom content type created in the Orchard UI which has a ContentPickerField associated with it. The name on that is NavigationItem, so I just need the friendly URL off of a ContentPickerField's associated ContentItem **/
if (tab.ContentItem.HomepageTab.NavigationItem != null && tab.ContentItem.HomepageTab.NavigationItem.Ids != null)
{
//this is way, super hacky - getting the actual friendly URL would be better
t.NavigateUrl = "/Contents/Item/Display/" + tab.ContentItem.HomepageTab.NavigationItem.Ids[0];
}
tabs.Add(t);
}
** Edit **
I have a class declaration for HomepageTab at the top which does not correlate to the tab.ContentItem.HomepageTag as that is dynamic off the ContentItem property. It is structured like this:
public class HomepageTab
{
public dynamic DisplayOrder { get; set; }
public string ImageUrl { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ButtonText { get; set; }
public dynamic ContentItem { get; set; }
public string TabText { get; set; }
public string NavigateUrl { get; set; }
public string TabId
{
get { return "t" + this.DisplayOrder.ToString(); }
}
}
Thoughts?
tab.ContentItem.HomepageTab.NavigationItem is your content item picker field, but expressed this way, it's a dynamic object, so the compiler can get all sorts of confused if you try to use it without casting it. So first I'd recommend casting:
var pickerField = tab.ContentItem.HomepageTab.NavigationItem as ContentPickerField;
if (pickerField != null) {
Then you can get the first and only item in the field (caution, we're likely causing a select N+1 issue here, see below):
var firstItem = pickerField.ContentItems.FirstOrDefault();
Finally, we can ask for the display URL for that item:
if (firstItem != null) {
var url = Url.ItemDisplayUrl(firstItem);
This should work just fine. Be careful however: as I said above, getting the collection of items for each tab may trigger one new database query per tab, degrading performance. To avoid that problem, you could pre-fetch the related content items with a technique similar to what I describe in this post: https://weblogs.asp.net/bleroy/speeding-up-pages-with-lots-of-media-content-items
Instead of pre-fetching images, what you'd do here is first get a list of all the related ids (that's free, those ids come stored in the fields, which are stored with the content items you already have). Then you'd build a local cache of ids to items using a GetMany. And finally you'd use that cache instead of the ContentItems collection to look-up items from the ids.
I hope this makes sense.
This code works fine.
using (ContextDB db = new ContextDB())
{
var custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
But this one doesn't
public class CustAcct
{
public int AcctNo { get; set; }
public string Company { get; set; }
public string UserName { get; set; }
}
....
....
....
using (ContextDB db = new ContextDB())
{
CustAcct custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
It returns this error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'EMailReader.Models.CustAcct'. An explicit conversion exists (are you missing a cast?)
I used Google, found many related topics but still couldn't put it to work using the available solutions
I just need to return data to a strong typed model.
EDITED:
After more research I found this solution bellow, but I wonder why I cannot retrieve directly in the list from LinqToSql.
List<CustAcct> temp = new List<CustAcct>();
IEnumerable<dynamic> items = custAcct;
foreach (var item in items)
{
temp.Add(new CustAcct()
{
AcctNo = item.AcctNo,
Company = item.Company,
UserName = item.UserName,
});
}
You are re defining those properties by creating new Class. And this will override LINQ2SQL generated class.
Just change "public class CustAcct" to "public partial class CustAcct".
This will solve your problem, and you do not need to define those properties again. Remove those from your class. Those will be automatically create for you.
If you can just post your class, and I will change it for you.
//Shyam