实体框架 - 存储库检查(大文本)

发布于 2024-11-29 22:26:52 字数 20227 浏览 0 评论 0原文

我正在使用 Entity 框架在 C#/ASP.NET 中创建一个完整的存储库,但目前我担心我忽略了诸如处置 ObjectContext 之类的事情。在下面的代码行中,您将看到我的完整存储库(至少是你们理解我的问题所需的内容),我希望有人好心地查看它并告诉我我是否犯了一些错误。

这个项目对我来说非常非常重要,但我对存储库/EF 模型很陌生。

Global.asax

public class Global : System.Web.HttpApplication
{
    private WebObjectContextStorage _storage;

    public override void Init()
    {
        base.Init();
        _storage = new WebObjectContextStorage(this);
    }

    protected void Application_Start(object sender, EventArgs e)
    {

    }

    protected void Session_Start(object sender, EventArgs e)
    {

    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        ObjectContextInitializer.Instance().InitializeObjectContextOnce(() =>
        {
            ObjectContextManager.InitStorage(_storage);
        });
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {

    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {

    }

    protected void Application_Error(object sender, EventArgs e)
    {

    }

    protected void Session_End(object sender, EventArgs e)
    {

    }

    protected void Application_End(object sender, EventArgs e)
    {

    }
}

ObjectContextManager

public static class ObjectContextManager
{
    public static void InitStorage(IObjectContextStorage storage)
    {
        if (storage == null) 
        {
            throw new ArgumentNullException("storage");
        }
        if ((Storage != null) && (Storage != storage))
        {
            throw new ApplicationException("A storage mechanism has already been configured for this application");
        }            
        Storage = storage;
    }

    /// <summary>
    /// The default connection string name used if only one database is being communicated with.
    /// </summary>
    public static readonly string DefaultConnectionStringName = "TraceConnection";        

    /// <summary>
    /// Used to get the current object context session if you're communicating with a single database.
    /// When communicating with multiple databases, invoke <see cref="CurrentFor()" /> instead.
    /// </summary>
    public static ObjectContext Current
    {
        get
        {
            return CurrentFor(DefaultConnectionStringName);
        }
    }

    /// <summary>
    /// Used to get the current ObjectContext associated with a key; i.e., the key 
    /// associated with an object context for a specific database.
    /// 
    /// If you're only communicating with one database, you should call <see cref="Current" /> instead,
    /// although you're certainly welcome to call this if you have the key available.
    /// </summary>
    public static ObjectContext CurrentFor(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentNullException("key");
        }

        if (Storage == null)
        {
            throw new ApplicationException("An IObjectContextStorage has not been initialized");
        }

        ObjectContext context = null;
        lock (_syncLock)
        {
            context = Storage.GetObjectContextForKey(key);

            if (context == null)
            {
                context = ObjectContextFactory.GetTraceContext(key);
                Storage.SetObjectContextForKey(key, context);
            }
        }

        return context;
    }

    /// <summary>
    /// This method is used by application-specific object context storage implementations
    /// and unit tests. Its job is to walk thru existing cached object context(s) and Close() each one.
    /// </summary>
    public static void CloseAllObjectContexts()
    {
        foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
        {
            if (ctx.Connection.State == System.Data.ConnectionState.Open)
                ctx.Connection.Close();
        }
    }      

    /// <summary>
    /// An application-specific implementation of IObjectContextStorage must be setup either thru
    /// <see cref="InitStorage" /> or one of the <see cref="Init" /> overloads. 
    /// </summary>
    private static IObjectContextStorage Storage { get; set; }

    private static object _syncLock = new object();
}

ObjectContextInitializer

public class ObjectContextInitializer
{
    private static readonly object syncLock = new object();
    private static ObjectContextInitializer instance;

    protected ObjectContextInitializer() { }

    private bool isInitialized = false;

    public static ObjectContextInitializer Instance()
    {
        if (instance == null)
        {
            lock (syncLock)
            {
                if (instance == null)
                {
                    instance = new ObjectContextInitializer();
                }
            }
        }

        return instance;
    }

