RavenDB select documents by index field - c#

I'm new to RaveDB (Actually I've started learn it only yesterday). And try to implement some functionality.
Lets we have next class hierarchy:
public abstract class Transaction
{
public string Id { get; set; }
}
public class CategoryTransaction : Transaction
{
public string AccountId { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Amount { get; set; }
}
public class ExchangeTransaction : Transaction
{
public string DebitAccountId { get; set; }
public string CreditAccountId { get; set; }
public decimal DebitAmount { get; set; }
public decimal CreditAmount { get; set; }
}
Everything storing excelent int db. I event add Conventions = FindTypeTagName ... for store documents as 'transactions/*' document id.
But I want to create index for select all transactions for specific Account (by field AccountId). I've create index:
public class AccountTransactionResult
{
public string AccId { get; set; }
}
public class Account_Transactions : AbstractMultiMapIndexCreationTask<AccountTransactionResult>
{
public Account_Transactions()
{
this.AddMap<CategoryTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.AccountId
} );
this.AddMap<ExchangeTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.DebitAccountId
} );
this.AddMap<ExchangeTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.CreditAccountId
} );
}
}
This index works well I can't get all type of transactions from DB (Exchange and Category) as single IEnumerable<Transaction>. That is great.
But I want to use the index to return transactions only for particular account. Because ExchangeTransaction can belong to two Accounts. I want to see ExchangeTransaction for both Accounts. I can make query in Raven Studio by AccId (index field) and it works greate! But I can't create the same request in the code :(.
Can someone help me? How can I use index field AccId in C# LINQ query?
This code
var query = session.Query<Transaction, Account_Transactions>()
return all type of transaction, but I don't how to filter the transactions by index field in the DB.
Thanks in advance. And please sorry me my English, it is not my native language.

My fault, in documentation everythings is clear. I missed some useful paragraph.
The easiest way to do this is by writing a multi-map index like this
one:
public class AnimalsIndex : AbstractMultiMapIndexCreationTask
{
public AnimalsIndex()
{
AddMap<Cat>(cats => from c in cats
select new { c.Name });
AddMap<Dog>(dogs => from d in dogs
select new { d.Name });
}
}
And query it like this:
var results = session.Advanced.LuceneQuery<object>("AnimalsIndex")
.WhereEquals("Name", "Mitzy");
>
In my case I should use next construction:
var transactions = session.Advanced.LuceneQuery<Transaction>( "Account/Transactions" )
.WhereEquals( "AccId", account2.Id )
And everything is working now.

Related

Building objects with many-to-many relationship using Dapper

