Skip to content

Instantly share code, notes, and snippets.

@Boggin
Created October 15, 2013 15:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Boggin/68c39025069a6f524097 to your computer and use it in GitHub Desktop.
Save Boggin/68c39025069a6f524097 to your computer and use it in GitHub Desktop.
Lookup tables for code-first Entity Framework (with auditing by FrameLog).
namespace Demo.Repository.Models
{
public class Country : Lookup
{
/// <summary>
/// Gets or sets the issues that link to this.
/// </summary>
/// <remarks>This may be referenced by many issues.</remarks>
public ICollection<Issue> Issues { get; set; }
}
}
namespace Demo.Repository
{
public class CountryRepository : LookupRepository<Country>, ICountryRepository
{
public CountryRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public int UpdateCountries(IEnumerable<Country> countries)
{
return this.UpdateLookups(countries);
}
}
}
namespace Demo.Repository.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class Issue : ICloneable
{
public ICollection<Country> AdditionalCountries { get; set; }
public string Country { get; set; }
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
/// <summary>
/// Shallow clone.
/// </summary>
/// <returns>Returns a <c>MemberwiseClone</c>.</returns>
public object Clone()
{
return this.MemberwiseClone();
}
}
}
namespace Demo.Repository
{
public class IssueRepository : GenericRepository<Issue>, IIssueRepository
{
public IssueRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public void SaveIssue(Issue issue, Person user)
{
var context = this.UnitOfWork.ApplicationContext;
if (issue.Id == 0)
{
this.Add(issue);
}
else
{
// fetch the stored entity.
var storedIssue =
this.DataSet
.Include(i => i.AdditionalCountries)
.Single(i => i.Id == issue.Id);
// update stored entity scalar values.
context.Entry(storedIssue).CurrentValues.SetValues(issue);
// relationship identifiers provided by update.
codes = issue.AdditionalCountries.Select(e => e.Code).ToList();
var countries = UpdateRelationship(codes, storedIssue.AdditionalCountries);
foreach (var lookup in countries)
{
context.Countries.Attach(lookup);
}
}
// check for user.
var person = context.People.FirstOrDefault(p => p.Identifier == user.Identifier);
if (person != null)
{
user = person;
}
this.UnitOfWork.Commit(user);
}
private static List<T> UpdateRelationship<T>(
ICollection<string> codes,
ICollection<T> lookups) where T : Lookup, new()
{
var stored = lookups.Select(x => x.Code).ToList();
// remove relationships that are in the store but not the update.
foreach (var code in stored.Where(code => !codes.Contains(code)))
{
lookups.Remove(lookups.First(x => x.Code.Equals(code)));
}
var additions = new List<T>();
// add those relationships that are in the update but not the store.
foreach (
var lookup in
from code in codes
where lookups.All(x => x.Code != code)
select new T { Code = code })
{
additions.Add(lookup);
lookups.Add(lookup);
}
return additions;
}
}
}
namespace Demo.Repository.Models
{
using System;
using System.ComponentModel.DataAnnotations;
using FrameLog;
public class Lookup : IHasLoggingReference, ICloneable
{
[Key]
public string Code { get; set; }
public string Text { get; set; }
// https://bitbucket.org/MartinEden/frame-log/wiki/Home
public object Reference
{
get
{
return this.Code;
}
}
/// <summary>
/// Shallow clone.
/// </summary>
/// <returns>Returns a <c>MemberwiseClone</c>.</returns>
public object Clone()
{
return this.MemberwiseClone();
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var y = obj as Lookup;
Debug.Assert(y != null, "y != null");
if (y.Text != null)
{
return this.Code.Equals(y.Code) && this.Text.Equals(y.Text);
}
// it's okay to provide this check for adding codes to relationships.
return this.Code.Equals(y.Code);
}
public override int GetHashCode()
{
return unchecked((this.Code ?? string.Empty).GetHashCode());
}
}
}
namespace Demo.Repository
{
public class LookupRepository<T> : GenericRepository<T> where T : Lookup
{
public LookupRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public IEnumerable<T> GetAllLookups()
{
return this.DataSet.ToList();
}
public int UpdateLookups(IEnumerable<T> lookups)
{
var context = this.UnitOfWork.ApplicationContext;
var lookupList = lookups as IList<T> ?? lookups.ToList();
var storedLookups = this.DataSet;
var codes = lookupList.Select(c => c.Code).ToList();
foreach (var storedLookup in storedLookups)
{
// records that were removed.
if (!codes.Contains(storedLookup.Code))
{
storedLookups.Remove(storedLookup);
}
}
foreach (var lookup in lookupList)
{
// records that were added.
if (storedLookups.All(x => !x.Code.Equals(lookup.Code)))
{
storedLookups.Add(lookup);
}
else
{
var storedlookup = storedLookups.First(x => x.Code.Equals(lookup.Code));
// records that were updated.
if (!storedlookup.Equals(lookup))
{
context.Entry(storedlookup).CurrentValues.SetValues(lookup);
}
}
}
return this.UnitOfWork.Commit();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment