I'm done configuring the Fluent NHibernate application using multiple databases.
When I run the application, I see that the session is creating the same tables in all the databases.
I tried limiting the creation by using the following line of code in Mapping class
Schema("Monkey") <- in monkey ClassMap
Schema("Banana") <- in Banana ClassMap
The SQL Query Generated:
if exists (select * from dbo.sysobjects where id = object_id(N'Banana.[Banan
a]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Banana.[Banana]
if exists (select * from dbo.sysobjects where id = object_id(N'Monkey.[Monke
y]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Monkey.[Monkey]
create table Banana.[Banana] (
Id INT IDENTITY NOT NULL,
Color NVARCHAR(255) null,
primary key (Id)
)
At the above point the debugger caught an error saying:
The specified schema name "Banana" either does not exist or you do not
have permission to use it.
only add the the relevant tables to the sessionfactory for each database. I would seperate them per namespaces: "BananaDbMaps" and "MonkeyDbMaps"
foreach (var dataBase in dataBases)
{
var model = new PersistenceModel();
foreach (var type in Assembly.GetExecutingAssembly().GetExportedTypes())
{
if (!type.IsInterface && !type.IsAbstract && type.IsSubclassOf(typeof(IMappingProvider)) && type.Namespace.EndsWith(dataBase.Key + "DbMaps"))
{
model.Add(type);
}
}
config = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(dataBase.Value))
.Mappings(m => m.UsePersistenceModel(model))
.BuildConfiguration();
_allFactories.Add(dataBase.Key, config.BuildSessionFactory());
}
Related
public partial class UserProfile : System.Web.UI.Page
{
private static int _userId = 0 ;
protected void Page_Load(object sender, EventArgs e)
{
string FoB;
if (Session["user"] != null)
{
_userId = DataManager.GetUserId(Session["user"].ToString());
}
string connection = WebConfigurationManager.ConnectionStrings["Database"].ConnectionString;
SqlConnection conn = new SqlConnection(connection);
SqlCommand comm = new SqlCommand("SELECT * from dbo.Rating where UserID=_userId", conn);
SqlDataReader reader;
conn.Open();
reader = comm.ExecuteReader();
reader.Read();
FoB = reader["GenreID"].ToString();
if(FoB=="1" )
{
FB.Text = reader["RatingValue"].ToString();
};
}
while (reader.HasRows);
reader.Close();
conn.Close();
}
}
I have a table named rating. It has 4 columns
RatingId, UserID, GenreID,Rating value
I want to display rating value on a label based on the current user logged in and different rating value against different Genres. UserID and GenreID are foreign keys from table Genre and User.
Edit (comment)
CREATE TABLE [dbo].[Rating] (
[RatingID] INT IDENTITY (1, 1) NOT NULL,
[UserID] INT NULL, [GenreID] INT NOT NULL,
[RatingValue] INT NOT NULL,
PRIMARY KEY CLUSTERED ([RatingID] ASC),
CONSTRAINT [FK_Rating_Genre] FOREIGN KEY ([GenreID])
REFERENCES [dbo].[Genre] ([GenreID])
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [FK_Rating_User] FOREIGN KEY ([UserID])
REFERENCES [dbo].[User] ([UserID])
ON DELETE CASCADE ON UPDATE CASCADE );
I want to show 8 different rating values of 8 different genres by 1 single current user.
If I understand you correctly, you want to map your label to different columns, based on another, discriminating column:
using (var conn = new SqlConnection(connection))
using (var comm = new SqlCommand("SELECT * from dbo.Rating where UserID=#userId", conn))
{
comm.Parameters.AddWithValue("#userId", _userId);
conn.Open();
using (var reader = comm.ExecuteReader())
{
if (reader.HasRows && reader.Read())
{
FB.Text = reader["GenreID"].ToString() == "1"
? reader["RatingValue"].ToString();
: reader["SomeOtherColumn"].ToString();
}
}
}
If one or more of the columns to be mapped reside in another table other than Rating, you'll need to join to that table - we'll need to see your table structures to help you.
Edit, re displaying 8 Genres
I've assumed the User and Genre tables both have a column Name - join to these tables to look up the rating. The GROUP and MAX will eliminate any cases where the same user has more than one rating in the same Genre (switch the MAX to AVG or MIN if you need otherwise). Top 8 will restrict the genres. So adjust the Sql like so:
SELECT TOP 8 u.Name AS UserName, g.Name as GenreName, MAX(r.RatingValue) AS TopRating
FROM dbo.Rating r
INNER JOIN dbo.[User] u
ON r.UserId = u.UserID
INNER JOIN dbo.[Genre] g
ON r.GenreID = g.GenreID
WHERE UserID=#userId
GROUP BY u.Name, g.Name
ORDER BY g.Name;
Now, for user interface, you won't be able to display a table in a single label. The easiest would simply be to bind the result of the reader directly to a new GridView control on your WebForm
using (var reader = comm.ExecuteReader())
{
if (reader.HasRows)
{
gridView.DataSource = reader;
gridView.DataBind();
}
}
This will show a table with 3 columns matching the selected columns, and and up to 8 rows.
I want to fetch metadata information regarding a Database view.
I can find my view using the following code
DatabaseMetaData databaseMetaData = connection.getMetaData();
String tableName = "";
ResultSet rSet = databaseMetaData.getTables(null, databaseName, null, new String[] {TABLE, VIEW});
while (rSet.next()) {
tableName = rSet.getString(TABLE_NAME_COLUMN);
if (rSet.getString(TABLE_TYPE_COLUMN).equals(TABLE)) {
tableNames.add(tableName);
} else if (rSet.getString(TABLE_TYPE_COLUMN).equals(VIEW)) {
viewNames.add(tableName);
}
}
Now when i have the view name, i want to fetch the list of data columns in the VIEW.
I use the following code
ResultSet rSet = databaseMetaData.getColumns(null, databaseName, table, null);
while (rSet.next()) {
columnDefinitions.add(new ColumnDefinition(rSet.getString(COLUMN_NAME_COLUMN), getColumnClassName(rSet
.getInt(DATA_TYPE_COLUMN))));
}
the concern here being that this query only returns the list of base columns from which this VIEW was derived. Say if my base table had 3 columns and i created a view if that table with 2 extra columns deriving information from the 3 base columns.
I want to fetch all 5 columns name and information. Is this possible ?
Is there any other method available to do this ?
I need to filter some Entities by various fields using "normal" WHERE and IN clauses in a query over my database, but I do not know how to do that with EF.
This is the approach:
Database table
Licenses
-------------
license INT
number INT
name VARCHAR
...
desired SQL Query in EF
SELECT * FROM Licenses WHERE license = 1 AND number IN (1,2,3,45,99)
EF Code
using (DatabaseEntities db = new DatabaseEntities ())
{
return db.Licenses.Where(
i => i.license == mylicense
// another filter
).ToList();
}
I have tried with ANY and CONTAINS, but I do not know how to do that with EF.
How to do this query in EF?
int[] ids = new int[]{1,2,3,45,99};
using (DatabaseEntities db = new DatabaseEntities ())
{
return db.Licenses.Where(
i => i.license == mylicense
&& ids.Contains(i.number)
).ToList();
}
should work
I've got a MySQL database with typical schema for tagging items:
item (1->N) item_tag (N->1) tag
Each tag has a name and a count of how many items have that tag
ie:
item
(
item_id (UNIQUE KEY)
)
item_tag
(
item_id (NON-UNIQUE INDEXED),
tag_id (NON-UNIQUE INDEXED)
)
tag
(
tag_id (UNIQUE KEY)
name
count
)
I need to write a maintenance routine to batch re-tag one or more existing tags to a single new or existing other tag. I need to make sure that after the retag, no items have duplicate tags and I need to update the counts on each tag record to reflect the number of actual items using that tag.
Looking for suggestions on how to implement this efficiently...
if i understood you correctly then you could try something like this:
/* new tag/item table clustered PK optimised for group by tag_id
or tag_id = ? queries !! */
drop table if exists tag_item;
create table tag_item
(
tag_id smallint unsigned not null,
item_id int unsigned not null,
primary key (tag_id, item_id), -- clustered PK innodb only
key (item_id)
)
engine=innodb;
-- populate new table with distinct tag/items
insert ignore into tag_item
select tag_id, item_id from item_tag order by tag_id, item_id;
-- update counters
update tag inner join
(
select
tag_id,
count(*) as counter
from
tag_item
group by
tag_id
) c on tag.tag_id = c.tag_id
set
tag.counter = c.counter;
An index/constraint on the item_tag table can prevent duplicate tags; or create the table with a composite primary key using both item_id and tag_id.
As to the counts, drop the count column from the tag table and create a VIEW to get the results:
CREATE VIEW tag_counts AS SELECT tag_id, name, COUNT(*) AS count GROUP BY tag_id, name
Then your count is always up to date.
This is what I've got so far, which seems to work but I don't have enough data yet to know how well it performs. Comments welcome.
Some notes:
Had to add a unique id field to to the item_tags table get the duplicate tag cleanup working.
Added support for tag aliases so that there's a record of retagged tags.
I didn't mention this before but each item also has a published flag and only published items should affect the count field on tags.
The code uses C#, subsonic+linq + "coding horror", but is fairly self explanatory.
The code:
public static void Retag(string new_tag, List<string> old_tags)
{
// Check new tag name is valid
if (!Utils.IsValidTag(new_tag))
{
throw new RuleException("NewTag", string.Format("Invalid tag name - {0}", new_tag));
}
// Start a transaction
using (var scope = new SimpleTransactionScope(megDB.GetInstance().Provider))
{
// Get the new tag
var newTag = tag.SingleOrDefault(x => x.name == new_tag);
// If the new tag is an alias, remap to the alias instead
if (newTag != null && newTag.alias != null)
{
newTag = tag.SingleOrDefault(x => x.tag_id == newTag.alias.Value);
}
// Get the old tags
var oldTags = new List<tag>();
foreach (var old_tag in old_tags)
{
// Ignore same tag
if (string.Compare(old_tag, new_tag, true)==0)
continue;
var oldTag = tag.SingleOrDefault(x => x.name == old_tag);
if (oldTag != null)
oldTags.Add(oldTag);
}
// Redundant?
if (oldTags.Count == 0)
return;
// Simple rename?
if (oldTags.Count == 1 && newTag == null)
{
oldTags[0].name = new_tag;
oldTags[0].Save();
scope.Complete();
return;
}
// Create new tag?
if (newTag == null)
{
newTag = new tag();
newTag.name = new_tag;
newTag.Save();
}
// Build a comma separated list of old tag id's for use in sql 'IN' clause
var sql_old_tags = string.Join(",", (from t in oldTags select t.tag_id.ToString()).ToArray());
// Step 1 - Retag, allowing duplicates for now
var sql = #"
UPDATE item_tags
SET tag_id=#newtagid
WHERE tag_id IN (" + sql_old_tags + #");
";
// Step 2 - Delete the duplicates
sql += #"
DELETE t1
FROM item_tags t1, item_tags t2
WHERE t1.tag_id=t2.tag_id
AND t1.item_id=t2.item_id
AND t1.item_tag_id > t2.item_tag_id;
";
// Step 3 - Update the use count of the destination tag
sql += #"
UPDATE tags
SET tags.count=
(
SELECT COUNT(items.item_id)
FROM items
INNER JOIN item_tags ON item_tags.item_id = items.item_id
WHERE items.published=1 AND item_tags.tag_id=#newtagid
)
WHERE
tag_id=#newtagid;
";
// Step 4 - Zero the use counts of the old tags and alias the old tag to the new tag
sql += #"
UPDATE tags
SET tags.count=0,
alias=#newtagid
WHERE tag_id IN (" + sql_old_tags + #");
";
// Do it!
megDB.CodingHorror(sql, newTag.tag_id, newTag.tag_id, newTag.tag_id, newTag.tag_id).Execute();
scope.Complete();
}
I am trying to delete an object and cascade the delete to the child objects in a one-to-many association. I think that I have done everything correctly for this to work. However, when I run my test, NHibernate attempts to insert a null value into the foreign key column of the child table rather than deleting the items.
From my parent mapping (Carrier):
<set name="Drivers" access="field.camelcase-underscore">
<key column="CarrierId"/>
<one-to-many class="Vehicle"/>
</set>
From my child mapping (Vehicle):
<many-to-one name="Carrier" class="Carrier" column="CarrierId" not-null="true"/>
My test:
[Test]
public void Can_delete_a_carrier_and_associated_vehicles() {
object id;
var carrier = new Carrier { BusinessRef = 759540, Name = "Carrier1" };
var vehicle = new Vehicle { Carrier = carrier, BusinessRef = "FOOBAR", VehicleType = VehicleType.Trailer };
using (var txn = session.BeginTransaction()) {
id = session.Save(carrier);
session.Save(vehicle);
txn.Commit();
}
session.Clear();
using (var txn = session.BeginTransaction()) {
var fromDb = session.Get<Carrier>(id);
Assert.IsNotNull(fromDb);
Assert.AreEqual("FOOBAR", fromDb.Vehicles.First().BusinessRef);
session.Delete(fromDb);
txn.Commit();
}
}
The generated SQL:
INSERT INTO Carriers (...) VALUES (...); select last_insert_rowid();#p0 = 'WSH', #p1 = 759540, #p2 = False
INSERT INTO Vehicles (...) VALUES (...); select last_insert_rowid();#p0 = 2, #p1 = 'FOOBAR', #p2 = 4
SELECT carrier0_.Id, ... FROM Carriers carrier0_ WHERE carrier0_.Id=#p0;#p0 = 4
SELECT vehicles0_.CarrierId as CarrierId1_, ... FROM Vehicles vehicles0_ WHERE vehicles0_.CarrierId=#p0;#p0 = 4
UPDATE Vehicles SET CarrierId = null WHERE CarrierId = #p0;#p0 = 4
It's the line in bold that is causing the test to fail because I have a not null constraint on carrier (see vehicle mapping).
This is what I don't understand, if I have a not-null constraint, why does NHibernate try and insert null into the column.
So what do I need to do to ensure that deleting a carrier, deletes all vehicles?
Thanks,
Ben
After all this, the problem ended up being a typo in one of the other sets defined on the parent object. It was only through trying a few more specific tests that I found I was trying to cast a collection to the wrong type - doh!
So basically, if you use the mapping above then the deletes will cascade (providing you don't make silly typos :))