Consider an Sqlite database, whose partial schema is shown below (we are not considering the Book_Tag table here). Note the many-to-many relationship between media items and tags using the link table Media_Tag:
An object model for these tables is as follows:
public enum MediaType
{
Dvd,
BluRay,
Cd,
Vhs,
Vinyl,
Other
}
public class MediaItem
{
public MediaType type { get; set; }
public long number { get; set; }
public int runningTime { get; set; }
public int releaseYear { get; set; }
public ICollection<Tag> tags { get; set; }
}
public class Tag
{
public string name { get; set; }
}
currently, Dapper is being used to read from the Media table, but without considering tags. The code is as follows:
public IEnumerable<MediaItem> readAll()
{
using (var db = new SqliteConnection(this.connectionString))
{
db.Open();
var sql = "SELECT * FROM Media;";
return db.Query<MediaItem>(sql);
}
}
public MediaItem readById(int id)
{
using (var db = new SqliteConnection(this.connectionString))
{
db.Open();
var sql = "SELECT * FROM Media WHERE id = #id;";
var #params = new { id = id };
return db.Query<MediaItem>(sql, #params).First();
}
}
How to change this so that the tag property of MediaItem is considered when creating the objects, for both cases (read by id and read all rows from the table)? Is a join query required? I'm sure Dapper has a way of doing this nicely, but I don't know how it's done.
You are not interested in anything from the link table so something like this SQL should do:
SELECT M.Id, M.title, M.type, M.Number, M.image, M.runningTime, M.releaseYear, T.Id, T.Name FROM Media as M
INNER JOIN Media_Tag AS MT ON M.id = MT.mediaId
INNER JOIN Tags AS T ON T.id = MT.tagId
If SqLite allows you can use M.*, T.* instead.
I have taken the liberty to add Id properties to your entity classes. I think you are going to need it, otherwise all your tags will be different instead of being unique. You might make it work without it, but it should make your life easier.
public class MediaItem
{
public int Id { get; set; } // New
public MediaType type { get; set; }
public long number { get; set; }
public int runningTime { get; set; }
public int releaseYear { get; set; }
public ICollection<Tag> tags { get; set; }
}
public class Tag
{
public int Id { get; set; } // New
public string name { get; set; }
}
Since both your entity classes have a unique id, you will have to pick them up and make sure they are unique going through the results. We do that by using dictionaries to keep them. I'm only showing the ReadAll, you should be able to do ReadById accordingly.
string sql = "<see above>";
using (var db = new SqliteConnection(this.connectionString))
{
var mediaDictionary = new Dictionary<int, Media>();
var tagDictionary = new Dictionary<int, Tag>();
var list = db.Query<Media, Tag, Media>(
sql,
(media, tag) =>
{
Media mediaEntry;
if (!mediaDictionary.TryGetValue(media.Id, out mediaEntry))
{
// Haven't seen that one before, let's add it to the dictionary
mediaEntry = media;
mediaDictionary.Add(mediaEntry.Id, mediaEntry);
}
Tag tagEntry;
if (!tagDictionary.TryGetValue(tag.Id, out tagEntry))
{
// Haven't seen that one before, let's add it to the dictionary
tagEntry = tag;
tagDictionary.Add(tagEntry.Id, tagEntry);
}
// Add the tag to the collection
mediaEntry.Tags.Add(tagEntry);
return mediaEntry;
},
splitOn: "Id") // This default and could be omitted
.Distinct()
.ToList();

EF Core. How to extract (left) joined table as collection?

I need help for extract joined table as a collection in context linq-syntax expression.
public class Computer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Component
{
public int Id { get; set; }
public string Name { get; set; }
public int ComputerId { get; set; } //FK
public Computer Computer { get; set; } //HasOne(Computer).WithMany().HasFK(ComputerId)
}
Computer no have backrefs to Components
Need to select computers with related components via Linq-syntax.
(from computer in db.Computers
join component in db.Components on computer.Id equals component.ComputerId into components //| JOINED
from component in components.DefaultIfEmpty() //| TABLE
select new ComputerFullData
{
Computer = computer,
Components = components // <-- collection
})
........other code
This linq does not work and generate NullRefException
But, if Computer have ICollection< Component > backref property, than i would could extract collection of components via call Include(x => x.Components) which in fact is revert-JOIN like in my example. In this case, I could have problematic access to the collection of components.
How can I achieve the same through Linq?
This works beautifully for me in EF Core:
public class Computer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Component
{
public int Id { get; set; }
public string Name { get; set; }
public int ComputerId { get; set; }
public Computer Computer { get; set; }
}
public class Db : DbContext
{
public static readonly ILoggerFactory MyLoggerFactory
= LoggerFactory.Create(builder => builder.AddConsole());
public DbSet<Component> Components { get; set; }
public DbSet<Computer> Computers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseLoggerFactory(MyLoggerFactory)
.UseSqlServer(#"Server=(localdb)\mssqllocaldb; Database=demo;Integrated Security=True");
}
}
class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.EnsureCreated();
db.Components.Add(
new Component
{
Computer = new Computer
{
Name = "Fred"
},
Name = "Fred component"
});
db.SaveChanges();
}
using (var db = new Db())
{
var computersWithComponents =
(from c in db.Computers
select new
{
Computer = c,
Components = (from cp in db.Components
where cp.ComputerId == c.Id
select cp).ToList()
}).ToList();
}
}
}
This is a lot like what you might write in SQL. I was pleased to find that referencing the context (db) in a sub-query like this is acceptable to the EF query provider, and the generated SQL is similar to what I might write by hand:
SELECT [c].[Id], [c].[Name], [c0].[Id], [c0].[ComputerId], [c0].[Name]
FROM [Computers] AS [c]
LEFT JOIN [Components] AS [c0] ON [c].[Id] = [c0].[ComputerId]
ORDER BY [c].[Id], [c0].[Id]
Out of interest—and I know this is not a direct answer to your question—why did you reject having the navigation property for Components on your model? I often find with EF that it's helpful to just do what works rather than try excessively hard to get exactly the model I would prefer. (If I have very specific requirements for the shape of the objects I need, I might have two class hierarchies - an EF database model which is idiomatic to EF, and my preferred model, with methods to translate between the two.) Anyway, in this case it's not too hard to get what you want, so that would likely be overkill here.

How can I get the count of a list in an Entity Framework model without including/loading the entire collection?

