How can I define an Order parameter through a belongsTo table in Castle ActiveRecord - castle-activerecord

Hi guys take a look in the following entity:
Produto = Products (in english)
[ActiveRecord("produtos", Lazy=true)]
public partial class Produto : ActiveRecordBase<Produto>, IObjetoEntidade<Produto>
{
[PrimaryKey(PrimaryKeyType.Native, "prod_id")]
public virtual int Id { get; set; }
[StringLengthValidator(0, 100)]
[Property("prod_descricao")]
public virtual string Descricao { get; set; }
BelongsTo("prod_grupop_id", Lazy=FetchWhen.OnInvoke)]
public virtual GrupoProduto GrupoProduto { get; set; }
Look that GrupoProduto is a BelongsTo, when I try to put it in a order parameter, castle throw an exception that can't find the property. I make the follwing code:
DetachedCriteria dc = DetachedCriteria.For<Produto>();
dc.SetFetchMode("GrupoProduto", NHibernate.FetchMode.Join);
return SlicedFindAll(primeiroRegistro, quantidade, dc, new[] { Order.Asc("GrupoProduto.Nome")});
or
DetachedCriteria dc = DetachedCriteria.For<Produto>();
dc.SetFetchMode("GrupoProduto", NHibernate.FetchMode.Join);
dc.AddOrder(Order.Asc("GrupoProduto.Nome"));
return SlicedFindAll(primeiroRegistro, quantidade, dc);
But in both cases I receive the error, can anyone help me?

Use an alias:
DetachedCriteria dc = ...
dc.CreateAlias("GrupoProduto", "gp");
...
...Order.Asc("gp.Nome")

Related

Advanced NoSQL Query (RavenDB)

I'm trying to run a query that gets all of my references, but it isn't working.
What I have right now is
from UserGroups
where Id="ActionGroup"
select Accomplishments.ID, Accomplishments.Accomplish
But I need only the Accomplishments.Accomplish that belong in my other collection ActivityAccomplishments and these are nested in another object.
To be exact, I'm trying to figure out how to query the UserGroups collection and only look at the one with id="ActionGroup". After that I need all of the Accomplishments.Accomplish strings within the UserGroup list to be filtered out if they don't match a id in ActivityAccomplishment.
Basically, in the UserGroup I'm looking at it's List Accomplishments needs to filter out all strings within the Acc class that don't match an Id in ActivityAccomplishments. Can someone please help me.
Here are the classes I'm using.
public class UserGroups
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Acc> Accomplishments { get; set; }
}
public class Acc
{
public string Id { get; set; }
public List<string> Accomplish { get; set; }
}
public class ActivityAccomplishments
{
public string Id { get; set; }
}
try this:
from UserGroups
where Id = "ActionGroup" AND Accomplishments[].Accomplish != "theIdYouDontWant"
select Accomplishments[].Accomplish as AccomplishStringsList
(not necessary to add the 'as AccomplishStringsList' - it is just a name for the results)

Entity Framework Core - Very slow performance

