I have the following console application which creates a ShardManagerDB and creates one database for each company on the main database.
I can see on azure the databases created on the server however they are not on the elastic pool.
Question:
1. Is this doable with the current API?
2. If not, what are other recommended approaches?
using System.Data.SqlClient;
using mynm.Data;
using System.Linq;
using mynm.Models.GlobalAdmin;
namespace mynm.DbManagementTool
{
class Program
{
static void Main(string[] args)
{
SetupSSM();
}
//This will create the Shard Management DB if it doesnt exist
private static void SetupSSM()
{
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder
{
UserID = SettingsHelper.AzureUsernamedb,
Password = SettingsHelper.AzurePasswordDb,
ApplicationName = SettingsHelper.AzureApplicationName,
DataSource = SettingsHelper.AzureSqlServer
};
DbUtils.CreateDatabaseIfNotExists(connStrBldr.ConnectionString, SettingsHelper.Azureshardmapmgrdb);
Sharding sharding = new Sharding(SettingsHelper.AzureSqlServer, SettingsHelper.Azureshardmapmgrdb, connStrBldr.ConnectionString);
CreateShardPerCompany(sharding);
}
private static void CreateShardPerCompany(Sharding sharding)
{
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder
{
UserID = SettingsHelper.AzureUsernamedb,
Password = SettingsHelper.AzurePasswordDb,
ApplicationName = SettingsHelper.AzureApplicationName,
DataSource = SettingsHelper.AzureSqlServer
};
UnitOfWork unitOfWork = new UnitOfWork();
ConfigurationDBDataContext context = new ConfigurationDBDataContext();
context.Empresas.Add(new Empresa()
{
Id = 1,
Nombre = "company name 1",
NIT = "873278423",
NombreRepresentanteLegal = "myself",
TelefonoRepresentanteLegal = "32894823",
NombreContacto = "myself",
TelefonoContacto = "32423"
});
context.SaveChanges();
var listofEmpresas = unitOfWork.EmpresaRepository.Get().ToList();
foreach(Empresa empresa in listofEmpresas)
{
DbUtils.CreateDatabaseIfNotExists(connStrBldr.ConnectionString, empresa.NIT);
sharding.RegisterNewShard(SettingsHelper.AzureSqlServer, empresa.NIT, connStrBldr.ConnectionString, empresa.Id);
}
}
}
}
the sharding.css
internal class Sharding
{
public ShardMapManager ShardMapManager { get; private set; }
public ListShardMap<int> ShardMap { get; private set; }
// Bootstrap Elastic Scale by creating a new shard map manager and a shard map on
// the shard map manager database if necessary.
public Sharding(string smmserver, string smmdatabase, string smmconnstr)
{
// Connection string with administrative credentials for the root database
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder(smmconnstr);
connStrBldr.DataSource = smmserver;
connStrBldr.InitialCatalog = smmdatabase;
// Deploy shard map manager.
ShardMapManager smm;
if (!ShardMapManagerFactory.TryGetSqlShardMapManager(connStrBldr.ConnectionString, ShardMapManagerLoadPolicy.Lazy, out smm))
{
this.ShardMapManager = ShardMapManagerFactory.CreateSqlShardMapManager(connStrBldr.ConnectionString);
}
else
{
this.ShardMapManager = smm;
}
ListShardMap<int> sm;
if (!ShardMapManager.TryGetListShardMap<int>("ElasticScaleWithEF", out sm))
{
this.ShardMap = ShardMapManager.CreateListShardMap<int>("ElasticScaleWithEF");
}
else
{
this.ShardMap = sm;
}
}
// Enter a new shard - i.e. an empty database - to the shard map, allocate a first tenant to it
// and kick off EF intialization of the database to deploy schema
// public void RegisterNewShard(string server, string database, string user, string pwd, string appname, int key)
public void RegisterNewShard(string server, string database, string connstr, int key)
{
Shard shard;
ShardLocation shardLocation = new ShardLocation(server, database);
if (!this.ShardMap.TryGetShard(shardLocation, out shard))
{
shard = this.ShardMap.CreateShard(shardLocation);
}
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder(connstr);
connStrBldr.DataSource = server;
connStrBldr.InitialCatalog = database;
// Go into a DbContext to trigger migrations and schema deployment for the new shard.
// This requires an un-opened connection.
using (var db = new ElasticScaleContext<int>(connStrBldr.ConnectionString))
{
// Run a query to engage EF migrations
(from b in db.Terceros
select b).Count();
}
// Register the mapping of the tenant to the shard in the shard map.
// After this step, DDR on the shard map can be used
PointMapping<int> mapping;
if (!this.ShardMap.TryGetMappingForKey(key, out mapping))
{
this.ShardMap.CreatePointMapping(key, shard);
}
}
}
In the code implementing database creation: DbUtils.CreateDatabaseIfNotExists() -- you are probably using a T-SQL CREATE DATABASE command to Create an Azure database on a logical server. Currently CREATE DATABASE doesn't support specifying the Pool -- however an update to Azure DB is expected within the next month that will extend the functionality of CREATE DATABASE and ALTER DATABASE to specify the Pool name as well.
In the meantime, you can make a REST API call from the CreateDatabaseIfNotExists() routine to add the database to the pool once it is created, using the Update SQL Database command: https://msdn.microsoft.com/en-us/library/azure/mt163677.aspx.
However, making a rest call from inside your c# can be complex, and is discussed here: http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client . It may be simpler to wait for the upcoming support in the CREATE DATABASE command, which would require a very small modification within your CreateDatabaseIfNotExists() routine.
Related
Method One
_AcDb.Line oLine = new _AcDb.Line(ptStart, ptEnd);
AddToModelSpace("PLOT", oLine);
Where AddToModelSpace is:
public static void AddToModelSpace(string strLayer, _AcDb.Entity oEntity)
{
_AcAp.Document acDoc = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcDb.Database acCurDb = acDoc.Database;
_AcEd.Editor ed = acDoc.Editor;
using (_AcDb.BlockTable bt = acCurDb.BlockTableId.GetObject(_AcDb.OpenMode.ForRead) as _AcDb.BlockTable)
using (_AcDb.BlockTableRecord ms = bt[_AcDb.BlockTableRecord.ModelSpace].GetObject(_AcDb.OpenMode.ForWrite) as _AcDb.BlockTableRecord)
ms.AppendEntity(oEntity);
oEntity.Layer = strLayer;
oEntity.Dispose();
}
Method Two
// Get the current document and database
_AcAp.Document docActive = _AcAp.Application.DocumentManager.MdiActiveDocument;
_AcDb.Database docDB = docActive.Database;
// Start a transaction
using (_AcDb.Transaction acTrans = docDB.TransactionManager.StartTransaction())
{
// Open the Block table for read
_AcDb.BlockTable acBlkTbl;
acBlkTbl = acTrans.GetObject(docDB.BlockTableId,
_AcDb.OpenMode.ForRead) as _AcDb.BlockTable;
// Open the Block table record Model space for write
_AcDb.BlockTableRecord acBlkTblRec;
acBlkTblRec = acTrans.GetObject(acBlkTbl[_AcDb.BlockTableRecord.ModelSpace],
_AcDb.OpenMode.ForWrite) as _AcDb.BlockTableRecord;
// Create line
using (_AcDb.Line acLine = new _AcDb.Line(ptStart, ptEnd))
{
// Add the new object to the block table record and the transaction
acBlkTblRec.AppendEntity(acLine);
acTrans.AddNewlyCreatedDBObject(acLine, true);
}
// Save the new object to the database
acTrans.Commit();
}
I have used AddToModelSpace in my project so I hope it is fine!
Method Two is the way Autodesk recommends in the developer's documentation (you can read this section).
In Method One, you use the ObjectId.GetObject() method to open the BlockTable and the model space 'BlockTableRecord'. This method uses the top transaction to open object which means that there's an active transaction you should use to add the newly created entity. You can get it with Database.TransactionManager.TopTransaction. If you don't want to use a transaction at all, you have to use the "for advanced use only" ObjectId.Open() method.
A Method Three should be using some extension methods to be called from within a transaction. Here's a simplified (non error checking) extract of the ones I use.
static class ExtensionMethods
{
public static T GetObject<T>(
this ObjectId id,
OpenMode mode = OpenMode.ForRead,
bool openErased = false,
bool forceOpenOnLockedLayer = false)
where T : DBObject
{
return (T)id.GetObject(mode, openErased, forceOpenOnLockedLayer);
}
public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead)
{
return SymbolUtilityServices.GetBlockModelSpaceId(db).GetObject<BlockTableRecord>(mode);
}
public static ObjectId Add (this BlockTableRecord owner, Entity entity)
{
var tr = owner.Database.TransactionManager.TopTransaction;
var id = owner.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
return id;
}
}
Using example:
using (var tr = db.TransactionManager.StartTransaction())
{
var line = new Line(startPt, endPt) { Layer = layerName };
db.GetModelSpace(OpenMode.ForWrite).Add(line);
tr.Commit();
}
I'm trying to configure the write-through and read-through properties of apache Ignite with an Oracle database. I searched in many places like the Ignite oficial documentation, also in the ignite examples on GitHub,
but there isn't much information or examples coded in C# that is the lenguaje in which I'm developing my app.
What I want is to retrieve from a persistent store (in this case an Oracle database), an specific data in the cache (Ignite) that is not already loaded. In a similar way, I need all my changes on the cache to be reflected on the database.
I tied to create and spring.xml with the configuration of the database (ip, port, username, pass, database), but I can't make it work if that is the way it should be done.
Thanks in advance and sorry for my english.
1) Implement ICacheStore interface (or inherit CacheStoreAdapter helper class)
public class OracleStore : CacheStoreAdapter
{
public override object Load(object key)
{
using (var con = new OracleConnection
{
ConnectionString = "User Id=<username>;Password=<password>;Data Source=<datasource>"
})
{
con.Open();
var cmd = con.CreateCommand();
cmd.CommandText = "SELECT * FROM MyTable WHERE ID=#id";
cmd.Parameters.Add("#id", OracleType.Int32);
cmd.Parameters["#id"].Value = key;
using (var reader = cmd.ExecuteReader())
{
// Read data, return as object
}
}
}
public override void Write(object key, object val)
{
oracleDb.UpdateRow(key, val);
}
public override void Delete(object key)
{
oracleDb.DeleteRow(key);
}
}
2) Implement store factory:
public class OracleStoreFactory : IFactory<OracleStore>
{
public OracleStore CreateInstance()
{
return new OracleStore();
}
}
3) Configure cache to use store:
using (var ignite = Ignition.Start())
{
var cacheCfg = new CacheConfiguration
{
ReadThrough = true,
WriteThrough = true,
KeepBinaryInStore = false, // Depends on your case
CacheStoreFactory = new OracleStoreFactory()
};
var cache = ignite.CreateCache<int, MyClass>(cacheCfg);
cache.Get(1); // OracleStore.Load is called.
}
Documentation for Ignite.NET (in C#): https://apacheignite-net.readme.io/docs/persistent-store
C# examples are available in a full download package: https://ignite.apache.org/download.cgi#binaries (click apache-ignite-fabric-1.9.0-bin.zip, download, unzip, open platforms\dotnet\examples\Apache.Ignite.Examples.sln)
Blog post explaining cache store implementation in C#:
https://ptupitsyn.github.io/Entity-Framework-Cache-Store/
Working with Oracle DB in .NET: Connecting to Oracle Database through C#?
In my Azure ASP.NET MVC website I want to display how many clients are connected to a Redis sessions state provider and how long they are active. I use the aspnet-redis-providers lib on the Azure Github.
In Redis, it creates a {[app_name]_[sessionkey}_Internal key with a SessionTimeout key with the value of the configured session timeout. The EXPIRE for that key is set to that time and when you check to TTL for the key you see the session access.
How can I use the session state provider library to access this information? If that is not possible, is there any other library I can use to query this info safely, without interfering with the session state provider?
Here is what I was able to do. I created my own session object collection and grabbed all keys (which I am putting in DB 1) then I loop through all keys and grab the TTL.
using StackExchange.Redis;
using StackExchange.Redis.Extensions.Newtonsoft;
using StackExchange.Redis.Extensions.Core;
using System.Linq;
private static Lazy<ConnectionMultiplexer> conn = new Lazy<ConnectionMultiplexer>(
() => ConnectionMultiplexer.Connect(ConfigurationManager.AppSettings["RedisServerMaster"]
+ "," + ConfigurationManager.AppSettings["RedisServerSlave"]
+ "," + ConfigurationManager.AppSettings["RedisOptions"])
public class SessionObjects
{
public string SessionId { get; set; }
public TimeSpan? TTL { get; set; }
}
List<SessionObjects> lso = new List<SessionObjects>();
var serializer = new NewtonsoftSerializer();
StackExchangeRedisCacheClient cacheClient;
cacheClient = new StackExchangeRedisCacheClient(rConn, serializer, 1);
IEnumerable<string> keys = cacheClient.SearchKeys("*");
var db = rConn.GetDatabase(1);
foreach (var s in keys)
{
SessionObjects so = new SessionObjects();
so.SessionId = s;
so.TTL = db.KeyTimeToLive(s);
lso.Add(so);
}
I'm trying to use Entity Framework with MySQL. I've just installed MySQL Installer for Windows which includes MySQL Connector for .NET.
I can't find any documentation on how to actually use this thing, so I've been cobbling pieces together from various tutorials. It appears I need the references for MySql.Data and MySql.Data.Entity for EF6 (screenshot).
Then I stole this source code from some random project which is the only reference I could find to MySqlConnectionFactory:
class ImgSigDbConfig : DbConfiguration
{
public ImgSigDbConfig()
{
base.AddDependencyResolver(new MySqlDependencyResolver());
base.SetProviderFactory(MySqlProviderInvariantName.ProviderName, new MySqlClientFactory());
base.SetProviderServices(MySqlProviderInvariantName.ProviderName, new MySqlProviderServices());
base.SetDefaultConnectionFactory(new MySqlConnectionFactory());
base.SetMigrationSqlGenerator(MySqlProviderInvariantName.ProviderName, () => new MySqlMigrationSqlGenerator());
base.SetProviderFactoryResolver(new MySqlProviderFactoryResolver());
base.SetManifestTokenResolver(new MySqlManifestTokenResolver());
}
}
I couldn't figure out how to set my database credentials anywher ein there, but apparently you can pass them in when constructing the DbContext:
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<ImgSimContext>());
var conn = new MySqlConnectionStringBuilder
{
Server = "localhost",
UserID = "root",
Password = "pass",
Database = "ImgSig"
};
using (var db = new ImgSimContext(conn.ToString()))
{
SqlConnection.ClearAllPools();
db.Database.Initialize(force: true);
Now I've created a very simple POCO:
internal class ImgSig
{
[Key, Required]
public int Id { get; set; }
[Index, StringLength(16), Required]
public byte[] Hash { get; set; }
}
But when it hits db.Database.Initialize an exception is thrown:
BLOB/TEXT column 'Hash' used in key specification without a key length
Which makes perfect sense because in MySQL you have to set the key length for any blob/text fields (although it would be nice to see what SQL it's trying to run).
So my questions are:
How can I change the data type of Hash to a fixed length binary(16)?
How can I set the key length (preferably using attributes)?
I've built my project and named the db connection "VMaxEntities"
The connection string exists in web.config.
I have another connection string "Development_VMaxEntities"
Whenever I call the db, I use the code using (VMaxEntities db = new VMaxEntities())
This calls:
public partial class VMaxEntities : DbContext
{
public VMaxEntities()
: base("name=VMaxEntities")
{
}
What I want to do is connect to a development database instead of the live one, IF the current URI contains localhost.
So - thanks to cgotberg's answer below, here's what I used to get it working: (note, I add the password here instead of in web.config)
public VMaxEntities()
: base("name=Secure_VMaxEntities")
{
if (System.Web.HttpContext.Current.Request.Url.Host == "localhost")
{
var connectionString = this.Database.Connection.ConnectionString + ";password=***********";
this.Database.Connection.ConnectionString = connectionString.Replace("catalog=VMax", "catalog=DEV_VMax");
}
else
{
this.Database.Connection.ConnectionString += ";password=************";
}
}
I've done something like this in the past
using(var db = new VMaxEntities())
{
var connectionString = context.Database.Connection.ConnectionString;
var datasource = context.Database.Connection.DataSource;
var databaseServer = "yourDevDatabaseServerName"
db.Database.Connection.ConnectionString = connectionString.Replace(datasource, databaseServer);
}