    /// <summary>
    /// This is the method which should be given the call to intialize the ObjectContext; e.g.,
    /// ObjectContextInitializer.Instance().InitializeObjectContextOnce(() => InitializeObjectContext());
    /// where InitializeObjectContext() is a method which calls ObjectContextManager.Init()
    /// </summary>
    /// <param name="initMethod"></param>
    public void InitializeObjectContextOnce(Action initMethod)
    {
        lock (syncLock)
        {
            if (!isInitialized)
            {
                initMethod();
                isInitialized = true;
            }
        }
    }

}

ObjectContextFactory

public static class ObjectContextFactory
{
    /// <summary>
    /// Gets the TraceContext
    /// </summary>
    /// <param name="connectionString">Connection string to use for database queries</param>
    /// <returns>The TraceContext</returns>
    public static TraceContext GetTraceContext(string configName)
    {
        string connectionString = ConfigurationManager.ConnectionStrings[configName].ConnectionString;
        return new TraceContext(connectionString);
    }
}

WebObjectContextStorage

public class WebObjectContextStorage : IObjectContextStorage
{   
    public WebObjectContextStorage(HttpApplication app)
    { 
        app.EndRequest += (sender, args) =>
                              {
                                  ObjectContextManager.CloseAllObjectContexts();
                                  HttpContext.Current.Items.Remove(HttpContextObjectContextStorageKey);
                              };
    }        

    public ObjectContext GetObjectContextForKey(string key)
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        return storage.GetObjectContextForKey(key);
    }

    public void SetObjectContextForKey(string factoryKey, ObjectContext session)
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        storage.SetObjectContextForKey(factoryKey, session);
    }

    public IEnumerable<ObjectContext> GetAllObjectContexts()
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        return storage.GetAllObjectContexts();
    }

    private ObjectContextStorage GetObjectContextStorage()
    {
        HttpContext context = HttpContext.Current;
        ObjectContextStorage storage = context.Items[HttpContextObjectContextStorageKey] as ObjectContextStorage;
        if (storage == null)
        {
            storage = new ObjectContextStorage();
            context.Items[HttpContextObjectContextStorageKey] = storage;
        }
        return storage;
    }       

    private static readonly string HttpContextObjectContextStorageKey = "HttpContextObjectContextStorageKey";       
}

ObjectContextStorage

public class ObjectContextStorage : IObjectContextStorage
{
    private Dictionary<string, ObjectContext> storage = new Dictionary<string, ObjectContext>();

    /// <summary>
    /// Initializes a new instance of the <see cref="SimpleObjectContextStorage"/> class.
    /// </summary>
    public ObjectContextStorage() { }

    /// <summary>
    /// Returns the object context associated with the specified key or
    /// null if the specified key is not found.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public ObjectContext GetObjectContextForKey(string key)
    {
        ObjectContext context;
        if (!this.storage.TryGetValue(key, out context))
            return null;
        return context;
    }


    /// <summary>
    /// Stores the object context into a dictionary using the specified key.
    /// If an object context already exists by the specified key, 
    /// it gets overwritten by the new object context passed in.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="objectContext">The object context.</param>
    public void SetObjectContextForKey(string key, ObjectContext objectContext)
    {           
        this.storage.Add(key, objectContext);           
    }

    /// <summary>
    /// Returns all the values of the internal dictionary of object contexts.
    /// </summary>
    /// <returns></returns>
    public IEnumerable<ObjectContext> GetAllObjectContexts()
    {
        return this.storage.Values;
    }
}

GenericRepository