I have the following entities (I'll show the properties I'm working with because I don't want to make it larger than needed):
PROPERTY: Where a property can be child of another one and has a 1-1 relationship with GeoLocation and can have multiple Multimedia and Operation
public partial class Property
{
public Property()
{
InverseParent = new HashSet<Property>();
Multimedia = new HashSet<Multimedia>();
Operation = new HashSet<Operation>();
}
public long Id { get; set; }
public string GeneratedTitle { get; set; }
public string Url { get; set; }
public DateTime? DatePublished { get; set; }
public byte StatusCode { get; set; }
public byte Domain { get; set; }
public long? ParentId { get; set; }
public virtual Property Parent { get; set; }
public virtual GeoLocation GeoLocation { get; set; }
public virtual ICollection<Property> InverseParent { get; set; }
public virtual ICollection<Multimedia> Multimedia { get; set; }
public virtual ICollection<Operation> Operation { get; set; }
}
GEOLOCATION: As mentioned, it has a 1-1 relationship with Property
public partial class GeoLocation
{
public int Id { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public long? PropertyId { get; set; }
public virtual Property Property { get; set; }
}
MULTIMEDIA: it can hold multiple Images, with different sizes, for a single Property. The detail here is that Order specifies the order of the images to be shown in the client application, but it doesn't start always with 1. There're some cases where a Property has Multimedia files that starts with 3 or x.
public partial class Multimedia
{
public long Id { get; set; }
public long? Order { get; set; }
public string Resize360x266 { get; set; }
public long? PropertyId { get; set; }
public virtual Property Property { get; set; }
}
OPERATIONS: defines all the operations a Property can have, using OperationType to name this operation. (rent, sell, etc.)
public partial class Operation
{
public Operation()
{
Price = new HashSet<Price>();
}
public long Id { get; set; }
public long? OperationTypeId { get; set; }
public long? PropertyId { get; set; }
public virtual OperationType OperationType { get; set; }
public virtual Property Property { get; set; }
public virtual ICollection<Price> Price { get; set; }
}
public partial class OperationType
{
public OperationType()
{
Operation = new HashSet<Operation>();
}
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Operation> Operation { get; set; }
}
PRICE: defines the price for each operation and the currency type. (i.e.: A property can have the rent option - Operation - for X amount in USD currency, but another price registered for the same Operation in case of use another currency type )
public partial class Price
{
public long Id { get; set; }
public float? Amount { get; set; }
public string CurrencyCode { get; set; }
public long? OperationId { get; set; }
public virtual Operation Operation { get; set; }
}
Said that, I want to get all the records (actually are about 40K-50K), but only for a few properties. As I mentioned before, the Multimedia table can have a lot of records for each Property, but I only need the first one with the smaller Order value and sorted by DatePublished. After that, I need to convert the result into MapMarker object, which is as follows:
public class MapMarker : EstateBase
{
public long Price { get; set; }
public int Category { get; set; }
public List<Tuple<string, string, string>> Prices { get; set; }
}
In order to achieve this, I made the following:
public async Task<IEnumerable<MapMarker>> GetGeolocatedPropertiesAsync(int quantity)
{
var properties = await GetAllProperties().AsNoTracking()
.Include(g => g.GeoLocation)
.Include(m => m.Multimedia)
.Include(p => p.Operation).ThenInclude(o => o.Price)
.Include(p => p.Operation).ThenInclude(o => o.OperationType)
.Where(p => p.GeoLocation != null
&& !string.IsNullOrEmpty(p.GeoLocation.Address)
&& p.GeoLocation.Longitude != null
&& p.GeoLocation.Latitude != null
&& p.StatusCode == (byte)StatusCode.Online
&& p.Operation.Count > 0)
.OrderByDescending(p => p.ModificationDate)
.Take(quantity)
.Select(p => new {
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
p.Operation,
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
})
.ToListAsync();
var mapMarkers = new List<MapMarker>();
try
{
foreach (var property in properties)
{
var mapMarker = new MapMarker();
mapMarker.Id = property.Id.ToString();
mapMarker.Url = property.Url;
mapMarker.Title = property.GeneratedTitle ?? string.Empty;
mapMarker.Address = property.Address ?? string.Empty;
mapMarker.Latitude = property.Latitude.ToString() ?? string.Empty;
mapMarker.Longitude = property.Longitude.ToString() ?? string.Empty;
mapMarker.Domain = ((Domain)Enum.ToObject(typeof(Domain), property.Domain)).ToString();
mapMarker.Image = property.Resize360x266 ?? string.Empty;
mapMarker.Prices = new List<Tuple<string, string, string>>();
foreach (var operation in property.Operation)
{
foreach (var price in operation.Price)
{
var singlePrice = new Tuple<string, string, string>(operation.OperationType.Name, price.CurrencyCode, price.Amount.ToString());
mapMarker.Prices.Add(singlePrice);
}
}
mapMarkers.Add(mapMarker);
}
}
catch (Exception ex)
{
throw;
}
return mapMarkers;
}
but the results take more than 14 minutes and this method could be called multiple times in a minute. I want to optimize it to return the results in the less time possible. I alreay tried removing ToListAsync(), but in the foreach loop it takes a lot of time too, and that makes all the sense.
So, what do you think I can do here?
Thanks in advance.
UPDATE:
Here is GetAllProperties() method, I forgot to include this one.
private IQueryable<Property> GetAllProperties()
{
return _dbContext.Property.AsQueryable();
}
And the SQL query that Entity Framework is making against SQL Server:
SELECT [p].[Id], [p].[Url], [p].[GeneratedTitle], [g].[Address], [g].[Latitude], [g].[Longitude], [p].[Domain], (
SELECT TOP(1) [m].[Resize360x266]
FROM [Multimedia] AS [m]
WHERE [p].[Id] = [m].[PropertyId]
ORDER BY [m].[Order]), [t].[Id], [t].[CreationDate], [t].[ModificationDate], [t].[OperationTypeId], [t].[PropertyId], [t].[Id0], [t].[CreationDate0], [t].[ModificationDate0], [t].[Name], [t].[Id1], [t].[Amount], [t].[CreationDate1], [t].[CurrencyCode], [t].[ModificationDate1], [t].[OperationId]
FROM [Property] AS [p]
LEFT JOIN [GeoLocation] AS [g] ON [p].[Id] = [g].[PropertyId]
LEFT JOIN (
SELECT [o].[Id], [o].[CreationDate], [o].[ModificationDate], [o].[OperationTypeId], [o].[PropertyId], [o0].[Id] AS [Id0], [o0].[CreationDate] AS [CreationDate0], [o0].[ModificationDate] AS [ModificationDate0], [o0].[Name], [p0].[Id] AS [Id1], [p0].[Amount], [p0].[CreationDate] AS [CreationDate1], [p0].[CurrencyCode], [p0].[ModificationDate] AS [ModificationDate1], [p0].[OperationId]
FROM [Operation] AS [o]
LEFT JOIN [OperationType] AS [o0] ON [o].[OperationTypeId] = [o0].[Id]
LEFT JOIN [Price] AS [p0] ON [o].[Id] = [p0].[OperationId]
) AS [t] ON [p].[Id] = [t].[PropertyId]
WHERE (((([g].[Id] IS NOT NULL AND ([g].[Address] IS NOT NULL AND (([g].[Address] <> N'') OR [g].[Address] IS NULL))) AND [g].[Longitude] IS NOT NULL) AND [g].[Latitude] IS NOT NULL) AND ([p].[StatusCode] = CAST(1 AS tinyint))) AND ((
SELECT COUNT(*)
FROM [Operation] AS [o1]
WHERE [p].[Id] = [o1].[PropertyId]) > 0)
ORDER BY [p].[ModificationDate] DESC, [p].[Id], [t].[Id], [t].[Id1]
UPDATE 2: As #Igor mentioned, this is the link of the Execution Plan Result:
https://www.brentozar.com/pastetheplan/?id=BJNz9KdQI
Ok, a few things that should help. #1. .Include() and .Select() should in general be treated mutually exclusive.
You are selecting:
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
p.Operation,
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
but then in your foreach loop accessing Price and OperationType entities off it.
Edit Updated the example for the collection of operation. (Whups)
Instead I would recommend:
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
Operations = p.Operation.Select( o => new
{
OperationTypeName = o.OperationType.Name,
o.Price.Amount,
o.Price.CurrencyCode
}).ToList(),
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
Then adjust your foreach logic to use the returned properties rather than a returned entity and related entity values.
Loading 40-50k records with something like that image field (MultiMedia) is potentially always going to be problematic. Why do you need to load all 50k in one go?
This looks like something that would put markers on a map. Solutions like this should consider applying a radius filter at the very least to get markers within a reasonable radius of a given center point on a map, or if loading a larger area (zoomed out map) calculating regions and filtering data by region or getting a count falling in that region and loading/rendering the locations in batches of 100 or so rather than potentially waiting for all locations to load. Something to consider.

Update 2 tables from Edit Action (ViewModel).

I want first to say sorry, because I'm new to this programming language, so forgive me if I say or do something wrong.
I created a project with 3 class libraries (1 of them contains the tables from sql server). I made a ViewModel based on the tutorial from: "http://tutlane.com/tutorial/aspnet-mvc/how-to-use-viewmodel-in-asp-net-mvc-with-example", and now I want to be able to update the data in those tables, but I don't know how. I tried to do something but it failed.
`namespace BOL2.ViewModel
{
public class NIRIO
{
[Key]
public int ID { get; set; }
public int Number { get; set; }
public Nullable Date { get; set; }
public int NirID { get; set; }
[DisplayName("TypeID")]
public int TipID { get; set; }
public int SupplierID { get; set; }
public int ProductID { get; set; }
public Nullable<System.DateTime> EntryDate { get; set; }
public Nullable<System.DateTime> ExitDate { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal Total { get; set; }
public BOL2.tbl_NIR tbnir;
public BOL2.tbl_I_O tblio { get; set; }`
This is my ViewModel. It contains data from those 2 tables (tbl_NIR, first 3, and the others from tbl_I_O. I saw something on my research that they had a repository class, but I don't now if I should do another class for the viewmodel or I sould use the 2 that I already have? Any help is greatly appreciated.
You could do something similar to this.
public void UpdateCar(CarViewModel viewModel)
{
using (DataContext context = new DataContext())
{
CarEntity dataModel = context.CarEntities.where(x => x.Id == viewModel.Id).First();
dataModel.Name = viewModel.Name;
dataModel.Type = viewModel.Type;
context.SaveChanges();
}
}
You need to create your model objects from your view model and set the values for the models.
I'm not quite sure what is the method you used and didn't work. Just in case you tried to update the tables through the view, then you cannot do that. Because you have join
inform IT explanation
I recommend you to create a stored procedure in your database. Then call the procedure trough the code. It's secure and fast. explained here

Custom DomainDataSource/Table view issue in RIA Services

The project I'm using is based on a standard "Silverlight Business Application".
I have a number of tables which can display fine, but I'm looking to display a composite table based on SQL joins from other tables. So, in my Model.Designer.cs I have a small dummy class:
public class JoinClass
{
[Key]
public string LanguagesName { get; set; }
public string VersionsName { get; set; }
public string StringsName { get; set; }
public string TranslatedStringsValue { get; set; }
}
Then, in my DomainService.cs I create my query:
public IQueryable<JoinClass> GetJoinClass()
{
IQueryable<JoinClass> query = from o in this.ObjectContext.TranslatedStrings
where o.LanguagesID == 10
select new JoinClass { LanguagesName = o.Languages.LanguagesName, VersionsName = o.Strings.Versions.VersionsName, StringsName = o.Strings.StringsName, TranslatedStringsValue = o.TranslatedStringsValue };
return query;
}
In the Silverlight app I access all this via a DomainDataSource:
<riaControls:DomainDataSource Name="joinClass" LoadSize="20" QueryName="GetJoinClass" AutoLoad="True">
<riaControls:DomainDataSource.DomainContext>
<ds:LanguageModelDomainContext />
</riaControls:DomainDataSource.DomainContext>
<riaControls:DomainDataSource.SortDescriptors>
<riaControls:SortDescriptor PropertyPath="LanguagesName"></riaControls:SortDescriptor>
</riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>
and display the results in a DataGrid:
<sdk:DataGrid IsReadOnly="True" Name="translatedStringsDataGrid" ItemsSource="{Binding Data, ElementName=joinClass}">
nothing rocket science - the issue is that I can only get one record to display in the DataGrid. The query, GetJoinClass(), seems to execute fine and return nearly 600 records, but only one ever appears in the DataGrid. All the other standard queries I use don't have this problem.
Any pointers gratefully received!
Answer -
The [Key]. All properties of the class should have the [Key] attribute, not just one, e.g.
public class JoinClass
{
[Key]
public string LanguagesName { get; set; }
[Key]
public string VersionsName { get; set; }
[Key]
public string StringsName { get; set; }
[Key]
public string TranslatedStringsValue { get; set; }
}
I don't know why.

EF Code First: Many-to-many and one-to-many

This is probably just because my knowledge with the EF Code First fluent API is lacking, but I'm stumped.
I want to model the following:
A Groups collection with Id and Name
A Users collection with Id and Name
Each user is assigned to exactly one primary group
Each user may have zero or many secondary groups
The table structure I'm going for would look like:
Groups
Id
Name
Users
Id
Name
PrimaryGroupId
SecondaryGroupAssignments
UserId
GroupId
I've been beating my head against a wall trying to model this with EF Code First, but I can't get it to accept both relationships between User and Group. Sorry for not posting any .NET code (I'm happy to), but it's probably all wrong anyway.
Is there a way to make EF model this? I'm assuming I have to do some sort of configuration with the Fluent API. Maybe a better question is: is there any good, definitive reference for the Fluent API?
Thanks!
Try this (untested):
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> PrimaryUsers { get; set; }
public virtual ICollection<User> SecondaryUsers { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int PrimaryGroupId { get; set; }
public virtual Group PrimaryGroup { get; set; }
public virtual ICollection<Group> SecondaryGroups { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasRequired(u => u.PrimaryGroup)
.WithMany(g => g.PrimaryUsers)
.HasForeignKey(u => u.PrimaryGroupId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany(u => u.SecondaryGroups)
.WithMany(g => g.SecondaryUsers)
.Map(m => m.MapLeftKey("UserId")
.MapRightKey("GroupId")
.ToTable("SecondaryGroupAssignments"));
}
}
Based on Ladislav's excellent answer, here's how to do it without using any mappings - just attributes applied to the Model classes themselves:
public class Group
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
[ForeignKey("PrimaryGroup")]
public int PrimaryGroupId { get; set; }
[Required]
public Group PrimaryGroup { get; set; }
[InverseProperty("Users")]
public ICollection<Group> SecondaryGroups { get; set; }
}
Notes
If you want, you can add the virtual keyword to the 2 ICollections and the Group. This allows lazy-loading. Performance-wise, I don't recommend it, but it is possible.
I included MaxLength attributes with an arbitrary (but safe) length of 300, because putting strings out in EF without a MaxLength gets you low-performance NVarChar(MAX) columns. Totally irrelevant to what's being asked but better to post good code.
I recommend against class names "User" and "Group" for your EF classes. They're going to complicate any SQL you attempt to run later, having to type [User] and [Group] to access them, and complicate using these classes in MVC Controllers where your class User will conflict with the Context property User that gives you access to the Asp.Net Identity library.

Resources