db4o - how do I get a distinct list of classes contained in a .db4o DB file? - db4o

Say I open a .db4o file. What would be the Java code (psuedo) that would generate a list of unique Java CLASSES/TYPES contained in the database?
I am sure I could write the code to do it, but I am afraid it could be quite slow. Is there a good way to do this without querying for every object in the database? Could I use some sort of index?
I'd rather not rely on stored metadata about the database, I'd rather rely on the true information about what objects are actually stored within.

You can use something like (C# but it can be easily converted to Java :)
const string DatabaseFileName = "c:\\temp\\Learning.odb";
static void Main(string[] args)
{
using (var db = Db4oEmbedded.OpenFile(DatabaseFileName))
{
var classes = db.Ext().StoredClasses();
foreach (var #class in classes)
{
Console.WriteLine();
Console.WriteLine(#class.GetName());
foreach (var field in #class.GetStoredFields())
{
Console.WriteLine("\t{1} {0}", field.GetName(), field.GetStoredType().GetName());
}
}
}
}
Note that you have more interesting methods in ExtObjectContainer interface.
Hope this helps.

Related

More graceful way to do bulk update in EF6

This is in a WPF application.
Currently I have the following code which works, but is slow:
public void IgnoreOffers(ICollection<Offer> offers)
{
using (var context = new DbContext())
{
foreach (Offer o in offers)
{
var offer = context.Offers.Find(o.Id);
offer.Ignore = true;
}
context.SaveChanges();
}
}
I know all the offers exist already in the database
Is there a more performant way of doing this?
Disclaimer: I'm the owner of the project Entity Framework Plus
This library allows you to perform BatchUpdate
context.Offers.Where(o => offers.Contains(o.Id)
.BatchUpdate(o => new Offer() { Ignore = true });
If you have too many offers, you might need to do it in multiple batch as the number of parameter in SQL is limited.
Everything will be done on the database side, so no offer will be needed to be loaded on the application side.
This method will offer you the best performance.
Disclaimer: I'm the owner of the project Entity Framework Extensions
This library is not free but offers the BulkUpdate features.
// BulkUpdate
context.BulkUpdate(offers);
// If you want to only update the `Ignore` property
context.BulkUpdate(offers, o => o.ColumnInputExpression = x => new { x.Ignore });

How to delete tenant completely?

I'm developing a multitenant application and would like to have to option to remove a tenant. This however seems to be less trivial than one would assume.
My goal is to delete all references to the tenant everywhere in the database. I understand that Tenant is Soft-Delete, but I since I don't want my database to fill up with old meaningless data I've tried disabling the soft-delete filter.
Here is some code that I've tried:
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
{
await TenantRepository.DeleteAsync(x => x.Id == tenantId);
}
This did not work. The tenant is marked as "IsDeleted" but not removed.
Then I figured that maybe it has something to do with UnitOfWork so I made sure no UnitOfWork was active and then manually controlled it:
using (var unitOfWork = _unitOfWorkManager.Begin())
{
// the codeblock above went here
unitOfWork.Complete();
}
This did not work, same result. And this is just the AbpTenant table. I'm also trying to delete from all other tables. For example AbpSettings and AbpLanguages. It's very unclear to me how to do that at all - the "managers" doesn't contain any Delete functions.
I tried creating IRepository for these entities but it does not work. The error reads
The type Abo.Configuration.Setting cannot be used as a type parameter TEntity in the generic type or method IRepository. There is no implicit reference conversion from Abp.Configuration.Setting to Abo.Domain.Entities.IEntity.
That leaves me with the option to use the DataContext directly:
using (EntityFramework.MyDbContext db = new EntityFramework.MyDbContext())
{
List<PermissionSetting> perms = await db.Permissions.Where(x => x.TenantId == tenantId).ToListAsync();
for (int i=0; i<perms.Count(); i++)
{
db.Permissions.Remove(perms[i]);
}
// I also tried deleting them in bulk at first
// ((DbSet<PermissionSetting>)db.Permissions).RemoveRange(db.Permissions.Where(x => x.TenantId == tenantId));
await db.SaveChangesAsync();
}
I tried that with and without UnitOfWork.
But it simply does not get deleted from the database. I'm getting no errors or Exceptions.
Why does it not get deleted? How can I delete it? Surely it must be possible?
since I don't want my database to fill up with old meaningless data I've tried disabling the soft-delete filter.
From the question on Disable SoftDelete for AbpUserRole:
protected override void CancelDeletionForSoftDelete(EntityEntry entry)
{
if (IsSoftDeleteFilterEnabled)
{
base.CancelDeletionForSoftDelete(entry);
}
}
The type Abo.Configuration.Setting cannot be used as a type parameter TEntity in the generic type or method IRepository. There is no implicit reference conversion from Abp.Configuration.Setting to Abo.Domain.Entities.IEntity.
Inject IRepository<Setting, long> instead of IRepository<Setting>.
That leaves me with the option to use the DataContext directly
...
But it simply does not get deleted from the database. I'm getting no errors or Exceptions.
From the documentation on Data Filters:
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
using (var db = new ...)
{
// ...
}
}
That said, there is no way to easily delete related tenant data completely. Consider writing SQL.

Using SQLDependency or SqlCacheDependency with Repository pattern

I am building an ASP.NET MVC application using the Repository pattern. A typical method in my Repository is as follows
public IList<T> Select<T>(string cacheKey, string Sql, object filter) where T: new()
{
IList<T> items = MemoryCache.Default.Get(cacheKey) as IList<T>;
if (items == null || !items.Any())
{
items = Connection.Select<T>(Sql, filter);
MemoryCache.Default.Add(cacheKey, items, DateTime.Now.AddMinutes(120));
}
return items;
}
and it is being used as follows
IEnumerable<OSADCOL> osadcols = Repository.Select<OSADCOL>("OSADCOLS__TblId=" + Id, "TBLid = #id", new { id = Id });
In the above mentioned example OSADCOL is a model in my app and represents a Table with the same name in my Database. The Connection.Select function is ORMlite function. I don't use Entity Framework for performance issues. My problem is the following. I am currently cashing the result set for future relevance but I am doing it in a hard coded way. I am caching the result set for 2 hours. It is obvious that a proper implementation would be to discard my cashed data when the OSADCOL's table data changes. It seems that I have to use SQLDependency or SQLCacheDependency. The questions are the following:
How am I going to use SQLDependency or SQLCacheDependency with this Repository?
What is the actual difference between these 2?
It is mentioned in some forums that SQLDependency creates memory leaks? Is that true? And If yes is there an alternative approach?
Any Ideas?

EF migration Seeding with large dataset

I am using EF Code First Migration with ASP.NET MVC5 project. I want to seed the database with about 5000 records. The examples on the asp.net web site and blogs all have the seed function within the configuration method.
internal sealed class Configuration : DbMigrationsConfiguration<foo.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations\ApplicationDbContext";
}
protected override void Seed(foo.ApplicationDbContext context)
{
SeedProducts(context);
SeedFoods(context);
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
What are some options to seed large data sets when using EF Migration?
Currently The configuration file is over 5000 lines long and is different to manage.
I would prefer to store them in another database or excel spreadsheet and then import them in using the seed function. I am not sure how to go about importing data from external sources within the Seed method.
I also tried to break up the data set into several files but when I try to call the function
SeedProducts(context);
SeedFoods(context);
outside of the Configuration Class I get a build error: "The name does not exists in the current context". (I am not sure what this means?
You can store the sql file in the base directory and use it. You can create multiple sql files. I used following code to execute all sql files stored on base directory.
protected override void Seed(CRM.Data.DbContexts.CRMContext context)
{
var sqlfiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory+"\\initialdata", "*.sql");
sqlfiles.ToList().ForEach(x=> context.Database.ExecuteSqlCommand(File.ReadAllText(x)));
}
Why we need to have a seed data for 5000 records. If you prefer this way it will take lot of manual work also. where as, its not required here.
Instantly you can Create Scripts and execute that into you db. via Code as well as Manually.
Db Scripts can be taken for entire db as well as each table, Store Procedure wise also. So, that you will get records particularly.
Follow the steps from this link OR MSDN
Note: After Creating the Database Script. You can read the file from Seed Function and Execute the query from function itself. Or Manually you can go and execute when ever you need it.
I ended up using a CSV (comma delimited file) and storing it as a domain resource. Then reading the CSV file and adding database records:
I am able to Seed the database using EF Migration Seed method and a CSV file as defined as follows in the Migration.cs file. Note: the CSV file in the project in Visual Studio are set to the Build Action to Embedded Resource.
public void SeedData(WebApp.Models.ApplicationDbContext Context)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string resourceName = "WebApp.SeedData.Name.csv";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
CsvReader csvReader = new CsvReader(reader);
csvReader.Configuration.WillThrowOnMissingField = false;
var records = csvReader.GetRecords<Products>().ToArray();
foreach (Product record in records)
{
Context.Products.AddOrUpdate(record);
}
}
}
Context.SaveChanges();
}

How to set up a multitenancy application using ASP.NET MVC?

A multitenancy application is an app that is shared by multiple organizations (medical practices, law offices..) and each organization, in turn, has it's own users. They all log on a centralized environment.
To be identified within the application, the organization must be expressed in the URL. There are two major URL forms for that. Subdomains and folders:
[tenancy_name].appname.com/projects/view/123
www.appname.com/[tenancy_name]/projects/view/123
At first I tried the second because this solution does not involve dealing with DNSs. But then the problem: Everytime the developer needs to express an url (#Html.Action or #Url.Action) it has to explicitly pass the [tenancy_name]. This adds an unwanted overhead to the development. A possible workaround would be to implement custom versions of these HTML helpers that automatically take into account the tenancy name. I'm considering this option but looking for something more straitghtforward. I also realized ASP.NET MVC automatically passes route values for outgoing URLs but only when the controller and action are the same as the current. It would be nice if route values were always passed.
To implement the first option, the subdomain one, I think, I would need some third party DNS manager. I heard of DynDNS and took a look at it but I thought it unclear how they work just looking at their site. Would I need to trigger a web-service to tell them to create another subdomain everytime a new tenancy is created? Do they support wildcards in the DNS? Do they work on Windows Azure or shared hostings?
I'm here looking for directions. Which way should I go?
look this project on codeplex, the "baseRoute" maybe can help you.
http://mvccoderouting.codeplex.com/
Regards.
Following made View resolution trivial in our app:
How to use:
For views that you need to overload for a particular tenant - treat them same way as custom display modes:
Following will work:
Index.cshtml
Index.cust2.mobile.cshtml
or
Partials/CustomerAgreement.cust1.cshtml
Partials/CustomerAgreement.cust2.cshtml
as far as I remember display/editor templates also work same way
Known issues:
1. You have to create Layouts for all combinations of primary+secondary (for whatever MVC-reason)
2. Regardless of what resharper is saying about its support of display modes - it does not support "." as part of the display mode name (here's an issue to track progress http://youtrack.jetbrains.com/issue/RSRP-422413)
//put in application start --------
DisplayModeProvider.Instance.Modes.Clear();
foreach (var displayMode in GetDisplayModes())
{
DisplayModeProvider.Instance.Modes.Add(displayMode);
}
private IEnumerable<IDisplayMode> GetDisplayModes()
{
return new CompoundDisplayModeBuilder()
.AddPrimaryFilter(_ => dependencyResolver.GetService(typeof(IResolveCustomerFromUrl)).GetName(),
"cust1",
"cust2")
.AddSecondaryFilter(ctx => ctx.Request.Browser.IsMobileDevice, "mobile")
.BuildDisplayModes();
}
//end of application start part
//and the mode builder implementation:
public class CompoundDisplayModeBuilder
{
private readonly IList<DefaultDisplayMode> _primaryDisplayModes = new List<DefaultDisplayMode>();
private readonly IList<DefaultDisplayMode> _secondaryDisplayModes = new List<DefaultDisplayMode>();
//NOTE: this is just a helper method to make it easier to specify multiple tenants in 1 line in global asax
//You can as well remove it and add all tenants one by one, especially if resolution delegates are different
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, string> contextEval, params string[] valuesAsSuffixes)
{
foreach (var suffix in valuesAsSuffixes)
{
var val = suffix;
AddPrimaryFilter(ctx => string.Equals(contextEval(ctx), val, StringComparison.InvariantCultureIgnoreCase), val);
}
return this;
}
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_primaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public CompoundDisplayModeBuilder AddSecondaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_secondaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public IEnumerable<IDisplayMode> BuildDisplayModes()
{
foreach (var primaryMode in _primaryDisplayModes)
{
var primaryCondition = primaryMode.ContextCondition;
foreach (var secondaryMode in _secondaryDisplayModes)
{
var secondaryCondition = secondaryMode.ContextCondition;
yield return new DefaultDisplayMode(primaryMode.DisplayModeId + "." + secondaryMode.DisplayModeId){
ContextCondition = ctx => primaryCondition(ctx) && secondaryCondition(ctx)
};
}
}
foreach (var primaryFilter in _primaryDisplayModes)
{
yield return primaryFilter;
}
foreach (var secondaryFilter in _secondaryDisplayModes)
{
yield return secondaryFilter;
}
yield return new DefaultDisplayMode();
}
}

Resources