public class GenericRepository : IRepository
{
    private readonly string _connectionStringName;
    private ObjectContext _objectContext;
    private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
    private bool _usePlurazation;

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository&lt;TEntity&gt;"/> class.
    /// </summary>
    public GenericRepository()
        : this(string.Empty, false)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository&lt;TEntity&gt;"/> class.
    /// </summary>
    /// <param name="connectionStringName">Name of the connection string.</param>
    public GenericRepository(string connectionStringName, bool usePlurazation)
    {
        this._connectionStringName = connectionStringName;
        this._usePlurazation = usePlurazation;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository"/> class.
    /// </summary>
    /// <param name="objectContext">The object context.</param>
    public GenericRepository(ObjectContext objectContext, bool usePlurazation)
    {
        if (objectContext == null)
            throw new ArgumentNullException("objectContext");
        this._objectContext = objectContext;
        this._usePlurazation = usePlurazation;
    }

    public TEntity GetByKey<TEntity>(object keyValue) where TEntity : class
    {
        EntityKey key = GetEntityKey<TEntity>(keyValue);

        object originalItem;
        if (ObjectContext.TryGetObjectByKey(key, out originalItem))
        {
            return (TEntity)originalItem;
        }
        return default(TEntity);
    }

    public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
    {
        var entityName = GetEntityName<TEntity>();
        return ObjectContext.CreateQuery<TEntity>(entityName).OfType<TEntity>();
    }

    public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return GetQuery<TEntity>().Where(predicate);
    }

    public IQueryable<TEntity> GetQuery<TEntity>(ISpecification<TEntity> specification) where TEntity : class
    {
        return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>());
    }

    public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return GetQuery<TEntity>().OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return GetQuery<TEntity>().OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return GetQuery<TEntity>().Where(predicate).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return GetQuery<TEntity>().Where(predicate).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public IEnumerable<TEntity> Get<TEntity>(ISpecification<TEntity> specification, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().SingleOrDefault<TEntity>(criteria);
    }

    public TEntity Single<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
    }

    public TEntity First<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return GetQuery<TEntity>().FirstOrDefault(predicate);
    }

    public TEntity First<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).FirstOrDefault();
    }

    public void Add<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }
        ObjectContext.AddObject(GetEntityName<TEntity>(), entity);
    }

    public void Attach<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        ObjectContext.AttachTo(GetEntityName<TEntity>(), entity);
    }

    public void Delete<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }
        ObjectContext.DeleteObject(entity);
    }

    public void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        IEnumerable<TEntity> records = Find<TEntity>(criteria);

        foreach (TEntity record in records)
        {
            Delete<TEntity>(record);
        }
    }

    public void Delete<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        IEnumerable<TEntity> records = Find<TEntity>(criteria);
        foreach (TEntity record in records)
        {
            Delete<TEntity>(record);
        }
    }

    public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return GetQuery<TEntity>().AsEnumerable();
    }

    public void Update<TEntity>(TEntity entity) where TEntity : class
    {
        var fqen = GetEntityName<TEntity>();

        object originalItem;
        EntityKey key = ObjectContext.CreateEntityKey(fqen, entity);
        if (ObjectContext.TryGetObjectByKey(key, out originalItem))
        {
            ObjectContext.ApplyCurrentValues(key.EntitySetName, entity);
        }
    }

    public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Where(criteria);
    }

    public TEntity FindOne<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Where(criteria).FirstOrDefault();
    }

    public TEntity FindOne<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
    }

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>());
    }

    public int Count<TEntity>() where TEntity : class
    {
        return GetQuery<TEntity>().Count();
    }

    public int Count<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Count(criteria);
    }

    public int Count<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).Count();
    }

    public IUnitOfWork UnitOfWork
    {
        get
        {
            if (unitOfWork == null)
            {
                unitOfWork = new UnitOfWork(this.ObjectContext);
            }
            return unitOfWork;
        }
    }

    private ObjectContext ObjectContext
    {
        get
        {
            if (this._objectContext == null)
            {
                if (string.IsNullOrEmpty(this._connectionStringName))
                {
                    this._objectContext = ObjectContextManager.Current;
                }
                else
                {
                    this._objectContext = ObjectContextManager.CurrentFor(this._connectionStringName);
                }
            }
            return this._objectContext;
        }
    }

    private EntityKey GetEntityKey<TEntity>(object keyValue) where TEntity : class
    {
        var entitySetName = GetEntityName<TEntity>();
        var objectSet = ObjectContext.CreateObjectSet<TEntity>();
        var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
        var entityKey = new EntityKey(entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
        return entityKey;
    }

    private string GetEntityName<TEntity>() where TEntity : class
    {
        // WARNING! : Exceptions for inheritance


        if (_usePlurazation)
        {
             return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));

        }
        else
        {
             return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, typeof(TEntity).Name);

        }
    }

    private IUnitOfWork unitOfWork;
}

