14
I Use This!
Activity Not Available

News

Posted about 14 years ago by Dmitri Maximov
Posted about 14 years ago by [email protected] (Alex Yakunin)
Posted about 14 years ago by [email protected] (Alex Yakunin)
Connection strings can be specified via provider and connectionString elements in App/Web.config files. Original approach with connection URLs is supported as well. Example: <domain connectionString="Data Source=localhost; Integrated ... [More] Security=True" name="test" provider="sqlserver" /> Notes: Index storages do not support connection strings so far, since we haven't invented a syntax for them yet. provider is the same string as protocol part in connection URL. Default schema is specified via defaultSchema element. Example: <domain connectionUrl="sqlserver://localhost/" defaultSchema="dbo" name="test"/> Default schema can be specified with both connection strings and connection URLs. See Xtensive.Storage.Tests\App.config for various examples. [Less]
Posted about 14 years ago by [email protected] (Alex Yakunin)
He everyone! I haven't been here for a while - that's because we worked really hard to bring a set of really important changes closer to release: DataObjects.Net v4.2 will be released on the next week; by the end of this week we're going to ... [More] publish one more release candidate. By now, we've implemented 174 issues after the last official release (August), so it's really a big step forward. Enough to increase the number by 0.2 ;) Unfortunately, I was unable to touch DisconnectedState so far, so it will be shown a bit later (v4.2.1).  But there are lots of other new and really attractive features: full-text search (example queries are here) working natively at MSSQL and PostgreSQL, O2O mapping (take a look at tests for it), support for SQL Server CE, unified API for registration and activation of persistent types and services (IoC framework we use now is much more flexible; there is no dependency on Common Service Locator, but container we use is replaceable), unified storage exceptions and lots of less important improvements - e.g. much faster object removal, better KeyGenerator model. I hope I'll be able to add chapters describing ASP.NET & ASP.NET MVC development with DataObjects.Net & add a bit more comprehensive samples until release. If this is done, I can fully honestly say DO4 is fully ready to be used in web applications. WPF readiness point is v4.2.1 release. Finally, during last two weeks I did a lot of refactoring in our projects, including Xtensive.Core. I'd like to mention few nice tricks we're using now: Most of extension methods to string, IEnumerable<T>, IList<T>, IDisposable<T> and few our own types are moved to System namespace, so they're visible by default now - everywhere. Really important, if you don't use a tool like ReSharper. Moreover, they're easily discoverable. string.FormathWith is one of newly added methods I really like. Take a look at an example of its usage. The original idea isn't ours, but it's really brilliant. P.S. I'll return back to regular day-to-day posts closer to release. [Less]
Posted about 14 years ago by Dmitri Maximov
Posted about 14 years ago by [email protected] (Alex Yakunin)
Few days ealier I posted a kind of quiz, and here is the answer to it. Let's have a look at example code using it: [Serializable] [HierarchyRoot] public class Author : Entity { [Key, Field] public int Id { get; private set; } ... [More] [Field(Length = 200)] public string Name { get; set; } [Field] public EntitySet<Book> Books { get; private set; } public override string ToString() { return Name; } // Constructors public Author() { } public Author(int id) : base(id) { } } [Serializable] [HierarchyRoot] [KeyGenerator(typeof(ProxyKeyGenerator<Book, Author>))] public class Book : Entity { [Key, Field] public int Id { get; private set; } [Field(Length = 200)] public string Name { get; set; } [Field] [Association(PairTo = "Books")] public EntitySet<Author> Authors { get; private set; } public override string ToString() { return string.Format("{0} by {1}", Name, Authors.ToCommaDelimitedString()); } // Constructors public Book() { } public Book(int id) : base(id) { } } [TestFixture] public class ProxyKeyGeneratorTest { [Test] public void CombinedTest() { // Creating new Domain configuration var config = new DomainConfiguration("sqlserver://localhost/DO40-Tests") { UpgradeMode = DomainUpgradeMode.Recreate }; // Registering all types in the specified assembly and namespace config.Types.Register(typeof (Author).Assembly, typeof(Author).Namespace); // And finally building the domain var domain = Domain.Build(config); using (Session.Open(domain)) { using (var transactionScope = Transaction.Open()) { // Creating two authors var joseph = new Author {Name = "Joseph Albahari"}; var ben = new Author {Name = "Ben Albahari"}; // Creating the Book book with book.Id = joseph.Id var book = new Book(joseph.Id) {Name = "C# 4.0 in a Nutshell"}; book.Authors.Add(joseph); book.Authors.Add(ben); // Testing ProxyKeyGenerator Assert.AreSame(joseph, Query.SingleOrDefault(joseph.Key)); Assert.AreSame(ben, Query.SingleOrDefault(ben.Key)); // Must fail, if [KeyGenerator(typeof(ProxyKeyGenerator<Book, Author>))] // line is commented Assert.AreSame(book, Query.SingleOrDefault(book.Key)); // Let's finally print the Book Console.WriteLine(book); transactionScope.Complete(); } } } } In this example we manually assign Book key that is equal to Author key - imagine this is really necessary, e.g. we import the data and want to keep old Id values, or we simply deal with legacy schema using DomainUpgardeMode.Legacy. By default (i.e. if there is no [KeyGenerator(typeof(ProxyKeyGenerator<Book, Author>))] line) such an attempt must lead to completely unexpected result: the test fails, because an attempt to read this Book by its key returns null. But why? ProxyKeyGenerator used in this example actually affects just on Key comparison. DataObjects.Net compares Key objects using two values they store: KeyProviderInfo object (from storage model). Each KeyProviderInfo object has 1-to-1 relationship with KeyGenerator (the object that is actually responsible for key generation). Key value (Tuple, or simply a value of particular key type - I'll explain this below). See e.g. LongKey.GetHashCode() method code - it fully explains this. LongKey is one of actual Key implementations we use (the generic one). Others are Key<T>, Key<T1,T2>, ... Key<T1,T2,T3,T4> (later we may add other similar ones) - lightweight versions of Key used in most frequent cases to increase performance. Now few more important facts: There is a single KeyProviderInfo object (and thus KeyGenerator object) created for each KeyGenerator type referenced from your model. If custom key generator isn't bound to a particular hierarchy, its default implementation (of XxxKeyGenerator<TFieldType> type) is provided by current storage provider. E.g. SqlCachingKeyGenerator<TFieldType> is used by any SQL storage provider. So as you see, two keys from different hierarchies can be equal, if they share the same key value and the same key generator type. These keys are actually indistinguishable from the point of DataObjects.Net, i.e. both keys are nothing more than different instances of key of the same entity. So any subsequent attempt to resolve any of these keys will lead to the same result as it was in the first attempt. E.g. if you'd try to resolve Book key first, any subsequent attempt to do the same with Author key with the same value will anyway return Book (if expected entity type is not specified), and vice versa. This happens because key-entity pair gets cached after the first database lookup (btw, database lookup will rely on actual key type that is actually stored with key), and any subsequent cache lookup with virtually the same key will succeed further.  But since we use Query.SingleOrDefault<T>(...) in our test, it returns null. Finally, the most important question: Why key comparison is so strange? Short answer is: such behavior is required to support persistent interfaces. Imagine we have IPerson interface, and one more type support it; Book.Persons is EntitySet<IPerson> now. Persistent interface require any persistent type implementing it to: Use the same key type as it's specified in persistent interface Belong to a hierarchy sharing the same KeyGenerator as any other hierarchy where implementors of this persistent interface exists.  I.e. all implementors of persistent interface must share the same key type, and moreover, the same key generator. First condition ensures it is possible to map reference to persistent interface to relational structure. I.e. we know what columns must be created to describe such a reference. Second condition ensures identity of key value through all persistent interface implementations. If there is no such condition, two indistinguishable references to different IPerson instances could exist in the database. Such set of condition in conjunction with described way of key comparison allows us (and you) to successfully execute e.g. this code: Query.SingleOrDefault<IPerson>(Key.Create<IPerson>(value)). Btw, nearly this code is actually used to return reference property value (e.g. IPerson instance) - as you know, internally DataObjects.Net stores any reference as a set of its key values inside Tuple representing entity state. P.S. I understood we must add explicit assertions to Query.Single* method group throwing an exception with appropriate message when a key belonging to hierarchy X is resolved to entity from hierarchy Y (this is possible only in case with cache hit). In fact, it indicates you either must use ProxyKeyGenerator type, or fix your data import routines assigning the keys manually (or simply working without DataObjects.Net at all). [Less]
Posted about 14 years ago by [email protected] (Alex Yakunin)
What is intended purpose of this class: public sealed class ProxyKeyGenerator<TKeyConsumer, TKeySource> : KeyGenerator where TKeyConsumer : Entity where TKeySource : Entity { public override Tuple Next() { var ... [More] key = Key.Create<TKeySource>(Handlers.Domain); return key.Value; } public ProxyKeyGenerator(KeyProviderInfo keyProviderInfo) : base(keyProviderInfo) {} } I.e. imagine this key generator is practically used somewhere. What can be the purpose of this? :) A hint: I'm going to write a post related to legacy data import into DO4 database. [Less]
Posted about 14 years ago by [email protected] (Alex Yakunin)
Check it out.
Posted about 14 years ago by [email protected] (Alex Yakunin)
I just fixed a set of major issues related to single-page HTML & PDF version of Manual: Wrong spacing (both PDF and HTML), especially in code examples. That's because single-page (merged) version was produced by our own tool, and this tool ... [More] really didn't keep all the spaces earlier (there are XDocument-based transformations, so it was pretty easy to face this issue).  No bookmarks in PDF. Page breaks in PDF aren't based on CSS rules. But I finally found a good way of converting everything to PDF. Some links are missing \ wrong. Now everything is really simple: all the interesting links are shown as-is, so you can type them even from printed manual. Of course, they're clickable in PDF version.  So now Manual is fully ready for printing. That's really cool - actually a lot of my time was spent on achieving this. Left issues (except content-related ones): Table of contents does not include page numbers. It seems I'll be unable to fix this automatically.  Its size is 9Mb now I'll try to reduce it later ;) Now I'm working on its inclusion into .HxS / .Chm - it must be split to topics (up to <h2> headers) to appear there ideally. [Less]
Posted about 14 years ago by Dmitri Maximov