I have the following code
IDocumentSession documentSession = store.OpenSession();
var schools = documentSession.Query<School>().Where(x => x.SchoolName == "My Primary School").ToList();
foreach (var school in schools)
{
Console.WriteLine(school);
}
schools = documentSession.Query<School>().Where(x => x.Id == "109940").ToList();
foreach (var school in schools)
{
Console.WriteLine(school.SchoolName); // returns My Primary School
}
When the first query runs, no records are returned, although I have a matching record in the database. however the second query returns a the record I am expecting. I am running the db as embedded and I havent set up any indexes myself
Update: so if I customise the query to wait for non stale results it works fine, do I need to create a permanent index?
This is how RavenDB behaves. If there is no index to satisfy your query, RavenDB will create an index dynamically upon the query, and return with a stale results.
This is why the second time you get the result as you expected. This is one of the key designs of RavenDB, let the indexes do the work on the background and allow stale results rather blocking the query until the index is not stale.
In production the temp index will be promoted to auto index, once RavenDB will detect that the index has a regular use. Instead of relaying on auto indexes, you can also create a static index at some point.
Related
I have a table with a column with a unique constraint. The user will edit the data in this table in the form of a DataTable bound to a DataGrid. Once they are done modifying the data, the save function must apply their changes back to the database. For application architecture reasons, each change to the database must be performed as a create, update, or delete action one row at a time. I can validate the DataTable to be sure that the end result does not violate the constraint, but it seems impossible to verify that it will never be violated in between. Even if I do not allow duplicate values inside the DataGrid itself, the user could put a placeholder value in while making the modifications that would violate the constraint, and the DataTable class only seems to store the original and final versions of each row, not the intermediate versions.
How can I avoid any unique constraint violations while making these changes to the data?
The easiest way is to update all of the constrained values in a SQL statement:
UPDATE [Foo] Set [SomeUniqueValue] = CASE [ID] WHEN 1 THEN 'A' WHEN 2 THEN 'B' WHEN 3 THEN 'C' END WHERE [ID] IN (1,2,3)
This will not violate the unique if you were swapping the values around, as long as the uniqueness is preserved by the time the statement is finished.
If you must do it one row at a time, you would likely have to brute force run through your list of updates, catching errors when it failed, and in a loop keep retrying the ones that errored, until all updates were completed, or no more updates could be made:
bool didSomething;
List<Row> rowsThatNeedToTryAgain = new List<Row>();
do {
didSomething = false;
rowsThatNeedToTryAgain.Clear();
foreach (Row row in rowsToUpdate) {
try {
UpdateTheRowInTheDatabase(row);
didSomething = true;
} catch {
rowsThatNeedToTryAgain.Add(row);
}
}
rowsToUpdate.Clear();
rowsToUpdate.AddRange(rowsThatNeedToTryAgain);
} while (didSomething && rowsThatNeedToTryAgain.Count > 0);
if (rowsThatNeedToTryAgain.Count > 0)
throw new Exception("sorry, couldn't do 'em all");
A more elegant solution might do this from code without hitting the database, but that would assume your process is the only one updates at that time.
I'm trying to ensure that users can't access or modify rows in a table that belong to other users. After doings some research, this is the approach I've come up with (sample code)
public virtual ActionResult Edit(int id)
{
var myEntity = myEntityService.GetEntity(id);
if (myEntity == null)
return HttpNotFound();
if (myEntity.UserId != User.Identity.GetUserId())
return HttpNotFound();
MyEntityFormModel editMyEntity = Mapper.Map<MyEntity, MyEntityFormModel>(myEntity);
return View(editMyEntity);
}
}
And a similar approach for other CRUD operations: retrieve the entity by its Id, then checking to make sure that the UserId property of the entity (retrieved from AspNet.Identity.GetUserId() and stored during creation) matches the users UserId before allowing any CRUD operations to occur.
Is that secure enough and is it an efficient way to to keep people from accessing each others' data, or is there another security check I should implement?
I haven't worked with EF for the specifics of what's possible in that framework, but this is what I would do in nHibernate.
For GetEntity, do the get by both Id and UserId in the query parameters.
It saves on sql server load, bandwidth and network usage as you wont be returning any unnecessary data if the entity is not valid for the signed in user.
Insert: wont be an issue, as you will need to set the entity.UserId to the signed in User's Id anyway.
Update: This one is where you may need to manually verify if the entity.UserId matches the signed in user's Id. But check if EF has a way of doing "update entity where Id = X and UserId = Y" - it will make this far more secure and easier to maintain in the long run.
Delete: same as GetEntity - do a delete by both the Id and the UserId as query parameters. So if the record isn't for the current user then nothing will be deleted.
I guess the general approach would be almost be like treating the entities as if they have a composite key on Id and UserId.
So the MyEntityDbContext would look something like this
interface IMyEntityDbContext {
Entity GetEntity(int id, int userId);
int Insert(Entity entity);
void Delete(int id, int userId);
void Update(Entity entity);
//or if possible in EF
void Update(Entity entity, int userId);
}
It cuts down the amount of validation logic you will need to write to only in the Update statement. It also makes it absolutely impossible to Get or Delete records for a different user.
Is it possible to use one index as the base for another index?
public class BlogPostsOrderedByCreated : AbstractIndexCreationTask<BlogPost, BlogPost> {
/* Ordering Blogs after creation-date */
}
// TODO: Implement another index that uses above index
//public class BlogSelection :
Is this even possible or do I have to create a totally new index that duplicates the other index's code? If not possible, then why?
No, you cannot do that because it's very complex to implement this and the value in exchange in minimal.
It's complex to implement this because of:
- You should be notifying in the second index when there is any changed in the first index.
- How do you implement transactions?
And there is probably more. The dependably between the indexes will raise some really complex issue to solve, so you better have just two indexes.
If this not make sense in your scenario you may have been doing something the wrong way. For example, you don't have to create any BlogPostsOrderedByCreated and BlogSelection indexes, use just a linq query that will do both:
session.Query<Post>()
.Where(x => x.Name == "Blog selection")
.OrderBy(x => x.CreatedDate)
.ToList();
My general understanding is this would be tricky from the client side -- at the end of the day those index definitions are translated to linq to objects and sent to the server. When said linq is interperted it only has access to the System namespace and a few raven specific things.
What could work in a scenario like this is to define the index in a bundle on the server. Then you can do a lot more interesting things. Not entirely sure it would work but I think you'd have a fighting chance.
In order to use my Fluent NHibernate mappings on SQL Azure, I need to have a clustered index on every table. The default heap tables that Fluent NHibernate creates for many-to-many joins obviously don't do this as they don't have primary keys.
I want to be able to tell one side of the relationship to create a clustered index for its join table, but I'm not sure how. Here's what my mappings look like:
public class UserMap : ClassMap<User>{
public UserMap()
{
Table("Users");
Id(x => x.UserId).GeneratedBy.Identity().Column("UserId");
Map(x => x.UserName).Unique().Not.Nullable().Length(DataConstants.UserNameLength).Column("UserName");
Map(x => x.Email).Unique().Not.Nullable().Length(DataConstants.EmailAddressLength).Column("Email");
Map(x => x.Password).Not.Nullable().Length(DataConstants.PasswordHashLength).Column("Password");
HasMany(x => x.Clicks).Cascade.AllDeleteOrphan();
HasManyToMany(x => x.Roles).Cascade.SaveUpdate().Table("UsersInRole").ParentKeyColumn("UserId").
ChildKeyColumn("RoleId");
}
}
Please let me know if you need any more information!
I don't know if Fluent supports it directly (if not, just include the xml), but you can do it with Auxiliary Database Objects
<nhibernate-mapping>
<database-object>
<create>create clustered index ix on UsersInRole(UserId, RoleId)</create>
<drop>drop index UsersInRole.ix</drop>
</database-object>
</nhibernate-mapping>
I struggled with the same problem as the topic starter (as I'm combining Fluent NHibernate and Sql Azure as well) but the given answer didn't satify. This is because it is not dynamic by convention. Of course the HBM file could be dynamically created and added to the configuration afterwards with configuration.AddXmlFile("AddClusteredIndexesToManyToManyTables.hbm.xml"); but I just don't like the HBM files and I can't imagine there is a better way.
After several hours I found another solution which is dynamic (and readable!) and does not deal with hbm xml files. The solution is as follows:
Assumption #1: I will create a composite primary key for each junction table that results in a clustered index in SQL Server.
After the configuration has been build (thus the mappings are parsed), (Fluent) NHibernate gives us the oppertunity to look into the actual mappings with configuration.ClassMappings and configuration.CollectionMappings. The latter is used because we are interested in the many-to-many mappings.
foreach (var collectionMapping in configuration.CollectionMappings
// Filter on many-to-many
.Where(x => !x.IsOneToMany)) {
// Build the columns (in a hacky way...)
const string columnFormat = "{0}_id";
var leftColumn = new Column(string.Format(
columnFormat,
collectionMapping.Owner.MappedClass.Name));
var rightColumn = new Column(string.Format(
columnFormat,
collectionMapping.GenericArguments[0].Name));
// Fetch the actual table of the many-to-many collection
var manyToManyTable = collectionMapping.CollectionTable;
// Shorten the name just like NHibernate does
var shortTableName = (manyToManyTable.Name.Length <= 8)
? manyToManyTable.Name
: manyToManyTable.Name.Substring(0, 8);
// Create the primary key and add the columns
var primaryKey = new PrimaryKey {
Name = string.Format("PK_{0}", shortTableName),
};
primaryKey.AddColumn(leftColumn);
primaryKey.AddColumn(rightColumn);
// Set the primary key to the junction table
manyToManyTable.PrimaryKey = primaryKey;
}
And yes, the logic to get the left and right hand columnsAfter that the columns are a bit hacky but it works and you are free to adjust and edit my solution (^_-). The problem is that the collection mapping is fairly empty/unfilled.
Good luck with coding and creating conventions!
Any set of column(s) can be a clustered index... there is no requirement that I know of which forces you to use a PK constraint in order to build a clustered index.
More over I do not understand how a client could REQUIRE a clustered index. It might make them as a default but that's different than require. This is often reported as a "best practice" for SQL Server, but to the client, there's no real distinction between a secondary b-tree index on a column and the clustered index which orders the table's record. How would the client be able to distinguish the underlying storage of the data? One stores the data ordered by the cluster key, the other doesn't.
Maybe fluent-nhibernate performs better, or claims to - but it will "work" without any indexes.
But I'm not an expert in either so YMMV.
Great solution M.Mimpen.
When need map interfaces, put the ChildKeyColumn with interface name.
Ex:
HasManyToMany(x => x.Acessos).("IRole_id");
The class Acesso implements IRole interface. If you donĀ“t inform child key name, the column created will be "Acesso_id", but when create the key will try "IRole_id".
I am using Fluent-NHibernate (with automapping) to generate my tables but would like to choose a different clustered index than the ID field which is used by default. How can you create clustered indexes with Fluent NHibernate on a field other than the default Primary Key field?
The primary reasoning behind this is simple. I am using Guids for my primary key fields. By default, NHibernate creates clustered indexes on the primary key fields. Since Guids are usually not sequential, clustering on the primary key field causes a performance issue.
As we all know, appending records at the end of a table is a much cheaper operation than inserting records within the table. Also, the records in the table are physically stored in the order of the items in the clustered index. Since Guids are somewhat "random" and are not sequential, new Guids may be generated that are less than the value of other Id Guids already in the table--resulting in table inserts rather than appends.
To minimize this, I have a column called CreatedOn which is of type DateTime. I need for the table to be clustered on this CreatedOn column so that all new records are appended rather than inserted.
Any ideas for how to accomplish this are welcome!!!
Note: I realize that I could use Sequential Guids but prefer not to go down that path for security reasons.
Note: I still do not have an answer for this post but I have a few ideas I am pondering at the moment.
Using NHibernate without Fluent, I think it may be possible to created clustered indexes directly in NHibernate. I don't yet know enough about NHibernate to know how to do this. I am just pretty (as in almost absolutely) sure it can be done.
Fluent-NHibernate used to include a way to set attributes (e.g. like a clustered index) on a SQL object before the recent rewrite. Now that option appears to have gone away. I will probably post a question somewhere to see if that option is still available. If so, I could probably use that to set the clustered index.
Fluent-NHibernate provides the ability to expose a configuration for manual editing once it has been fluently built. I have not tried this functionality but expect it may offer the level of granularity that is needed to set clustered indexes.
Worst case scenario, I can write a SQL script to change the clustered indexes on all my tables once they are generated. However, I have a couple of questions regarding this approach. A. Since I am using automatic schema generation, will NHibernate "undo" my clustered index changes the next time it evaluates the configuration? 2. Will NHibernate error if it detects the clustered index has been changed? I need to test this but have not done so just yet. I really hate this solution though. I am testing my DB against SQLServer2008 and MySQL. Part of the beauty of NHibernate is that it is database agnostic. Once we introduce scripts, all bets are off.
There is an interface that is used in fluent conventions called IPropertyInstance Classes which inherit from this interface have an Index property which allows an Index to be created on the field. The problem is that there is no flag or other option to allow the index to be created as clustered. The simplest solution would be to add a property to this method to allow for clustered indexes to be created. I think I may suggest this to the Fluent-NHibernate developers.
This is an old post, but I hope could help someone else. This come from my experience on MS SQL Server. I believe different platforms require different solutions, but this should be a good starting point.
NHibernate doesn't set the CLUSTERED index on the primary key. It's the SQL Server default behavior. As there can be only one CLUSTERED per table, we need first to avoid the CLUSTERED creation on the primary key.
The only way I found to accomplish this is to create a custom Dialect, overriding the propery PrimaryKeyString. NHibernate's default comes from Dialect.cs:
public virtual string PrimaryKeyString
{
get { return "primary key"; }
}
For SQL Server
public override string PrimaryKeyString
{
get { return "primary key nonclustered"; }
}
This will force SQL Server to create a NONCLUSTERED primary key.
Now you can add your own CLUSTERED index on your favorite column through the tag in the XML mapping file.
<database-object>
<create>
create clustered index IX_CustomClusteredIndexName on TableName (ColumnName ASC)
</create>
<drop>
drop index IX_CustomClusteredIndexName ON TableName
</drop>
</database-object>
I can't answer that specifically, but I'll give you some database info since I'm here.
You'll need to tell NHibernate to create the primary key at a non-clustered index. There can only be only clustered index per table, so you need to create the table as a heap, and then put a clustered index on it.
As you said yourself, another option is to switch to the guid.comb ID generation strategy where PK uids are based on a part which is a Guid and a part which ensures that the generated IDs are sequential.
Check out more info in a post by Jeffrey Palermo here.
But you mention that do not want to do that for security reasons - why is that?
Just like #abx78 told, this is an old post, but I would like to share my knowledgde on a solution for this problem as well. I built the solution for idea 3 "Fluent NHibernate exposes mappings":
After the configuration has been build (thus the mappings are parsed), Fluent NHibernate gives us the oppertunity to look into the actual mappings with configuration.ClassMappings and configuration.CollectionMappings. The latter is used in the example below to set a composite primary key resulting in a clustered index in Sql Server (as #abx78 points out):
foreach (var collectionMapping in configuration.CollectionMappings) {
// Fetch the columns (in this example: build the columns in a hacky way)
const string columnFormat = "{0}_id";
var leftColumn = new Column(string.Format(
columnFormat,
collectionMapping.Owner.MappedClass.Name));
var rightColumn = new Column(string.Format(
columnFormat,
collectionMapping.GenericArguments[0].Name));
// Fetch the actual table of the many-to-many collection
var manyToManyTable = collectionMapping.CollectionTable;
// Shorten the name just like NHibernate does
var shortTableName = (manyToManyTable.Name.Length <= 8)
? manyToManyTable.Name
: manyToManyTable.Name.Substring(0, 8);
// Create the primary key and add the columns
// >> This part could be changed to a UniqueKey or to an Index
var primaryKey = new PrimaryKey {
Name = string.Format("PK_{0}", shortTableName),
};
primaryKey.AddColumn(leftColumn);
primaryKey.AddColumn(rightColumn);
// Set the primary key to the junction table
manyToManyTable.PrimaryKey = primaryKey;
// <<
}
Source: Fluent NHibernate: How to create a clustered index on a Many-to-Many Join Table?