我知道阅读代码需要一些时间,但如果有人查看它并给出关于如何做得更好或我不应该处理的提示,这会对我有所帮助 目的。

另外我还有一个小问题:“我想在这个存储库之上放置一个业务层,这将使像 global.asax 这样的东西保持相同,我猜,但需要静态类(对吗?),比如 BookProvider ,它为我提供了所有数据关于我的书实体

I'm making a full repository in C#/ASP.NET with the Entity framework but at the moment I'm afraid I'm overlooking something like disposing my ObjectContexts. In the following lines of codes you'll see my full repository (atleast what is needed for you guys to understand my problem) and I hope someone is kind enough to look through it and tell me if I made some mistakes.

This project is very very important for me but I'm new to the repository/EF models.

Global.asax

public class Global : System.Web.HttpApplication
{
    private WebObjectContextStorage _storage;

    public override void Init()
    {
        base.Init();
        _storage = new WebObjectContextStorage(this);
    }

    protected void Application_Start(object sender, EventArgs e)
    {

    }

    protected void Session_Start(object sender, EventArgs e)
    {

    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        ObjectContextInitializer.Instance().InitializeObjectContextOnce(() =>
        {
            ObjectContextManager.InitStorage(_storage);
        });
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {

    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {

    }

    protected void Application_Error(object sender, EventArgs e)
    {

    }

    protected void Session_End(object sender, EventArgs e)
    {

    }

    protected void Application_End(object sender, EventArgs e)
    {

    }
}

ObjectContextManager

public static class ObjectContextManager
{
    public static void InitStorage(IObjectContextStorage storage)
    {
        if (storage == null) 
        {
            throw new ArgumentNullException("storage");
        }
        if ((Storage != null) && (Storage != storage))
        {
            throw new ApplicationException("A storage mechanism has already been configured for this application");
        }            
        Storage = storage;
    }

    /// <summary>
    /// The default connection string name used if only one database is being communicated with.
    /// </summary>
    public static readonly string DefaultConnectionStringName = "TraceConnection";        

    /// <summary>
    /// Used to get the current object context session if you're communicating with a single database.
    /// When communicating with multiple databases, invoke <see cref="CurrentFor()" /> instead.
    /// </summary>
    public static ObjectContext Current
    {
        get
        {
            return CurrentFor(DefaultConnectionStringName);
        }
    }

    /// <summary>
    /// Used to get the current ObjectContext associated with a key; i.e., the key 
    /// associated with an object context for a specific database.
    /// 
    /// If you're only communicating with one database, you should call <see cref="Current" /> instead,
    /// although you're certainly welcome to call this if you have the key available.
    /// </summary>
    public static ObjectContext CurrentFor(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentNullException("key");
        }

        if (Storage == null)
        {
            throw new ApplicationException("An IObjectContextStorage has not been initialized");
        }

        ObjectContext context = null;
        lock (_syncLock)
        {
            context = Storage.GetObjectContextForKey(key);

            if (context == null)
            {
                context = ObjectContextFactory.GetTraceContext(key);
                Storage.SetObjectContextForKey(key, context);
            }
        }

        return context;
    }

    /// <summary>
    /// This method is used by application-specific object context storage implementations
    /// and unit tests. Its job is to walk thru existing cached object context(s) and Close() each one.
    /// </summary>
    public static void CloseAllObjectContexts()
    {
        foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
        {
            if (ctx.Connection.State == System.Data.ConnectionState.Open)
                ctx.Connection.Close();
        }
    }      

    /// <summary>
    /// An application-specific implementation of IObjectContextStorage must be setup either thru
    /// <see cref="InitStorage" /> or one of the <see cref="Init" /> overloads. 
    /// </summary>
    private static IObjectContextStorage Storage { get; set; }

    private static object _syncLock = new object();
}

ObjectContextInitializer

public class ObjectContextInitializer
{
    private static readonly object syncLock = new object();
    private static ObjectContextInitializer instance;

