/// <summary> /// This function will be called at the end of CreateSetupService and UpdateSetupService to setup any /// additional data in the dto used to display dropdownlists etc. /// It is also called at the end of the CreateService or UpdateService if there are errors, so that /// the data is available if the form needs to be reshown. /// This function should be overridden if the dto needs additional data setup /// </summary> /// <returns></returns> internal protected virtual async Task SetupSecondaryDataAsync(IGenericServicesDbContext db, TDto dto) { if (!SupportedFunctions.HasFlag(CrudFunctions.DoesNotNeedSetup)) { throw new InvalidOperationException("SupportedFunctions flags say that setup of secondary data is needed, but did not override the SetupSecondaryData method."); } }
/// <summary> /// This is used in an update. It copies only the properties in TDto that do not have the [DoNotCopyBackToDatabase] on them. /// You can override this if you need a more complex copy /// </summary> /// <param name="context"></param> /// <param name="source"></param> /// <param name="destination"></param> /// <return>status. destination is only valid if status.IsValid</return> protected internal virtual ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, TDto source, TEntity destination) { var mapper = GenericServicesConfig.AutoMapperConfigs[CreateDictionaryKey <TDto, TEntity>()].CreateMapper(); mapper.Map(source, destination); return(SuccessOrErrors.Success("Successful copy of data")); }
private ISuccessOrErrors DeleteBloggerWithPost(IGenericServicesDbContext db, SimplePostDto post) { var blogger = db.Set <Blog>().SingleOrDefault(x => x.Name == post.BloggerName); db.Set <Blog>().Remove(blogger); return(SuccessOrErrors.Success("It was fine.")); }
//------------------------------------------------------------------------------------------ //now the setup parts for the dropdown boxes /// <summary> /// This is called before a create and an update. It is an update if the dto key property is non zero. /// </summary> /// <param name="db"></param> /// <param name="dto"></param> protected override void SetupSecondaryData(IGenericServicesDbContext db, CrudSalesOrderDto dto) { var getPossibleAddresses = db.Set <CustomerAddress>().Include(x => x.Address).Where(x => x.CustomerID == dto.CustomerID).ToList(); dto.ShipToOptions.SetupDropDownListContent( getPossibleAddresses.Select( y => new KeyValuePair <string, string>(ListCustomerAddressDto.FormCustomerAddressFormatted(y), y.AddressID.ToString("D"))), "--- choose ship to address ---"); if (dto.SalesOrderID != 0 && dto.ShipToAddressID != null) { //there is an entry, so set the selected value to that dto.ShipToOptions.SetSelectedValue(((int)dto.ShipToAddressID).ToString("D")); } dto.BillToOptions.SetupDropDownListContent( getPossibleAddresses.Select( y => new KeyValuePair <string, string>(ListCustomerAddressDto.FormCustomerAddressFormatted(y), y.AddressID.ToString("D"))), "--- choose bill to address ---"); if (dto.SalesOrderID != 0 && dto.BillToAddressID != null) { //there is an entry, so set the selected value to that dto.BillToOptions.SetSelectedValue(((int)dto.BillToAddressID).ToString("D")); } }
/// <summary> /// This provides the IQueryable command to get a list of TEntity, but projected to TDto. /// Can be overridden if standard AutoMapping isn't good enough, or return null if not supported /// </summary> /// <returns></returns> protected internal virtual IQueryable <TDto> ListQueryUntracked(IGenericServicesDbContext context) { var query = GetDataUntracked(context).ProjectTo <TDto>(GenericServicesConfig.AutoMapperConfigs[CreateDictionaryKey <TEntity, TDto>()]); //We check if we need to decompile the LINQ expression so that any computed properties in the class are filled in properly return(ApplyDecompileIfNeeded(query)); }
private static List <PropertyInfo> FindKeys(Type type, IGenericServicesDbContext context) { var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace; // Get the part of the model that contains info about the actual CLR types var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); // Get the entity type from the model that maps to the CLR type var entityType = metadata .GetItems <EntityType>(DataSpace.OSpace) .SingleOrDefault(e => objectItemCollection.GetClrType(e) == type); if (entityType == null) { throw new InvalidOperationException("This method expects a entity class. Did you provide a DTO by mistake?"); } var keyProperties = entityType.KeyProperties.Select(x => type.GetProperty(x.Name)).ToList(); if (!keyProperties.Any()) { throw new MissingPrimaryKeyException(string.Format("Failed to find a EF primary key in type {0}", type.Name)); } if (keyProperties.Any(x => x == null)) { throw new NullReferenceException(string.Format("Failed to find key property by name in type {0}", type.Name)); } return(keyProperties); }
/// <summary> /// This is used in a create. It copies only the properties in TDto that have public setter into the TEntity. /// You can override this if you need a more complex copy /// Note: If SupportedFunctions has the flag ValidateonCopyDtoToData then it validates the data (used by Action methods) /// </summary> /// <param name="context"></param> /// <param name="source"></param> /// <returns>status which, if Valid, has new TEntity with data from DTO copied in</returns> internal protected virtual ISuccessOrErrors <TEntity> CreateDataFromDto(IGenericServicesDbContext context, TDto source) { var result = new TEntity(); Mapper.Map(source, result); return(new SuccessOrErrors <TEntity>(result, "Successful copy of data")); }
/// <summary> /// This sets up the dropdownlist for the possible bloggers and the MultiSelectList of tags /// </summary> /// <param name="context"></param> /// <param name="dto"></param> protected override async Task SetupSecondaryDataAsync(IGenericServicesDbContext context, DetailPostDtoAsync dto) { var bloggers = await context.Set <Blog>().ToListAsync(); dto.Bloggers.SetupDropDownListContent(bloggers.Select(x => new KeyValuePair <string, string>(x.Name, x.BlogId.ToString("D"))), "--- choose blogger ---"); if (dto.PostId != 0) { //there is an entry, so set the selected value to that dto.Bloggers.SetSelectedValue(dto.BlogId.ToString("D")); } List <KeyValuePair <string, int> > preselectedTags; if (dto.PostId == 0) { //create, so just produce empty list preselectedTags = new List <KeyValuePair <string, int> >(); } else { var tags = await context.Set <Tag>() .Where(x => x.Posts.Any(y => y.PostId == dto.PostId)) .Select(x => new { Key = x.Name, Value = x.TagId }) .ToListAsync(); preselectedTags = tags.Select(x => new KeyValuePair <string, int>(x.Key, x.Value)) .ToList(); } dto.UserChosenTags.SetupMultiSelectList( context.Set <Tag>().ToList().Select(x => new KeyValuePair <string, int>(x.Name, x.TagId)), preselectedTags); }
private async Task <ISuccessOrErrors> DeleteBloggerWithPost(IGenericServicesDbContext db, Post post) { var blogger = await db.Set <Blog>().FindAsync(post.BlogId); db.Set <Blog>().Remove(blogger); return(SuccessOrErrors.Success("It was fine.")); }
//--------------------------------------------------- //private helpers private ISuccessOrErrors SetupRestOfDto(IGenericServicesDbContext context, Post post = null) { var db = context as SampleWebAppDb; if (db == null) { throw new NullReferenceException("The IDbContextWithValidation must be linked to TemplateWebAppDb."); } var status = SuccessOrErrors.Success("OK if no errors set"); //now we sort out the blogger var errMsg = SetBloggerIdFromDropDownList(db); if (errMsg != null) { status.AddNamedParameterError("Bloggers", errMsg); } //now we sort out the tags errMsg = ChangeTagsBasedOnMultiSelectList(db, post); if (errMsg != null) { status.AddNamedParameterError("UserChosenTags", errMsg); } return(status); }
/// <summary> /// This sets up the dropdownlist for the possible bloggers and the MultiSelectList of tags /// </summary> /// <param name="context"></param> /// <param name="dto"></param> protected override void SetupSecondaryData(IGenericServicesDbContext context, DetailPostDto dto) { dto.Bloggers.SetupDropDownListContent( context.Set <Blog>() .ToList() .Select(x => new KeyValuePair <string, string>(x.Name, x.BlogId.ToString("D"))), "--- choose blogger ---"); if (dto.PostId != 0) { //there is an entry, so set the selected value to that dto.Bloggers.SetSelectedValue(dto.BlogId.ToString("D")); } var preselectedTags = dto.PostId == 0 ? new List <KeyValuePair <string, int> >() : context.Set <Tag>() .Where(x => x.Posts.Any(y => y.PostId == dto.PostId)) .Select(x => new { Key = x.Name, Value = x.TagId }) .ToList() .Select(x => new KeyValuePair <string, int>(x.Key, x.Value)) .ToList(); dto.UserChosenTags.SetupMultiSelectList( context.Set <Tag>().ToList().Select(x => new KeyValuePair <string, int>(x.Name, x.TagId)), preselectedTags); }
/// <summary> /// This provides the IQueryable command to get a list of TEntity, but projected to TDto. /// Can be overridden if standard AutoMapping isn't good enough, or return null if not supported /// </summary> /// <returns></returns> internal protected virtual IQueryable <TDto> ListQueryUntracked(IGenericServicesDbContext context) { var query = GetDataUntracked(context).Project().To <TDto>(); //We check if we need to decompile the LINQ expression so that any computed properties in the class are filled in properly return(ApplyDecompileIfNeeded(query)); }
protected override ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, DetailPostDto source, Post destination) { var status = SetupRestOfDto(context, destination); return(status.IsValid ? base.UpdateDataFromDto(context, this, destination) : status); }
protected override async Task <ISuccessOrErrors> UpdateDataFromDtoAsync(IGenericServicesDbContext context, DetailPostDtoAsync source, Post destination) { var status = await SetupRestOfDto(context, destination); return(status.IsValid ? await base.UpdateDataFromDtoAsync(context, this, destination) : status); }
/// <summary> /// This copies an existing TEntity into a new the dto using a Lambda expression to define the where clause /// It copies TEntity properties into all TDto properties that have accessable setters, i.e. not private /// </summary> /// <returns>status. If Valid then dto, otherwise null</returns> internal protected virtual async Task <ISuccessOrErrors <TDto> > DetailDtoFromDataInAsync( IGenericServicesDbContext context, Expression <Func <TEntity, bool> > predicate) { var query = GetDataUntracked(context).Where(predicate).Project().To <TDto>(); //We check if we need to decompile the LINQ expression so that any computed properties in the class are filled in properly return(await ApplyDecompileIfNeeded(query).RealiseSingleWithErrorCheckingAsync()); }
protected override ISuccessOrErrors <Post> CreateDataFromDto(IGenericServicesDbContext context, DetailPostDto source) { var status = SetupRestOfDto(context); return(status.IsValid ? base.CreateDataFromDto(context, this) : SuccessOrErrors <Post> .ConvertNonResultStatus(status)); }
/// <summary> /// This is used in a create. It copies only the properties in TDto that have public setter into the TEntity. /// You can override this if you need a more complex copy /// </summary> /// <param name="context"></param> /// <param name="source"></param> /// <returns>status which, if Valid, has new TEntity with data from DTO copied in</returns> protected internal virtual ISuccessOrErrors <TEntity> CreateDataFromDto(IGenericServicesDbContext context, TDto source) { var result = new TEntity(); var mapper = GenericServicesConfig.AutoMapperConfigs[CreateDictionaryKey <TDto, TEntity>()].CreateMapper(); mapper.Map(source, result); return(new SuccessOrErrors <TEntity>(result, "Successful copy of data")); }
/// <summary> /// This copies an existing TEntity into a new dto using a Lambda expression to define the where clause /// It copies TEntity properties into all TDto properties that have accessable setters, i.e. not private /// </summary> /// <returns>status. If valid result is dto. Otherwise null</returns> protected internal virtual ISuccessOrErrors <TDto> DetailDtoFromDataIn(IGenericServicesDbContext context, Expression <Func <TEntity, bool> > predicate) { var query = GetDataUntracked(context).Where(predicate).ProjectTo <TDto>( GenericServicesConfig.AutoMapperConfigs[CreateDictionaryKey <TEntity, TDto>()]); //We check if we need to decompile the LINQ expression so that any computed properties in the class are filled in properly return(ApplyDecompileIfNeeded(query).RealiseSingleWithErrorChecking()); }
/// <summary> /// This is used to update the SalesOrderHeader total when a line item is deleted /// </summary> /// <param name="db"></param> /// <param name="lineItemBeingDeleted"></param> /// <returns></returns> public static ISuccessOrErrors UpdateSalesOrderHeader(IGenericServicesDbContext db, SalesOrderDetail lineItemBeingDeleted) { var salesOrderHeader = db.Set <SalesOrderHeader>().Include(x => x.SalesOrderDetails).Single(x => x.SalesOrderID == lineItemBeingDeleted.SalesOrderID); salesOrderHeader.SubTotal = salesOrderHeader.SalesOrderDetails.Where( x => x.SalesOrderDetailID != lineItemBeingDeleted.SalesOrderDetailID).Sum(x => x.LineTotal); return(SuccessOrErrors.Success("Removed Ok")); }
/// <summary> /// This is used to update the SalesOrderHeader total when a line item is deleted /// </summary> /// <param name="db"></param> /// <param name="lineItemBeingDeleted"></param> /// <returns></returns> public static ISuccessOrErrors UpdateSalesOrderHeader(IGenericServicesDbContext db, SalesOrderDetail lineItemBeingDeleted) { var salesOrderHeader = db.Set<SalesOrderHeader>().Include(x => x.SalesOrderDetails).Single(x => x.SalesOrderID == lineItemBeingDeleted.SalesOrderID); salesOrderHeader.SubTotal = salesOrderHeader.SalesOrderDetails.Where( x => x.SalesOrderDetailID != lineItemBeingDeleted.SalesOrderDetailID).Sum(x => x.LineTotal); return SuccessOrErrors.Success("Removed Ok"); }
/// <summary> /// This is called on create. We override it to get at the pasword hash/salt /// </summary> /// <param name="context"></param> /// <param name="source"></param> /// <returns></returns> protected override ISuccessOrErrors <Customer> CreateDataFromDto(IGenericServicesDbContext context, CrudCustomerDto source) { //We need to initialise the username and password var data = base.CreateDataFromDto(context, source); data.Result.PasswordHash = "empty"; data.Result.PasswordSalt = "empty"; return(data); }
public static ISuccessOrErrors DeleteAssociatedAddress(IGenericServicesDbContext db, CustomerAddress customerAddress) { var address = db.Set<Address>().Find(customerAddress.AddressID); if (address == null) return new SuccessOrErrors().AddSingleError( "Could not delete the associated entry as it was not in the database. Could it have been deleted by someone else?"); db.Set<Address>().Remove(address); return SuccessOrErrors.Success("Removed Ok"); }
protected internal override ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, TDto source, TEntity destination) { using (new LogStartStop(this)) { if (_whereToFail.HasFlag(InstrumentedOpFlags.FailOnUpdateDataFromDto)) { return(new SuccessOrErrors().AddSingleError("Flag was set to fail in UpdateDataFromDto.")); } return(base.UpdateDataFromDto(context, source, destination)); } }
protected internal override ISuccessOrErrors <TEntity> CreateDataFromDto(IGenericServicesDbContext context, TDto source) { using (new LogStartStop(this)) { if (_whereToFail.HasFlag(InstrumentedOpFlags.FailOnCreateDataFromDto)) { return(SuccessOrErrors <TEntity> .ConvertNonResultStatus(new SuccessOrErrors().AddSingleError("Flag was set to fail in CreateDataFromDto."))); } return(base.CreateDataFromDto(context, source)); } }
protected override ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, DetailPostDto source, Post destination) { var status = SetupRestOfDto(context, destination); if (status.IsValid) { //now we copy the items to the right place status = base.UpdateDataFromDto(context, this, destination); } return(status); }
//This code is the second attempt at improving the SQL query for Customers //It uses the from ... in format of LINQ which allows a let assignment in it. //This produces a better SQL query protected override IQueryable <ListCustomerVer2Dto> ListQueryUntracked(IGenericServicesDbContext context) { return(from x in context.Set <Customer>() let hasBoughtBefore = x.SalesOrderHeaders.Any() select new ListCustomerVer2Dto { CustomerID = x.CustomerID, CompanyName = x.CompanyName, FullName = x.Title + (x.Title == null ? "" : " ") + x.FirstName + " " + x.LastName + " " + x.Suffix, HasBoughtBefore = hasBoughtBefore, TotalAllOrders = hasBoughtBefore ? x.SalesOrderHeaders.Sum(y => y.TotalDue) : 0 }); }
//--------------------------------------------------------------- //protected methods /// <summary> /// This gets the key values from this DTO in the correct order. Used in FindItemTrackedForUpdate sync/async /// </summary> /// <param name="context"></param> /// <returns></returns> protected object[] GetKeyValues(IGenericServicesDbContext context) { var efkeyProperties = context.GetKeyProperties <TEntity>().ToArray(); var dtoProperties = typeof(TDto).GetProperties(BindingFlags.Public | BindingFlags.Instance); var keysInOrder = efkeyProperties.Select(x => dtoProperties.SingleOrDefault(y => y.Name == x.Name && y.PropertyType == x.PropertyType)).ToArray(); if (keysInOrder.Any(x => x == null)) { throw new MissingPrimaryKeyException("The dto must contain all the key(s) properties from the data class."); } return(keysInOrder.Select(x => x.GetValue(this)).ToArray()); }
protected override ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, CrudSalesOrderDto source, SalesOrderHeader destination) { var status = SetupRestOfDto(context, source); if (status.IsValid) { //now we copy the items to the right place status = base.UpdateDataFromDto(context, source, destination); } return(status); }
public static ISuccessOrErrors DeleteAssociatedAddress(IGenericServicesDbContext db, CustomerAddress customerAddress) { var address = db.Set <Address>().Find(customerAddress.AddressID); if (address == null) { return (new SuccessOrErrors().AddSingleError( "Could not delete the associated entry as it was not in the database. Could it have been deleted by someone else?")); } db.Set <Address>().Remove(address); return(SuccessOrErrors.Success("Removed Ok")); }
//--------------------------------------------------------------- //protected methods /// <summary> /// This gets the key values from this DTO. Used in FindItemTrackedForUpdate sync/async /// </summary> /// <param name="context"></param> /// <returns></returns> protected object[] GetKeyValues(IGenericServicesDbContext context) { var efkeyPropertyNames = context.GetKeyProperties <TEntity>().ToArray(); var dtoKeyProperies = typeof(TDto).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => efkeyPropertyNames.Any(y => y.Name == x.Name && y.PropertyType == x.PropertyType)).ToArray(); if (efkeyPropertyNames.Length != dtoKeyProperies.Length) { throw new MissingPrimaryKeyException("The dto must contain the key(s) properties from the data class."); } return(dtoKeyProperies.Select(x => x.GetValue(this)).ToArray()); }
protected override ISuccessOrErrors <SalesOrderDetail> CreateDataFromDto(IGenericServicesDbContext context, CreateLineItemDto source) { var status = base.CreateDataFromDto(context, source); if (!status.IsValid) { return(status); } //we read the list price from the products status.Result.UnitPrice = context.Set <Product>().Single(x => x.ProductID == source.ProductID).ListPrice; status.Result.UnitPriceDiscount = 0; return(status); }
//---------------------------------------------------------------------- //non-overridable internal methods /// <summary> /// This copies back the keys from a newly created entity into the dto as long as there are matching properties in the Dto /// </summary> /// <param name="context"></param> /// <param name="newEntity"></param> internal protected void AfterCreateCopyBackKeysToDtoIfPresent(IGenericServicesDbContext context, TEntity newEntity) { var dtoKeyProperies = typeof(TDto).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var entityKeys in context.GetKeyProperties <TEntity>()) { var dtoMatchingProperty = dtoKeyProperies.SingleOrDefault( x => x.Name == entityKeys.Name && x.PropertyType == entityKeys.PropertyType); if (dtoMatchingProperty == null) { continue; } dtoMatchingProperty.SetValue(this, entityKeys.GetValue(newEntity)); } }
private ISuccessOrErrors DeleteBloggerWithPost(IGenericServicesDbContext db, SimplePostDto post) { var blogger = db.Set<Blog>().SingleOrDefault(x => x.Name == post.BloggerName); db.Set<Blog>().Remove(blogger); return SuccessOrErrors.Success("It was fine."); }
private async Task<ISuccessOrErrors> DeleteBloggerWithPost(IGenericServicesDbContext db, Post post) { var blogger = await db.Set<Blog>().FindAsync(post.BlogId); db.Set<Blog>().Remove(blogger); return SuccessOrErrors.Success("It was fine."); }
private async Task<ISuccessOrErrors> FailDeleteRelationships(IGenericServicesDbContext db, Post post) { return new SuccessOrErrors().AddSingleError("I failed."); }
private static void RunCommand2(IGenericServicesDbContext db) { var service = new ListService(db); var list = service.GetAll<SimplePostDto>().ToList(); }