I have a model in Entity Framework Core that goes something like this:
public class Anime
{
public int EpisodeCount { get { return Episodes.Count() } }
public virtual ICollection<Episode> Episodes { get; set; }
}
I'm having the issue of EpisodeCount being 0. The solution currently is to run a .Include(x => x.Episodes) within my EF query, but that loads the entire collection of episodes where it's not needed. This also increases my HTTP request time, from 100ms to 700ms which is just not good.
I'm not willing to sacrifice time for simple details, so is there a solution where I can have EF only query the COUNT of the episodes, without loading the entire collection in?
I was suggested to do this
var animeList = context.Anime.ToPagedList(1, 20);
animeList.ForEach(x => x.EpisodeCount = x.Episodes.Count());
return Json(animeList);
but this also returns 0 in EpisodeCount, so it's not a feasible solution.
You need to project the desired data into a special class (a.k.a. ViewModel, DTO etc.). Unfortunately (or not?), in order to avoid N + 1 queries the projection must not only include the count, but all other fields as well.
For instance:
Model:
public class Anime
{
public int Id { get; set; }
public string Name { get; set; }
// other properties...
public virtual ICollection<Episode> Episodes { get; set; }
}
ViewModel / DTO:
public class AnimeInfo
{
public int Id { get; set; }
public string Name { get; set; }
// other properties...
public int EpisodeCount { get; set; }
}
Then the following code:
var animeList = db.Anime.Select(a => new AnimeInfo
{
Id = a.Id,
Name = a.Name,
EpisodeCount = a.Episodes.Count()
})
.ToList();
produces the following single SQL query:
SELECT [a].[Id], [a].[Name], (
SELECT COUNT(*)
FROM [Episode] AS [e]
WHERE [a].[Id] = [e].[AnimeId]
) AS [EpisodeCount]
FROM [Anime] AS [a]

Entity framework relationships

I have these three entities:
public class Dog
{
public int DogId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Checked { get; set; }
public string DogImage { get; set; }
public virtual ICollection<Result> Results { get; set; }
}
public class Event
{
public int EventId { get; set; }
public string EventName { get; set; }
public string EventLocation { get; set; }
public string EventType { get; set; }
public string EventDate { get; set; }
public virtual ICollection<Result> Results { get; set; }
}
public class Result
{
public int ResultId { get; set; }
public int Track { get; set; }
public int Obedience { get; set; }
public int Protection { get; set; }
[ForeignKey("Dog")]
public int DogId { get; set; }
public virtual Dog Dog { get; set; }
[ForeignKey("Event")]
public int EventId { get; set; }
public virtual Event Event { get; set; }
}
I´ve been getting help from here before in order to set it up like this.
Entity Framework errors when trying to create many-to-many relationship
So the way it is now I guess the result is the "glue" that ties these classes together containing foreign keys to the two other tables.
What I have been trying to achieve for days now is to:
Create an event.
Add dogs to the event.
Add results to the dogs participating in the choosenEvent.
Lets say I create an event like this:
[HttpPost]
public ActionResult CreateEvent(Event newEvent)
{
newEvent.EventDate = newEvent.EventDate.ToString();
_ef.AddEvent(newEvent);
return View();
}
Now I guess the next step would be to add a list of dogs to this event and in order to do that I need to somehow use my result-class since that's the "glue"-class. Please let me know if I'm even on the right track here.
It is not really a good idea to do many to many relationships like how you've done. See here
In order to get a proper many to many relationship, mapped in the proper way in the database, that doesn't have pitfalls, I would try it this way:
public class Dog {}
public class Event {}
public class Result {}
// This is a linking table between Dog and Results
public class DogResult
{
public int Id {get;set;}
public int DogId {get;set;}
public int ResultId {get;set;}
}
// This is a linking table between Events and Results
public class EventResult
{
public int Id {get;set;}
public int EventId {get;set;}
public int ResultId {get;set;}
}
When you now write your query you can do this:
using (var context = new DbContext())
{
var dogs = context.Dogs();
var dogResults = context.DogResults();
var results = context.Results();
var dogsAndResults = dogs.Join(
dogResults,
d => d.Id,
r => r.DogId,
(dog, dogResult) => new { dog, dogResult })
.Join(
results,
a => a.dogResult.ResultId,
r => r.Id,
(anon, result) => new { anon.dog, result });
}
It is a bit nasty looking, but it will give you back a list of anonymous objects containing a Dog and its related Result. But obviously it would be better to do this in a stored proc:
using (var context = new DbContext())
{
var results = context.Database.ExecuteStoreQuery<SomeResultDto>("SELECT * .... JOIN ... ");
}
This is cleaner, because you are using SQL.
This is a more complex way of dealing with it. But far more performant, especially if you understand fully how entity framework executes LINQ.
Obviously if you want to create these links:
using (var context = new DbContext())
{
context.Dogs.AddRange(dogs); // dogs being a list of dog entities
context.Results.AddRange(results); // events being a list of results entities
context.DogResults.AddRange(dogResults); // a list of the links
}
It is completely up to you how you create these links. To turn this into a sproc as well, you want to create some custom User Defined Table Types and use them as a Table Value Parameter.
var dogResults = dogs.SelectMany( d => results.Select ( r => new DogResult { DogId = d.Id, ResultId = r.Id } ) );
That is a beast of a LINQ query and basically it gets every dog and links it to every result. Run it in LinqPad and Dump the values.
I've only done this using the fluent method (when I was learning I found you can do everything in fluent, but not with annotations, so I've not looked into them), the following creates a many to many between my Unit entity and my UnitService entity:
modelBuilder.Entity<Unit>()
.HasMany<UnitService>(u => u.Services)
.WithMany(us => us.Units);
This code is in the protected override void OnModelCreating(DbModelBuilder modelBuilder) method.
In your case Event is Unit and Dog is UnitService.
Oh ooops, you don't need that at all, your 'join' table is your results table, in my case I don't care about the join table so its all hidden.
Maybe something like:
modelBuilder.Entity<Result>()
.HasMany<Event>(e => e.Results);
modelBuilder.Entity<Result>()
.HasMany<Dog>(d => d.Results);