    protected ObjectContextInitializer() { }

    private bool isInitialized = false;

    public static ObjectContextInitializer Instance()
    {
        if (instance == null)
        {
            lock (syncLock)
            {
                if (instance == null)
                {
                    instance = new ObjectContextInitializer();
                }
            }
        }

        return instance;
    }

    /// <summary>
    /// This is the method which should be given the call to intialize the ObjectContext; e.g.,
    /// ObjectContextInitializer.Instance().InitializeObjectContextOnce(() => InitializeObjectContext());
    /// where InitializeObjectContext() is a method which calls ObjectContextManager.Init()
    /// </summary>
    /// <param name="initMethod"></param>
    public void InitializeObjectContextOnce(Action initMethod)
    {
        lock (syncLock)
        {
            if (!isInitialized)
            {
                initMethod();
                isInitialized = true;
            }
        }
    }

}

ObjectContextFactory

public static class ObjectContextFactory
{
    /// <summary>
    /// Gets the TraceContext
    /// </summary>
    /// <param name="connectionString">Connection string to use for database queries</param>
    /// <returns>The TraceContext</returns>
    public static TraceContext GetTraceContext(string configName)
    {
        string connectionString = ConfigurationManager.ConnectionStrings[configName].ConnectionString;
        return new TraceContext(connectionString);
    }
}

WebObjectContextStorage

public class WebObjectContextStorage : IObjectContextStorage
{   
    public WebObjectContextStorage(HttpApplication app)
    { 
        app.EndRequest += (sender, args) =>
                              {
                                  ObjectContextManager.CloseAllObjectContexts();
                                  HttpContext.Current.Items.Remove(HttpContextObjectContextStorageKey);
                              };
    }        

    public ObjectContext GetObjectContextForKey(string key)
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        return storage.GetObjectContextForKey(key);
    }

    public void SetObjectContextForKey(string factoryKey, ObjectContext session)
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        storage.SetObjectContextForKey(factoryKey, session);
    }

    public IEnumerable<ObjectContext> GetAllObjectContexts()
    {
        ObjectContextStorage storage = GetObjectContextStorage();
        return storage.GetAllObjectContexts();
    }

    private ObjectContextStorage GetObjectContextStorage()
    {
        HttpContext context = HttpContext.Current;
        ObjectContextStorage storage = context.Items[HttpContextObjectContextStorageKey] as ObjectContextStorage;
        if (storage == null)
        {
            storage = new ObjectContextStorage();
            context.Items[HttpContextObjectContextStorageKey] = storage;
        }
        return storage;
    }       

    private static readonly string HttpContextObjectContextStorageKey = "HttpContextObjectContextStorageKey";       
}

ObjectContextStorage

public class ObjectContextStorage : IObjectContextStorage
{
    private Dictionary<string, ObjectContext> storage = new Dictionary<string, ObjectContext>();

    /// <summary>
    /// Initializes a new instance of the <see cref="SimpleObjectContextStorage"/> class.
    /// </summary>
    public ObjectContextStorage() { }

    /// <summary>
    /// Returns the object context associated with the specified key or
    /// null if the specified key is not found.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public ObjectContext GetObjectContextForKey(string key)
    {
        ObjectContext context;
        if (!this.storage.TryGetValue(key, out context))
            return null;
        return context;
    }


    /// <summary>
    /// Stores the object context into a dictionary using the specified key.
    /// If an object context already exists by the specified key, 
    /// it gets overwritten by the new object context passed in.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="objectContext">The object context.</param>
    public void SetObjectContextForKey(string key, ObjectContext objectContext)
    {           
        this.storage.Add(key, objectContext);           
    }

