Could you please advice me which of these two (if any) approaches to Transaction Script patter is correct?
Basically I need to implement "pure" TS, which is not to bend it in some convenient way. And the question is when I get data from Table Data Gateway shall I store them, for different parts (logical part for sake of clarity) of transaction and use them where do I need, OR get data don't store them but directly use them and if the same data are needed again call table gateway and ask for it.
Example: http://pastebin.com/hGCgrfEs
Thanks. Hopefully it makes sense if not let me know :)
The TSDoSomething2 class is closer to a good solution, i propose this to implement a SOLID transcript implementation:
public class OrderBusiness : IAcceptVisitOrder
{
private readonly string _id;
private int _status;
public OrderBusiness(string id, int status)
{
_id = id;
_status = status;
}
public void ChangeStatus(int newStatus)
{
//validation logic....
_status = newStatus;
}
public void Accept(IOrderVisitor visitor)
{
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
visitor.Visit(_id, _status);
}
}
public class Client
{
private readonly OrderApplicationService _service;
//Get from Service locator like ninject, etc
private readonly IReader<OrderBusiness, string> _reader = new OrderBusinessReader();
private readonly IOrderVisitor _visitor = new UpdateOrderVisitor();
public Client()
{
_service = new OrderApplicationService(_reader, _visitor);
}
public void Run(UpdateOrderCommand command)
{
_service.When(command);
}
}
public class UpdateOrderCommand
{
[Required]
public string Id { get; set; }
[Required]
public int Status { get; set; }
}
public class OrderApplicationService
{
private readonly IReader<OrderBusiness, string> _reader;
private readonly IOrderVisitor _visitor;
public OrderApplicationService(IReader<OrderBusiness, string> reader, IOrderVisitor visitor)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
_reader = reader;
_visitor = visitor;
}
public void When(UpdateOrderCommand command)
{
var order = GetBusinessObject(command.Id);
order.ChangeStatus(command.Status);
order.Accept(_visitor);
}
private OrderBusiness GetBusinessObject(string id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return _reader.Read(id);
}
}
public interface IReader<out T, in TK>
{
T Read(TK id);
}
public interface IAcceptVisitOrder
{
void Accept(IOrderVisitor visitor);
}
public interface IOrderVisitor
{
void Visit(string id, int status);
}
public class OrderBusinessReader : IReader<OrderBusiness, string>
{
public OrderBusiness Read(string id)
{
//Read data from db with EF, ADO.NET, ETC
var status = 0;
return new OrderBusiness(id, status);
}
}
class UpdateOrderVisitor : IOrderVisitor
{
public void Visit(string id, int status)
{
//Persist to database, etc
}
}
Hope this helps!Regards!
Related
Im trying to write a simple code to implement the Singleton and object null patterns.
the code should check if the new customer has a name, if yes put it in the real customer, and if not in the fakecustomer.
My focus in this question is: Is the Singleton pattern making my code thread safe in this case?
interface Icustomer
{
string Name { get; }
bool IsNull { get; }
}
class realcustomer : Icustomer
{
public string Name { get; set; }
public bool IsNull { get { return false; } }
public realcustomer(string name)
{
Name = name;
}
}
class fakecustomer : Icustomer
{
public string Name { get { return "customer not available"; } }
public bool IsNull { get { return true; } }
}
class checkifnull
{
public static Icustomer Getcustomer(string name)
{
if (string.IsNullOrEmpty(name))
{
return new fakecustomer();
}
else
{
return new realcustomer(name);
}
}
}
class Singleton
{
private int total = 0;
private static Icustomer cust;
private Singleton() { }
public static Icustomer makecust(string name)
{
if (cust == null)
{
if (string.IsNullOrEmpty(name))
{
cust = new fakecustomer();
}
else
{
cust = new realcustomer(name);
}
}
return cust;
}
public void add()
{
total++;
}
public int getTotal()
{
return total;
}
}
internal class Program
{
static void Main(string[] args)
{
Icustomer new_cust = Singleton.makecust("name");
}
}
each pattern works when implemented on its own, but now i'm trying to use both at the same time.
Using C# Mongo DB Driver version, 2.4.4 and I try to deserialize a complex class. It has been stored correctly in the MongoDB, but when being deserialized it is null.
This is my datamodel:
public abstract class Document
{
public DocumentId Id { get; private set; }
public DocumentProperty TestProperty { get; private set; }
protected Document() { }
public void SetId(string value)
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("value");
Id = new DocumentId(value);
}
public void AddProperty(DocumentProperty property)
{
if (property == null) throw new ArgumentNullException("property");
TestProperty = property;
}
}
public class DocumentId
{
public string Value { get; }
public DocumentId(string value)
{
Value = value;
}
}
public abstract class DocumentProperty
{
public string Value { get; }
protected DocumentProperty()
{
}
protected DocumentProperty(string value)
{
Value = value;
}
}
public class TitleDocumentProperty : DocumentProperty
{
public TitleDocumentProperty() { }
public TitleDocumentProperty(string value) : base(value)
{
}
}
This is my mapping code:
public class MongoClassMapper
{
public static void InitializeMap()
{
BsonClassMap.RegisterClassMap<DocumentProperty>(map =>
{
map.MapProperty(property => property.Value).SetDefaultValue("123");
map.SetIsRootClass(true);
});
BsonClassMap.RegisterClassMap<TitleDocumentProperty>(map =>
{
});
BsonClassMap.RegisterClassMap<Document>(map =>
{
map.MapProperty(document => document.Id);
map.MapProperty(document => document.TestProperty);
map.SetIsRootClass(true);
});
}
}
This is my methods for adding and retrieving data from Mongo:
public async Task<string> Add(Document document)
{
await _documents.InsertOneAsync(document);
return document.Id.Value;
}
public async Task<Document> Get(DocumentId id)
{
var mongoDocuments = await _documents.Find(document => document.Id == id)
.ToListAsync();
return mongoDocuments.SingleOrDefault();
}
This is the code that I use for testing:
private static MongoCache _cache;
static void Main(string[] args)
{
MongoClassMapper.InitializeMap();
_cache = new MongoCache(ConfigurationManager.ConnectionStrings["connName"].ConnectionString, "dbName");
string id = ItShouldCreateRecord().Result;
var doc = ItShouldGetRecord(id).Result;
}
private static Task<string> ItShouldCreateRecord()
{
//Arrange
Document document = new FakeDocument();
document.SetId(Guid.NewGuid().ToString());
document.AddProperty(new TitleDocumentProperty("this is a title"));
//Act
string id = _cache.Add(document).Result;
//Assert
//Assert.NotEmpty(id);
return Task.FromResult(id);
}
private static Task<Document> ItShouldGetRecord(string id)
{
//Arrange
//Act
return _cache.Get(new DocumentId(id));
//Assert
//Assert.NotNull(doc);
}
}
[BsonIgnoreExtraElements]
public class FakeDocument : Document
{
}
I expected that when I retrieve the document (using _cache.Get()) from the DB, that the property TestProperty would have an actual value. Currently, it is NULL.
The problem is the absence of a setter on your Value property which will cause MongoDB to not deserialize this property:
public abstract class DocumentProperty
{
public string Value { get; /* no setter*/ }
}
You can fix this by simply adding the setter (using any accessibility setting, even private works):
public abstract class DocumentProperty
{
public string Value { get; /* private if you want */ set; }
}
I have captured a JIRA issue and a pull request to get that fixed.
I am working on a CQRS pattern. I have created one project related to this approach in which I can insert and retrieve data. I came to know that there are two different models Write Model(Commands) and Read Model(Query). I just want to know that my approach for write model is right or not. And how to use temporarily database for event sourcing when multiple users doing same operations.
Command.cs
public class Command : Message
{
}
public class Insert : Command
{
public readonly Guid Id;
public readonly string Name;
public Insert(Guid id, string name)
{
Id = id;
Name = name;
}
}
public class Update : Command
{
public readonly Guid Id;
public readonly string NewName;
public readonly int OriginalVersion;
public Update(Guid id, string newName)
{
Id = id;
NewName = newName;
}
}
public class Delete : Command
{
public Guid Id;
public readonly int OriginalVersion;
public Delete(Guid id)
{
Id = id;
}
}
Event.cs
public class Event:Message
{
public int Version;
}
public class Inserted : Event
{
public readonly Guid Id;
public readonly string Name;
public Inserted(Guid id, string name)
{
Id = id;
Name = name;
}
}
public class Updated : Event
{
public readonly Guid Id;
public readonly string NewName;
public readonly int OriginalVersion;
public Updated(Guid id, string newName)
{
Id = id;
NewName = newName;
}
}
public class Deleted : Event
{
public Guid Id;
public Deleted(Guid id)
{
Id = id;
}
}
EventStore.cs
public interface IEventStore
{
void SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion);
List<Event> GetEventsForAggregate(Guid aggregateId);
}
public class EventStore : IEventStore
{
private readonly IEventPublisher _publisher;
private struct EventDescriptor
{
public readonly Event EventData;
public readonly Guid Id;
public readonly int Version;
public EventDescriptor(Guid id, Event eventData, int version)
{
EventData = eventData;
Version = version;
Id = id;
}
}
public EventStore(IEventPublisher publisher)
{
_publisher = publisher;
}
private readonly Dictionary<Guid, List<EventDescriptor>> _current = new Dictionary<Guid, List<EventDescriptor>>();
public void SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion)
{
List<EventDescriptor> eventDescriptors;
if (!_current.TryGetValue(aggregateId, out eventDescriptors))
{
eventDescriptors = new List<EventDescriptor>();
_current.Add(aggregateId, eventDescriptors);
}
else if (eventDescriptors[eventDescriptors.Count - 1].Version != expectedVersion && expectedVersion != -1)
{
throw new ConcurrencyException();
}
var i = expectedVersion;
foreach (var #event in events)
{
i++;
#event.Version = i;
eventDescriptors.Add(new EventDescriptor(aggregateId, #event, i));
_publisher.Publish(#event);
}
}
public List<Event> GetEventsForAggregate(Guid aggregateId)
{
List<EventDescriptor> eventDescriptors;
if (!_current.TryGetValue(aggregateId, out eventDescriptors))
{
throw new AggregateNotFoundException();
}
return eventDescriptors.Select(desc => desc.EventData).ToList();
}
}
public class AggregateNotFoundException : Exception
{
}
public class ConcurrencyException : Exception
{
}
ReadModel.cs
public interface IReadModelFacade
{
IEnumerable<InventoryItemListDto> GetInventoryItems();
InventoryItemDetailsDto GetInventoryItemDetails(Guid id);
}
public class InventoryItemDetailsDto
{
public Guid Id;
public string Name;
public int CurrentCount;
public int Version;
public InventoryItemDetailsDto(Guid id, string name, int currentCount, int version)
{
Id = id;
Name = name;
CurrentCount = currentCount;
Version = version;
}
}
public class InventoryItemListDto
{
public Guid Id;
public string Name;
public InventoryItemListDto(Guid id, string name)
{
Id = id;
Name = name;
}
}
public class InventoryListView : Handles<Inserted>, Handles<Updated>
{
public void Handle(Inserted message)
{
BullShitDatabase.list.Add(new InventoryItemListDto(message.Id, message.Name));
}
public void Handle(Updated message)
{
var item = BullShitDatabase.list.Find(x => x.Id == message.Id);
item.Name = message.NewName;
}
}
public class InvenotryItemDetailView : Handles<Inserted>, Handles<Updated>
{
public void Handle(Inserted message)
{
BullShitDatabase.details.Add(message.Id, new InventoryItemDetailsDto(message.Id, message.Name, 0, 0));
}
public void Handle(Updated message)
{
InventoryItemDetailsDto d = GetDetailsItem(message.Id);
d.Name = message.NewName;
d.Version = message.Version;
}
private InventoryItemDetailsDto GetDetailsItem(Guid id)
{
InventoryItemDetailsDto d;
if (!BullShitDatabase.details.TryGetValue(id, out d))
{
throw new InvalidOperationException("did not find the original inventory this shouldnt happen");
}
return d;
}
}
public class ReadModelFacade : IReadModelFacade
{
public IEnumerable<InventoryItemListDto> GetInventoryItems()
{
return BullShitDatabase.list;
}
public InventoryItemDetailsDto GetInventoryItemDetails(Guid id)
{
return BullShitDatabase.details[id];
}
}
public static class BullShitDatabase
{
public static Dictionary<Guid, InventoryItemDetailsDto> details = new Dictionary<Guid, InventoryItemDetailsDto>();
public static List<InventoryItemListDto> list = new List<InventoryItemListDto>();
}
It should't matter whether you're using EventStore or any other storing mechanism, you should be coding against interfaces (contracts) anyway.
But first things first, you commands IMO are not properly defined, they should be immutable objects which carry data and represent a domain operation (CRUD or not), so why do you have methods defined in the commands?
It is not a problem defining a command as a class, you'll need one in the end, but why don't you have an interface as the base type for all the commands? (SOLID principles)
All the class names (commands/events) have to be meaningful, that said, Update, Delete... don't say much really.
Also I don't see where your service layer is. The service layer should be responsible for handling the commands, so how are you planning to do this?
Bellow you have an example of how I would do it (a tad abstract but it gives you an idea):
// Message definitions
public interface IMessage
{
Guid ID {get; set;}
}
public interface IEvent : IMessage
{ }
public interface ICommand : IMessage
{ }
public class DeleteUserCommand : ICommand
{
public Guid ID {get; set;}
public Guid UserId {get; set;}
}
public class UserDeletedEvent : IEvent
{
public Guid ID {get; set;}
public Guid UserId {get; set;}
}
// Repository definitions
public interface IRepository
{ }
public interface IUserRepository : IRepository
{
void DeleteUser(Guid userId);
}
public UserRepository : IUserRepository
{
public void DeleteUser(Guid userId)
{}
}
// Service definitions
public interface IService
{ }
public class UserService : IService, IHandles<DeleteUserCommand>
{
public IUserRepository UserRepository {get; set;}
public void Handle(DeleteUserCommand deleteUserCommand)
{
UserRepository.DeleteUser(deleteUserCommand.Id)
//raise event
}
}
I just started to play around with MongoDB (C#) and tried to port a repository over from entity framework. I'm using the official C# driver 1.0. Now I did something like this:
internal class MongoContext
{
public MongoContext(string constring)
{
MongoServer server = MongoServer.Create(constring);
this.myDB = server.GetDatabase("MyDB");
BsonClassMap.RegisterClassMap<VoyageNumber>(cm =>
{ cm.MapField<string>(p => p.Id); });
BsonClassMap.RegisterClassMap<Schedule>(cm =>
{ cm.MapField<DateTime>(p => p.EndDate); cm.MapField<DateTime>(p => p.StartDate); });
BsonClassMap.RegisterClassMap<Voyage>(cm =>
{ cm.MapIdField<VoyageNumber>(p => p.VoyageNumber); cm.MapField<Schedule>(p => p.Schedule); });
}
private MongoDatabase myDB;
public MongoDatabase MyDB
{ get { return this.myDB; } }
}
I'd then go on and implement the Repository like this:
public class MongoVoyageRepository : IVoyageRepository
{
private readonly MongoContext context;
public MongoVoyageRepository(string constring)
{
this.context = new MongoContext(constring);
}
public void Store(Domain.Model.Voyages.Voyage voyage)
{
MongoCollection<Voyage> mongoVoyages = context.MyDB.GetCollection<Voyage>("Voyages");
//store logic...
}
}
Now I'd like to know if it is a good decision to instantiate a "context" like this in terms of performance. Does it make sense to put the BsonClass Maps in there?
Thank you for your input.
// entity base
public class MongoEntity {
public ObjectId _id { get; set; }
}
//user entity
public class Users : MongoEntity {
public string UserName { get; set; }
public string Password { get; set; }
}
// simple repository
public class Repository {
private MongoDatabase _db;
public MongoDatabase Database { get; private set; }
public Repository(string constr, string dbname) {
var server = MongoServer.Create(constr);
_db = server.GetDatabase(dbname);
Database = _db;
}
private MongoCollection<T> GetCollection<T>() where T : MongoEntity {
return _db.GetCollection<T>(typeof(T).Name);
}
public IEnumerable<T> List<T>() where T : MongoEntity {
return GetCollection<T>().FindAll();
}
public IEnumerable<T> List<T>(Expression<Func<T, bool>> exp) where T : MongoEntity {
return GetCollection<T>().AsQueryable<T>().Where(exp);
}
public T Single<T>(Expression<Func<T, bool>> exp) where T : MongoEntity {
return List<T>(exp).SingleOrDefault();
}
public void Insert<T>(T entity) where T : MongoEntity {
GetCollection<T>().Insert<T>(entity);
}
public void Insert<T>(ICollection<T> entities) where T : MongoEntity {
GetCollection<T>().InsertBatch(entities);
}
// Update, Delete method etc ...
}
// example
var repository = new Repository("mongodb://localhost", "test");
repository.Single<Users>(u => u.UserName == "myUserName");
I guess it does not make sense to register classes mapping each time when you create your repository class. Since MongoDB C# driver manages connections to the MongoDB internally, it seems to me that it's better to create MongoServer and register classes mapping only once, during application start and then use it.
I am using singleton in order to create MongoServer only once
public class MongoRead : MongoBase
{
public MongoRead(MongoServer server)
: base(server)
{
}
public override MongoDatabase Database
{
get { return Server.GetDatabase("myDb"); }
}
public MongoCollection Logs
{
get { return Database.GetCollection("logs"); }
}
private static MongoRead _instance = null;
public static MongoRead Instance
{
get
{
if (_instance == null)
{
_instance = RegisterMongoDb();
}
return _instance;
}
}
private static MongoRead RegisterMongoDb()
{
var readServer = MongoServer.Create(connectionString);
var read = new MongoRead(readServer);
var myConventions = new ConventionProfile();
myConventions.SetIdMemberConvention(new NoDefaultPropertyIdConvention());
BsonClassMap.RegisterConventions(myConventions, t => true);
return read;
}
}
So you also can use above class in your Repository:
public class MongoVoyageRepository : IVoyageRepository
{
private readonly MongoRead context
{
get { return MongoRead.Instance; }
};
public MongoVoyageRepository()
{
}
public void Store(Domain.Model.Voyages.Voyage voyage)
{
MongoCollection<Voyage> mongoVoyages =
context.Database.GetCollection<Voyage>("Voyages");
//store logic...
}
}
this is also interesting if you want to use repository pattern.
I'm updating a program and adding a new table to the database. The program uses Castle's ActiveRecord with repositories. I've set the classes and repository up and can get the test case out of the database fine. However, when I try to add a new record to the database, nothing happens. No error and no new record. If I get the test case out of the database and change something then call Save(), the record in the database changes.
This is the code I'm using to test:
ICountryRepository _countryRepository = Country.GetRepository(site.Id);
//get test case and update
Country c = _countryRepository.FindById(1).Value;
c.Name = "c";
_countryRepository.Save(c);
//create new Country and save
Country d = new Country();
d.Id = 2;
d.Name = "d";
_countryRepository.Save(d);
Now as it's a maintenence project with no real time to stop and study how the Castle framework is doing everything, I'm learning as I go along. I've picked up how to do things by studying the rest of the code and there are occasions in the code where the above code has been used to create new records, so I'm sure Save() is the right function to call.
I've tried dropping the create code in with another part of the code that inserts an object into a different table, just to make sure there weren't different levels of permission in play. There are no differences in the database between the table I added and the table I'm basing my code off.
As I've said, my experiences with ActiveRecord and Castle's framework here are few, it could/will be something quite simple. So hopefully someone can point it out to me.
Thanks.
Edit:
Country Class:
[ActiveRecord("Country")]
[SearchEntity]
public class Country : AbstractEntity<Country, ICountryRepository>
{
int _countryId;
string _name;
int _action;
[PrimaryKey("CountryId")]
public int Id
{
get { return _countryId; }
set { _countryId = value; }
}
[SearchProperty]
[Property(Length = 100)]
public string Name
{
get { return _name; }
set { _name = value; }
}
[Property]
public int PostRegisterActionId
{
get { return _action; }
set { _action = value; }
}
}
AbstractRepository since CountryRepository does nothing at the moment:
[Transient]
public abstract class AbstractRepository<T> : IRepository<T> where T : AbstractEntity, new()
{
#region Private Vars
protected FutureQueryRunner _futureQueryRunner;
protected IDynamicSearchService _dynamicSearchService;
protected bool _caching;
protected int _cachedPages;
protected CachingFutureQueryOfList<T> _cachedQuery;
protected IServiceBus _serviceBus;
private string _entityTypeName = string.Empty;
#endregion
#region Constructors
public AbstractRepository(IDynamicSearchService dynamicSearchService, IServiceBus serviceBus)
{
_dynamicSearchService = dynamicSearchService;
_serviceBus = serviceBus;
}
#endregion
#region Public Methods
public virtual void Save(T instance)
{
ActiveRecordMediator<T>.Save(instance);
}
public virtual void Create(T instance)
{
ActiveRecordMediator<T>.Create(instance);
}
public void Delete(T instance)
{
ActiveRecordMediator<T>.Delete(instance);
}
public virtual IFutureQueryOf<T> New()
{
return new NullFutureQuery<T>();
}
public virtual IFutureQueryOf<T> FindById(int id/*, params string[] eagerAssociations*/) // eager associations buggy
{
DetachedCriteria criteria = GetDefaultCriteria()
.Add(Expression.IdEq(id));
/*foreach (string eager in eagerAssociations)
criteria.SetFetchMode(eager, NHibernate.FetchMode.Eager);*/
return new FutureQueryOf<T>(_futureQueryRunner)
.SetCriteria(criteria);
}
public virtual IFutureQueryOfList<T> FindAll(string sortBy, bool sortAsc)
{
DetachedCriteria criteria = GetDefaultCriteria();
if (!string.IsNullOrEmpty(sortBy))
criteria.AddOrder(sortAsc ? NHibernate.Criterion.Order.Asc(sortBy) : NHibernate.Criterion.Order.Desc(sortBy));
return new FutureQueryOfList<T>(_futureQueryRunner)
.SetCriteria(criteria);
}
public virtual IFutureQueryOfList<T> FindAll(int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new DefaultCriteriaProvider<T>(DetachedCriteria.For<T>()),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAll");
}
public virtual IFutureQueryOfList<T> FindAll(string searchString, int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new SearchStringCriteriaProvider<T>(_dynamicSearchService, searchString),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAllSearchString_" + searchString);
}
public virtual IFutureQueryOfList<T> FindAll(IListFilter filter, int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new FilterCriteriaProvider<T>(_dynamicSearchService, filter),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAllListFilter"); // TODO - the cache key needs to represent individual filters
}
public virtual IFutureQueryOf<int> GetCount(string searchString)
{
return new FutureQueryOf<int>(_futureQueryRunner)
.SetCriteria(AddDefaultCriteria(new SearchStringCriteriaProvider<T>(_dynamicSearchService, searchString).GetDetachedCriteria())
.SetProjection(Projections.RowCount()));
}
public virtual IFutureQueryOf<int> GetCount()
{
return new FutureQueryOf<int>(_futureQueryRunner)
.SetCriteria(GetDefaultCriteria()
.SetProjection(Projections.RowCount()));
}
public virtual string EntityType
{
get
{
if (string.IsNullOrEmpty(_entityTypeName))
_entityTypeName = typeof(T).Name;
return _entityTypeName;
}
}
public IRepository<T> EnableCaching(bool caching)
{
_caching = caching;
return this;
}
public IRepository<T> WithPagesToCache(int cachedPages)
{
_cachedPages = cachedPages;
return this;
}
public virtual IRepository<T> ForSite(int siteId)
{
return this;
}
public IRepository<T> RunFutureQueriesWith(FutureQueryRunner futureQueryRunner)
{
_futureQueryRunner = futureQueryRunner;
return this;
}
#endregion
#region Protected Methods
protected virtual DetachedCriteria AddDefaultCriteria(DetachedCriteria criteria)
{
return criteria;
}
protected DetachedCriteria GetDefaultCriteria()
{
return AddDefaultCriteria(DetachedCriteria.For<T>());
}
protected IFutureQueryOf<U> NewQueryOf<U>(DetachedCriteria criteria)
{
return new FutureQueryOf<U>(_futureQueryRunner).SetCriteria(criteria);
}
protected IFutureQueryOfList<U> NewQueryOfList<U>(DetachedCriteria criteria)
{
return new FutureQueryOfList<U>(_futureQueryRunner).SetCriteria(criteria);
}
#endregion
#region Private Methods
private IFutureQueryOfList<T> FindAll(ICriteriaProvider<T> criteriaProvider, int page, int resultsPerPage, string sortBy, bool sortAsc, string cacheKey)
{
CachingFutureQueryOfList<T> rtnVal = null;
bool cached = false;
if (_cachedQuery != null && _caching)
{
rtnVal = _cachedQuery;
cached = rtnVal.SetPage(page, sortBy, sortAsc, cacheKey);
}
if (!cached)
{
rtnVal = new CachingFutureQueryOfList<T>(_futureQueryRunner, page, _cachedPages, resultsPerPage, cacheKey)
.SetCriteria(AddDefaultCriteria(criteriaProvider.GetDetachedCriteria()), sortBy, sortAsc);
if (_caching)
_cachedQuery = rtnVal;
}
return rtnVal;
}
#endregion
#region Criteria Providers
private interface ICriteriaProvider<U>
{
DetachedCriteria GetDetachedCriteria();
}
private class DefaultCriteriaProvider<U> : ICriteriaProvider<U>
{
private DetachedCriteria _criteria;
public DefaultCriteriaProvider(DetachedCriteria criteria)
{
_criteria = criteria;
}
public DetachedCriteria GetDetachedCriteria()
{
return _criteria;
}
}
private class SearchStringCriteriaProvider<U> : ICriteriaProvider<U>
{
private IDynamicSearchService _searchService;
private string _searchString;
public SearchStringCriteriaProvider(IDynamicSearchService searchService, string searchString)
{
_searchService = searchService;
_searchString = searchString;
}
public DetachedCriteria GetDetachedCriteria()
{
return _searchService.GetDetachedCriteria<U>(_searchString);
}
}
private class FilterCriteriaProvider<U> : ICriteriaProvider<U>
{
private IDynamicSearchService _searchService;
private IListFilter _filter;
public FilterCriteriaProvider(IDynamicSearchService searchService, IListFilter filter)
{
_searchService = searchService;
_filter = filter;
}
public DetachedCriteria GetDetachedCriteria()
{
return _searchService.GetDetachedCriteria<U>(_filter);
}
}
#endregion
}
If you're explicitly setting the Country PK, as it seems you're doing, you probably need to call Create() instead of Save() (I don't know if your repository implementation exposes this).
If this didn't work, please post your class mappings and repository implementation.