Data access using Entity Framework in ASP.NET MVC

I have a table with these columns(type) as described below.
TABLE
------------------------------------------------------------------
Dir(str) | Twnshp(int) | Rng(int) | Section(int) | Xcell(int) | Ycell(int)
------------------------------------------------------------------
I am trying to do this query using EF.
SELECT Xcell,Ycell FROM [CIR].[dbo].[TRS2Cell] where Twnshp = 1 and Rng = 4 and Section =31
After some study, I created a DAL Context and class as below.
PlotXYContext.cs
public class PlotXYContext :DbContext
{
public DbSet<PlotXY> XYCells { get; set; }
}
PlotXY.cs
[Table("TRS2Cell")]
public class PlotXY
{
public string Dir { get; set; }
[Key]
public int Twnshp { get; set; }
public int Rng { get; set; }
public int Section { get; set; }
public int Xcell { get; set; }
public int Ycell { get; set; }
}
Here is the code in my controller where I pass the three parameters.
PlotXYContext plotXYContext = new PlotXYContext();
var query = from TRS2Cell in plotXYContext.XYCells
where TRS2Cell.Twnshp == 1
&& TRS2Cell.Rng == 4
&& TRS2Cell.Section == 31
select TRS2Cell.Xcell;
I need help with EF as I am new to it and also is this the right query?
If so how do I retrieve the Xcell and Ycell values from the query.
Also the table has no unique column, no nulls, nothing needs to be updated here. All I want is to do a select.
Normally your not gonna want to do any data access code in your controller. You want to keep those separated.
Also when I first started using EF i got hung up on DB Context as well when I started with MVC. If you added your Ado.Net Entity Data Model correctly the db context should be automatically created for you. If you look at "YourEntity".cs file under "Entity".edmx => "Entity".Context.tt it will look something like
public partial class VuittonEntities : DbContext
{
public VuittonEntities()
: base("name=VuittonEntities")
{
}
To help you out with EF I'm gonna post all my code for a query.
So your Model class in the models folder will look like.
public class RoleGridViewModel
{
public int UserID { get; set; }
public string UserFirst { get; set; }
public string UserLast { get; set; }
public string UserRole { get; set; }
public string UserRoleDesc { get; set; }
}
This is your Data Access layer function: Here I'm creating a list of my model class because I'm gonna populate it in a gridview later on.
public List<RoleGridViewModel> GridRoles()
{
using (VuittonEntities db = new VuittonEntities())
{
return (from users in db.User
join roles in db.UserRole on users.RoleID equals roles.RoleID
select new RoleGridViewModel
{
UserID = users.UserID,
UserFirst = users.FirstName,
UserLast = users.LastName,
UserRole = roles.Role,
UserRoleDesc = roles.Role_Desc
}).ToList();
}
}
Here in your Controller you can call it like this. Normally you would call a businezz layer from your controller I'm going straight to the Data layer to show you how its done. Here var roles holds your query. I'm using Json result here but this can also be done in an action result
public JsonResult RolesGrid()
{
var roles = new UserDAL().GridRoles();
return Json(roles, JsonRequestBehavior.AllowGet);
}
If you just want to select a single item you have to use .First() at the end of the query like this...
public string currentRole(UserViewModel uvm)
{
using (VuittonEntities db = new VuittonEntities())
{
return (from us in db.User
join usRole in db.UserRole on us.RoleID equals usRole.RoleID
where (us.RoleID == uvm.RoleID) && (us.UserID == uvm.UserID)
select usRole.Role).First();
}
}
I found that I wasn't using the similar datatype as in my table to declare the class for it.Thant is the only issue I came across that resolved it and hence the error.
Thank you for all the replies.

Categories

Resources