    /// <summary>
    /// Returns all the values of the internal dictionary of object contexts.
    /// </summary>
    /// <returns></returns>
    public IEnumerable<ObjectContext> GetAllObjectContexts()
    {
        return this.storage.Values;
    }
}

GenericRepository

public class GenericRepository : IRepository
{
    private readonly string _connectionStringName;
    private ObjectContext _objectContext;
    private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
    private bool _usePlurazation;

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository<TEntity>"/> class.
    /// </summary>
    public GenericRepository()
        : this(string.Empty, false)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository<TEntity>"/> class.
    /// </summary>
    /// <param name="connectionStringName">Name of the connection string.</param>
    public GenericRepository(string connectionStringName, bool usePlurazation)
    {
        this._connectionStringName = connectionStringName;
        this._usePlurazation = usePlurazation;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository"/> class.
    /// </summary>
    /// <param name="objectContext">The object context.</param>
    public GenericRepository(ObjectContext objectContext, bool usePlurazation)
    {
        if (objectContext == null)
            throw new ArgumentNullException("objectContext");
        this._objectContext = objectContext;
        this._usePlurazation = usePlurazation;
    }

    public TEntity GetByKey<TEntity>(object keyValue) where TEntity : class
    {
        EntityKey key = GetEntityKey<TEntity>(keyValue);

        object originalItem;
        if (ObjectContext.TryGetObjectByKey(key, out originalItem))
        {
            return (TEntity)originalItem;
        }
        return default(TEntity);
    }

    public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
    {
        var entityName = GetEntityName<TEntity>();
        return ObjectContext.CreateQuery<TEntity>(entityName).OfType<TEntity>();
    }

    public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return GetQuery<TEntity>().Where(predicate);
    }

    public IQueryable<TEntity> GetQuery<TEntity>(ISpecification<TEntity> specification) where TEntity : class
    {
        return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>());
    }

    public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return GetQuery<TEntity>().OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return GetQuery<TEntity>().OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return GetQuery<TEntity>().Where(predicate).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return GetQuery<TEntity>().Where(predicate).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public IEnumerable<TEntity> Get<TEntity>(ISpecification<TEntity> specification, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
    {
        if (sortOrder == SortOrder.Ascending)
        {
            return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
        }
        return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
    }

    public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().SingleOrDefault<TEntity>(criteria);
    }

    public TEntity Single<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
    }

    public TEntity First<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
    {
        return GetQuery<TEntity>().FirstOrDefault(predicate);
    }

    public TEntity First<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).FirstOrDefault();
    }

    public void Add<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }
        ObjectContext.AddObject(GetEntityName<TEntity>(), entity);
    }

    public void Attach<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        ObjectContext.AttachTo(GetEntityName<TEntity>(), entity);
    }

    public void Delete<TEntity>(TEntity entity) where TEntity : class
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }
        ObjectContext.DeleteObject(entity);
    }

    public void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        IEnumerable<TEntity> records = Find<TEntity>(criteria);

        foreach (TEntity record in records)
        {
            Delete<TEntity>(record);
        }
    }

    public void Delete<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        IEnumerable<TEntity> records = Find<TEntity>(criteria);
        foreach (TEntity record in records)
        {
            Delete<TEntity>(record);
        }
    }

    public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return GetQuery<TEntity>().AsEnumerable();
    }

    public void Update<TEntity>(TEntity entity) where TEntity : class
    {
        var fqen = GetEntityName<TEntity>();

        object originalItem;
        EntityKey key = ObjectContext.CreateEntityKey(fqen, entity);
        if (ObjectContext.TryGetObjectByKey(key, out originalItem))
        {
            ObjectContext.ApplyCurrentValues(key.EntitySetName, entity);
        }
    }

    public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Where(criteria);
    }

    public TEntity FindOne<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Where(criteria).FirstOrDefault();
    }

    public TEntity FindOne<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
    }

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>());
    }

    public int Count<TEntity>() where TEntity : class
    {
        return GetQuery<TEntity>().Count();
    }

    public int Count<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
    {
        return GetQuery<TEntity>().Count(criteria);
    }

    public int Count<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
    {
        return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).Count();
    }

    public IUnitOfWork UnitOfWork
    {
        get
        {
            if (unitOfWork == null)
            {
                unitOfWork = new UnitOfWork(this.ObjectContext);
            }
            return unitOfWork;
        }
    }

    private ObjectContext ObjectContext
    {
        get
        {
            if (this._objectContext == null)
            {
                if (string.IsNullOrEmpty(this._connectionStringName))
                {
                    this._objectContext = ObjectContextManager.Current;
                }
                else
                {
                    this._objectContext = ObjectContextManager.CurrentFor(this._connectionStringName);
                }
            }
            return this._objectContext;
        }
    }

    private EntityKey GetEntityKey<TEntity>(object keyValue) where TEntity : class
    {
        var entitySetName = GetEntityName<TEntity>();
        var objectSet = ObjectContext.CreateObjectSet<TEntity>();
        var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
        var entityKey = new EntityKey(entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
        return entityKey;
    }

    private string GetEntityName<TEntity>() where TEntity : class
    {
        // WARNING! : Exceptions for inheritance


        if (_usePlurazation)
        {
             return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));

        }
        else
        {
             return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, typeof(TEntity).Name);

        }
    }

    private IUnitOfWork unitOfWork;
}

I know it will take some time to read through the code, but it would help me miles is somebody looks at it and gives tips on what to do better or where I do not dispose an object.

Also I got a little question: "I would like to put a business layer above this repository, that would keep things like global.asax the same I guess but would need static classes(right?) like a BookProvider which gives me all the data about my book-entities?

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

同尘 2024-12-06 22:26:52

我能给出的唯一具体说明是关于处理上下文:

foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
{
    if (ctx.Connection.State == System.Data.ConnectionState.Open)
        ctx.Connection.Close();
}

ObjectContext 实现了IDisposable,因此我认为标准方法是:

foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
    ctx.Dispose();

据我所知ObjectContext .Dispose() 只是关闭连接,因此它的作用与您所做的相同。但我认为这是一个内部实现细节,可能会在 EF 版本之间发生变化。

您的通用存储库就是一个,因为有很多此类存储库。在查看这些方法时,我想到了几点:

  • 由于您在 public IQueryable中公开 IQueryable, GetQuery(...) 为什么您需要大多数其他方法,例如 SingleFirstCount、 ETC。? (为什么不是 Any 等?)您可以从 IQueryable 中获得所有这些。

  • 您的Update方法仅适用于标量属性。但这是通用存储库的常见问题。没有简单的解决方案或根本没有以通用方式更新实体的解决方案。

  • 您希望通过使用存储库模式达到什么目标?如果您考虑内存中数据存储的单元可测试性,则无法公开 IQueryable,因为 LINQ to Entities 和 LINQ to Objects 不同。要测试您的 IQueryables 是否工作,您需要集成测试以及您的应用程序在生产中应该使用的真实数据库。但是,如果您不公开 IQueryable,您的存储库需要许多特定于业务的方法,这些方法将结果作为 POCO、POCO 集合或投影/选定属性的 DTO 返回,并隐藏内部查询规范,以便您可以使用内存中的数据模拟这些方法来测试您的业务逻辑。但这就是通用存储库不再足够的地方。 (例如,您将如何编写一个 LINQ Join,其中存储库中涉及多个实体/对象集,而该存储库只有一种实体类型作为通用参数?)

如果您问 10 个人如何编写他们的存储库是结构化的,你会得到十个不同的答案。没有什么是真正错误的或最好的,因为这取决于您要使用此存储库构建的应用程序。我相信没有人可以告诉您您的存储库的真正价值。当您开始编写应用程序时,您将在实践中看到它。对于某些应用程序来说,它可能是过度架构的(我认为这是最危险的,因为管理和控制无意义的架构是昂贵的,而且浪费了编写实际应用程序内容的时间)。对于其他需求,您可能必须扩展存储库。例如:

  • 如何处理实体导航属性的显式加载或查询(使用 EF 4.1 中的 CreateSourceQueryDbEntityEntry.Collection/Reference)?如果您的应用程序从不需要显式加载:很好。如果有必要,您需要扩展您的存储库。

  • 如何控制急切加载?有时您可能只需要一个父实体。有时您想要包含children1 集合,有时想要包含child2 引用。

  • 如何手动设置实体的实体状态?也许你永远不需要。但在下一个应用程序中它可能会非常有帮助。

  • 如何手动从上下文中分离实体?

  • 如何控制加载行为(通过上下文跟踪或不跟踪实体)?

  • 如何手动控制延迟加载行为和更改跟踪代理的创建?

  • 如何手动创建实体代理?当您使用延迟加载或更改跟踪代理时,在某些情况下您可能需要它。

  • 如何在不构建结果集合的情况下将实体加载到上下文中?另一种存储库方法,也许......或者也许不是。谁会提前知道您的应用程序逻辑需要什么。

等等等等......

The only concrete remark I can give is about disposing the context:

foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
{
    if (ctx.Connection.State == System.Data.ConnectionState.Open)
        ctx.Connection.Close();
}

ObjectContext implements IDisposable, so the standard way would be in my opinion:

foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
    ctx.Dispose();

As far as I know ObjectContext.Dispose() just closes the connection, so it does the same what you are doing. But I'd consider this as an internal implementation detail which might potentially change between EF releases.

Your generic repository is one as there are many of this kind. A few points which came to my mind when looking at the methods:

  • Since you are exposing IQueryable in public IQueryable<TEntity> GetQuery<TEntity>(...) why do you need most of the other methods like Single, First, Count, etc.? (Why not Any, etc.?) You get all this from your IQueryable.

  • Your Update method only works for scalar properties. But this is a common problem with generic repositories. There is no easy solution or no solution at all for updating entities in a generic way.

  • What is your goal you want to reach by using the repository pattern at all? If you have unit testability with an in-memory data store in mind, you cannot expose IQueryable because LINQ to Entities and LINQ to Objects are not the same. To test if your IQueryables work you need integration tests and the real database your application is supposed to work with in production. But if you don't expose IQueryable your repository needs a lot of business specific methods which return the results as POCOs, POCO collections or DTOs of projected/selected properties and hide the internal query specifications so that you can mock these methods with in-memory data to test your business logic. But this is the point where a generic repository is not sufficient anymore. (How are you going to write, for instance, a LINQ Join where more than one Entity/ObjectSet is involved in a repository which has only one entity type as generic parameter?)

If you ask ten people how their repositories are architectured, you'll get ten different answers. And none is really wrong or the best because it depends on the application you are going to build with this repository. I believe that nobody can tell you what your repository is really worth. You will see it in practice when you start writing an application. For some applications it might be over-architectured (which I consider as most dangerous because managing and keeping a meaningless architecture under control is expensive and a waste of time you lose for writing real application content). And for other needs you will likely have to extend the repository. For example:

  • How do you handle explicit loading or queries on navigation properties of an entity (with CreateSourceQuery or DbEntityEntry.Collection/Reference in EF 4.1)? If your application never needs explicite loading: Fine. If it's necessary you need to extend your repo.

  • How do you control eager loading? Sometimes perhaps you just want a parent entity. Sometimes you want to Includ the children1 collection and sometimes the child2 reference.

  • How do you set the entity state of an entity manually? Perhaps you never must. But in the next application it might be very helpful.

  • How do you detach an entity from the context manually?

  • How do you control loading behaviour (with or without tracking of entities by the context)?

  • How do you control manually lazy loading behaviour and creation of change tracking proxies?

  • How do you create an entity proxy manually? You might need it in some situations when you work with lazy loading or change tracking proxies.

  • How do you load entities into the context without building a result collection? Yet another repository method, perhaps... or perhaps not. Who knows in advance what your application logic will require.

And so on and so forth...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文